From b04ac3c14bc2da8dae7be0570002f37ec82fd358 Mon Sep 17 00:00:00 2001 From: Boris De Vloed Date: Wed, 23 Sep 2026 09:04:21 +0200 Subject: [PATCH 1/5] =?UTF-8?q?fix(grapes):=20Trousseau=20gris=20=E2=80=94?= =?UTF-8?q?=20drop=20the=20fr=20Wikipedia=20pin=20that=20pointed=20at=20Tr?= =?UTF-8?q?ousseau=20noir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fr override pinned `trousseau-gris` to the "Trousseau" article, so the French Pineau des Charentes card showed the noir variety's summary on the gris pill (visitor flag, 2026-09-21). `null` marks the fr article as absent; the fr/nl tooltips now carry the en "Trousseau gris" extract, translated. Co-Authored-By: Claude Fable 5.1 --- raw/wikipedia/grape_overrides.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raw/wikipedia/grape_overrides.json b/raw/wikipedia/grape_overrides.json index 95453f0..3544b4e 100644 --- a/raw/wikipedia/grape_overrides.json +++ b/raw/wikipedia/grape_overrides.json @@ -396,7 +396,7 @@ "tintilla-de-rota": "Tintilla de Rota", "tourbat": "Torbato", "triomphe-dalsace": "Triomphe d'Alsace", - "trousseau-gris": "Trousseau", + "trousseau-gris": null, "ugni-blanc": "Trebbiano", "valdiguie": "Valdiguié", "verdejo-negro": "Trousseau", From 2b3f06b12d4e23fa6ae306e4eb3726f975a453db Mon Sep 17 00:00:00 2001 From: Boris De Vloed Date: Wed, 23 Sep 2026 09:04:21 +0200 Subject: [PATCH 2/5] =?UTF-8?q?feat(map):=20zoom-dependent=20level=20of=20?= =?UTF-8?q?detail=20=E2=80=94=20vineyard=20footprints=20below=20z12,=20par?= =?UTF-8?q?cels=20above?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zoom, not the Simple/Advanced toggle, now decides which geometry the map draws. The 1,259 French records with an INAO parcellaire get a generalised vineyard footprint below the detail zoom — a 250 m morphological closing of their parcels (scripts/_lib/vineyard_envelope.py: connected-component decomposition before the closing, Languedoc 31 s instead of 832 s; a content-addressed cache under raw/cache/) — and the parcels themselves from z11, with a crossfade to z11.9. Median footprint area is 1.3× the parcels against 5–8× for the commune union the default view drew before, which painted the Gironde estuary for Saint-Estèphe (visitor flag, 2026-09-21). Every other record keeps one polygon at every zoom. Stage 04 writes a third GeoJSON/tileset, appellations-overview (z3–z11); the detail tileset is z11–z12 (88 + 42 MB, was 114 + 95). The commune-level appellations-villages.geojson is still written for audit_geometry_overlaps but no longer tiled. Gates: every slug in both bands (two records had no low-zoom feature before), the footprint bbox inside the parcels' bbox + r, a ratio warning. `area` stays the parcellaire area on both features so fill-sort-key and the paint ramps never step at the crossfade; `parts` and `frag` (perimeter/area) drive the outline: constant ink per fill from z10 (a 112-parcel record draws a 0.4–0.8 px edge, a single-part one keeps the area ramp), an opacity ramp on `parts` below the crossfade, and footprints draw with no outline below z6.5 and a lighter fill at the overview. The panel and the SSR card now say what every polygon is: the zoom-reactive footprint line, a "union of the n communes, water included" line for plain commune unions (every French IGP and Champagne said nothing before), and a zone-source line for the other countries' regulators (geoportal / MAPA / Bétard 2022 / administrative union). Feedback Flagged/Retracted/Note and Appellation Viewed carry `lod` and `zoom`. The toggle no longer swaps layers; it still gates spirits and the expert facets (to be simplified). New audit: scripts/audit_vineyard_envelopes.py (parity, containment, inflation, bridging onto neighbours, --water via BD TOPO incl. rivers). Also carries the 2026-09-22 feedback-triage leftovers that touch the same files: `view_mode` on the feedback events, the PLAUSIBLE_API_KEY-in-.env notes in CLAUDE.md / docs/analytics.md, and the CURATOR_TODO triage section. Co-Authored-By: Claude Fable 5.1 --- CLAUDE.md | 111 +++++++- CURATOR_TODO.md | 26 ++ docs/analytics.md | 12 +- locale/en/LC_MESSAGES/messages.po | 371 +++++++++++++++---------- locale/es/LC_MESSAGES/messages.po | 373 +++++++++++++++---------- locale/fr/LC_MESSAGES/messages.po | 346 +++++++++++++---------- locale/messages.pot | 346 +++++++++++++---------- locale/nl/LC_MESSAGES/messages.po | 372 +++++++++++++++---------- scripts/04_build_maps.py | 233 ++++++++++++++-- scripts/_lib/assets/app.js | 168 ++++++++--- scripts/_lib/content_block.py | 75 ++++- scripts/_lib/map_template.py | 415 +++++++++++++++++++--------- scripts/_lib/vineyard_envelope.py | 241 ++++++++++++++++ scripts/audit_vineyard_envelopes.py | 319 +++++++++++++++++++++ tests/test_content_block.py | 79 +++++- tests/test_vineyard_envelope.py | 147 ++++++++++ 16 files changed, 2719 insertions(+), 915 deletions(-) create mode 100644 scripts/_lib/vineyard_envelope.py create mode 100644 scripts/audit_vineyard_envelopes.py create mode 100644 tests/test_vineyard_envelope.py diff --git a/CLAUDE.md b/CLAUDE.md index 8b9f629..aeebc82 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -724,7 +724,8 @@ disjoint commune lists should *tile*, not overlap with real 2-D area. [scripts/audit_geometry_overlaps.py](scripts/audit_geometry_overlaps.py) streams `wiki/map-data/appellations-villages.geojson` (commune-level — -the right granularity for a one-commune sliver), reprojects to +the right granularity for a one-commune sliver; since the zoom-LOD split +this file is written for the audit only and is no longer tiled), reprojects to EPSG:3035, lightly simplifies, and computes every pairwise polygon overlap. It skips hierarchy pairs (parent ⊃ sub-denomination; siblings of one appellation) and classifies the rest by `share` = overlap area @@ -5118,6 +5119,112 @@ is vendored at `scripts/_lib/vendor/openfreemap-{positron,dark}.json` — key-fr OpenStreetMap vector tiles that merge into one style and keep the same visibility toggle. See [scripts/_lib/vendor/README.md](scripts/_lib/vendor/README.md). +## Zoom-dependent level of detail (vineyard footprints) + +Zoom, not the Simple/Advanced toggle, decides which geometry the map +draws (branch `feat/zoom-lod`, 2026-09-23). Two vector sources, one +`source-layer` (`appellations`) and one `promoteId: 'slug'` each, built by +`_build_source_block` in +[scripts/_lib/map_template.py](scripts/_lib/map_template.py); the constants +live in [scripts/_lib/vineyard_envelope.py](scripts/_lib/vineyard_envelope.py) +(`lod_config()`) and reach app.js as the `LOD` object, so Python and JS +never disagree about the zoom bands: + +- **`appellations-overview`** (`wiki/map-data/appellations-overview.pmtiles`, + tippecanoe z3–z11, layers `maxzoom: 11.9` = the end of the crossfade, so + the layers are neither drawn nor hit-testable once the parcels are at + full opacity). For the ~1,260 French records + whose detail geometry is the INAO parcellaire (`geom_source` `parcellaire` + / `parcellaire-dgc`) it carries a **generalised vineyard footprint**: a + morphological closing of the parcels — buffer +250 m then −250 m in + EPSG:3035, simplify 30 m — `geom_source = parcellaire-envelope`. Parcels + closer than 500 m merge into one silhouette, holes narrower than that + fill, and the result never leaves the parcels' 250 m dilation; median + area 1.3× the parcels, against 5–8× for the commune union it replaces + (which painted the Gironde estuary for Saint-Estèphe). A sub-denomination + that inherits its polygon (`parent-appellation` / `sibling-dgc`) inherits + the donor's footprint (`overview_by_slug`), so a child never draws a + larger low-zoom shape than its parent. Every other record — the other + countries' zones, Champagne and the IGPs (commune unions), the + lieux-dits — draws the same polygon in both sources. +- **`appellations`** (`appellations.pmtiles`, tippecanoe z11–z12 with + `--no-tiny-polygon-reduction-at-maximum-zoom`, layers `minzoom: 11`, + overzoomed to the client's maxZoom 14): the parcels, exactly as the old + advanced mode. +- **Crossfade z11.0 → z11.9**: both sources drawn with complementary + `fill-opacity` / `line-opacity`; the selection halos fade with their + fills and sit above both fills (layer order: fill-overview, fill, + halo-overview, halo, outline-overview, outline). Single-record fits cap + at z12 so "show me this appellation" lands on parcels; `fitToFiltered` + stays at z10. +- **Feature properties.** `area` is the detail polygon's area on *both* + features (fill-sort-key and the paint ramps read it; it must not step at + the crossfade); the overview feature adds `area_drawn`. `parts` and + `frag` (perimeter/area in m⁻¹, measured on the tiled geometry by + `shape_metrics`) drive the outline: from z10, `line-width = + min(area ramp, max(W_min, K_z / frag))` with `K_z = 0.35·2^z / 108747` + — constant ink per fill area, so a 112-parcel record gets a 0.4–0.8 px + edge at z11–z12 where a single-part record keeps the area ramp, with + floors (0.25 / 0.35 / 0.5 / 0.7 px at z10 / z11 / z12 / z14) so a small + compact climat stays visible — and below the crossfade a `line-opacity` + ramp on `parts` (scale-free; perimeter/area would dim every small climat) + removes the stroke of the shapes that survive generalisation in hundreds + of patches (a sub-half-pixel width still renders as a hairline; only + opacity removes it). At the continental overview footprints stack five to + ten deep per region, so `parcellaire-envelope` features draw with no + outline below z6.5 and a fill scaled ×0.5 at z3 → ×1 at z8. +- **Build.** Stage 04 computes the footprint after the geometry-outlier + clip on the tile-simplified parcels; the parcellaire is split into + connected components first (STRtree over the r-dilated part bounds + + union-find — exact, a closing cannot merge parts more than 2r apart), + which takes Languedoc's 31,488 parts from 832 s to ~30 s. Results are + cached under `raw/cache/vineyard-envelopes/v-r/` keyed on the + input geometry's digest, so a warm build costs nothing, a new + parcellaire release invalidates exactly the records that changed, and + the fresh-build rule holds; bump `ENVELOPE_VERSION` whenever the + parameters change. Fatal gates: every slug in both tilesets (before the + split, Crozes-Hermitage and Fiefs Vendéens had no low-zoom feature at + all), the footprint bbox inside the parcels' bbox + r + simplify + (`_check_envelope`), no empty geometry; a footprint above 3× its parcels + is reported. A sub-denomination that inherits its footprint still gets + its own geometry-outlier clip applied to it. The commune-level + `appellations-villages.geojson` is still + written — not tiled — because + [scripts/audit_geometry_overlaps.py](scripts/audit_geometry_overlaps.py) + is calibrated on commune borders; the old + `appellations-villages.pmtiles` is unlinked. +- **Disclosure** (same commit as the geometry, JS and SSR in step: + `renderAocCard` in app.js and `_approx_line` in + [scripts/_lib/content_block.py](scripts/_lib/content_block.py)): the + zoom-reactive footprint line (`geom_lod_footprint`, shown below z12, + patched in place on `zoomend` — never re-render the card from a zoom, it + would wipe pressed feedback chips and a half-typed note); the + commune-union line `geom_approx_aires_union` ("réunion des {n} + communes …, plans d'eau … compris") for plain `aires-csv` / + `dgc-village-override` / `communes` records — every French IGP and + Champagne, which said nothing before; a zone-source line for the other + countries (`geom_src_geoportal` / `_mapa` / `_betard` / `_pdo_union` / + `_admin_union`); an About paragraph. `Feedback Flagged / Retracted / + Note` and `Appellation Viewed` carry `lod` (`footprint` / `parcels` / + `zone` — the band boundary is `footprint_max_zoom` = 11.9, where the + overview layers stop) and `zoom` — see [docs/analytics.md](docs/analytics.md). +- **The toggle** no longer swaps layers; it still gates spirits and the + expert facets and keeps its `view_mode` key (to be simplified to one + mode — next step). `bbox_villages` on the startup record is now the + overview feature's bbox; `drawnBbox` / `fitBbox` in app.js replace the + mode-keyed reads. +- **Audit.** [scripts/audit_vineyard_envelopes.py](scripts/audit_vineyard_envelopes.py): + parity, containment (within `containment_tolerance_m`, the polygonal + buffer's corner shave plus the simplify), bbox, inflation, and + **bridging** — the share of the footprint's *added* area that lies on + another appellation's parcels, umbrellas whose parcels contain the + record's excluded; `--water` fetches IGN BD TOPO + `surface_hydrographique` (every nature, rivers included) per record into + `raw/ign/bdtopo-hydro/` and measures the water inside the added area. + Run it with `--strict` after any stage-04 build that touches geometry; + the golden comparator does not see geometry (only the four app bundles + go red on a tileset change, through the `?v=` fingerprint). + ## Structured data (JSON-LD) on entity pages Each **indexable** per-appellation entity page (`//`) carries a @@ -5267,7 +5374,7 @@ to the console; a dev site keyed on `localhost` in `PLAUSIBLE_SITES` makes local testing land in a dashboard. The stub card's "help us find it" opens the same row on `sources`. The sidebar's GitHub-issue link is gone (clicks, no issues). The curation view is [scripts/feedback_report.py](scripts/feedback_report.py) -(`PLAUSIBLE_API_KEY` in the environment, per-session): net flags per +(`PLAUSIBLE_API_KEY` in the repo-root `.env`): net flags per appellation × aspect against panel opens, plus every note verbatim. ## Code style diff --git a/CURATOR_TODO.md b/CURATOR_TODO.md index 131c5fd..d1d3cdc 100644 --- a/CURATOR_TODO.md +++ b/CURATOR_TODO.md @@ -549,6 +549,29 @@ Also: `_prt_canonical_name` enqueues two-column "Name Synonym" strings as unknow vlaamse-landwijn (BGA §7 broad rule); appenzell-ausserrhoden, appenzell-innerrhoden, basel-stadt, nidwalden, schwyz (+ zurichsee), uri (règlement defers to federal OVin). +## Cross-country — visitor feedback triage (2026-09-22) — 🟡 2 fixed, 1 open design + +`feedback.csv` / `scripts/feedback_report.py`, all time: 4 flags, 2 retracted. Evidence and the adversarial verification (18 agents) are summarised in the session notes; numbers below were re-derived at least twice. + +| slug | aspect | verdict | action | +|---|---|---|---| +| navarra | grapes | VIVC pin "Oneca (Galvani)" | ✅ fixed 2026-09-20 (pins verified live) | +| pineau-des-charentes | grapes (fr, retracted) | roster = cahier §V (14 varieties incl. trousseau gris) — but the **fr tooltip for Trousseau Gris showed the Trousseau *noir* article** (`raw/wikipedia/grape_overrides.json` fr pin → "Trousseau": Bastardo photo, "Le trousseau N…", VIVC 14165 gris in the footer); nl was translated from that same fr card | ✅ fixed 2026-09-22: fr pin set to `null` (`override-absent`), fr + nl cards re-translated from en *Trousseau gris* | +| gres-de-montpellier | boundary (retracted) | advanced polygon = the 44 INAO parcellaire rows (Saint-Vincent-de-Barbeyrargues has a Languedoc row, none for id_denom 2917 — INAO's gap); **default (simple) mode draws the 45-commune aires-csv union: 819.8 km², 18× the 46.4 km² parcellaire, 45.9 km² of it lagoon (Étang de l'Or 29 km²), no on-screen disclosure** | ❌ open — same class as saint-estephe | +| saint-estephe | boundary (open) | advanced polygon sound: 1 INAO row, 99.95 % in commune 33395, 100 % of RPG 2023/2024 vineyard parcels inside (IGN RPG via data.geopf.fr WFS), every 1855 growth + cru bourgeois inside or < 250 m; detached 1.18 km² part = Saint-Corbian / Le Boscq; **default mode draws the IGN commune: 33.05 km², of which 8.56 km² (25.9 %) is BD TOPO `Estuaire` (Gironde) and ~9 km² western forest / palus** | ❌ open — see below | + +### Simple-mode polygons carry open water, and the panel does not say the shape is the commune — 🟡 disclosure + footprint on branch `feat/zoom-lod` (2026-09-23), water mask still open + +- **Branch `feat/zoom-lod` (2026-09-23, not merged):** zoom now picks the geometry instead of the mode — below z12 the 1,259 French parcel-level records draw a generalised vineyard footprint (250 m morphological closing of the INAO parcellaire, `scripts/_lib/vineyard_envelope.py`; median 1.3× the parcels vs 5–8× for the commune union), from z12 the parcels; the commune union is no longer drawn for them, which removes the water for the 32 records whose twin is parcellaire (Saint-Estèphe 26 % estuary → 0). Every card now says what the polygon is: the zoom-reactive footprint line, a "réunion des {n} communes, plans d'eau compris" line for plain `aires-csv` / `dgc-village-override` / `communes` records (the 97 water records that no envelope can reach — every IGP, Champagne), and a zone-source line for the other countries (geoportal / MAPA / Bétard / administrative union). `Feedback *` and `Appellation Viewed` carry `lod` + `zoom`. Audit: `scripts/audit_vineyard_envelopes.py` (`--water` measures BD TOPO water, rivers included, inside the footprint's added area). +- **Still open:** the BD TOPO open-water mask for the commune-derived geometries (the 97 FR records + the non-FR lakes below) — a display decision, the design verdict below stands; and the CH `see_flaeche` exact source. + +- Only France has a simple ≠ advanced split (354 of 2,952 villages features differ, all FR); everywhere else the "simple" polygon is the advanced one, so a visitor cannot escape the water by switching mode. +- FR sizing (villages layer ∩ BD TOPO v3 `surface_hydrographique` Estuaire / Lagune / Lac ≥ 0.2 km², scratch run 2026-09-22): 129 appellations carry ≥ 0.3 km² of water; ≥ 10 % of the polygon for muscat-de-mireval 29.7 %, muscat-de-frontignan 28.4 %, saint-estephe 25.9 %, sable-de-camargue 25.8 %, pauillac 23.2 %, pays-des-bouches-du-rhone-terre-de-camargue 19.6 %, aude-la-cote-revee 17.6 %, cotes-de-thau 14.2 %, medoc 12.6 %, pays-d-herault-berange 11.2 %, fitou 10.5 %, saint-julien 10.5 %. The scan excludes `Ecoulement naturel` (tidal rivers), so margaux (~4 % via OSM) and the Loire communes are under-reported. 97 FR *advanced* polygons are affected too — all aires-csv / aires-csv-dgc / parent-appellation / dgc-village-override, none parcellaire. +- Non-FR (Overpass sizing, verification only): cheyres 56 % Lac de Neuchâtel, lavaux 42 % / vully 35 % / la-cote 34 % Léman, zug 36 %, riviera-del-garda-classico 21.5 %, colli-del-trasimeno 18 %, balatonmelleki 12 %, noord-holland 14 %; the Neusiedler See sits inside neusiedlersee / leithaberg / burgenland / weinland. For CH the exact lake area is already on disk: swissBOUNDARIES3D `tlm_hoheitsgebiet.see_flaeche` per Gemeinde (851.6 km² over 205 communes; the 10 standalone lake rows are already dropped by `ch/geometry.py`). +- Design panel verdict (3 designs, 2 judges): **subtract an IGN BD TOPO open-water mask (Licence Ouverte 2.0) from the commune-derived geometries only** — fetch `BDTOPO_V3:surface_hydrographique` with `nature IN ('Estuaire','Lagune','Lac') AND persistance='Permanent'` in stage 00 (raw/manifest entry, sha-keyed cache), floor **0.2 km²** (1.0 leaves the Camargue at 4.6 %), mask simplified at `TILE_SIMPLIFY_TOLERANCE`, applied in stage 04 above the `parent_village_geom_by_slug` hand-off with an explicit source frozenset {aires-csv, communes, aires-csv-dgc, dgc-village-override, parent-appellation} (never parcellaire, never cadastre-lieu-dit), refusal on empty / > 70 % loss, `land_clipped` km² recorded on the feature and a `[geo] water` build line, 6-decimal coordinate rounding, memoised per shared geometry object, a `--no-water-clip` flag whose build must compare `identical` against a golden snapshot, and a CLAUDE.md note on the ordering against `geometry_outlier_overrides.json` (centroid-matched). The GISCO-LAU-coastline alternative is free (zip already in raw/es/gisco) but fixes only the Médoc, not the lagoons; OSM is verification-only (ODbL share-alike on the published geojson). Lakes and lagoons are legally inside the commune superficie, so this is a *display* decision — record it as such in the panel text. +- ~~Disclosure (owed regardless of the clip)~~ superseded by the branch above (the drawn shape is now a function of zoom, not of a hidden `v_source`): ~~emit `geom_source_villages` (from `v_source`) as a startup field (register it in `STARTUP_AOCS_FIELDS`), render a mode-aware `approx-line` when `viewMode === 'simple'` and it differs from `geom_source` (the label `meta_geom_approx_communal` exists in every catalogue and is unused), and re-render the open panel from `applyMode()` — today a mode switch leaves the card stale. `geom_approx_aires` fires only for `aires-csv-dgc`; plain `aires-csv` records (pineau-des-charentes) get no line either.~~ +- Done 2026-09-22: `Feedback Flagged` / `Retracted` / `Note` now carry `view_mode`, and the report prints it — the flag's `geom_source` is the *advanced* provenance and was misread as "the visitor was looking at the parcellaire". + ## Spain ### Pliego URLs — ✅ complete (2026-05-10) @@ -737,6 +760,9 @@ These surfaced in the audit but require code changes, not lookups: - **ES Xunta parroquia data source** — Terras do Navia delimits by Galician parroquias (sub-municipal civil parishes). Currently renders 3 whole municipios; pliego limits 2 of them to specific parroquias. Needs a Xunta / IGN parroquia cartography fetch in stage 00 plus a new `xunta-parroquia-list` step in the stage-04 ES geometry chain. Whole-municipio polygon overcounts but is visible. - **ES role-routing coverage** — 74 parents have an unrouted `name` role, 14 unrouted `geo_area`, 9 unrouted `link_to_terroir`, 4 each for `description` / `grape_varieties`. Section bodies are present, just not labelled with the canonical role. A handful more keyword additions to the stage-02 router would close most of these. Worth a separate pass when stage-04 rendering surfaces specific gaps. - **ES stage-01 `--refresh` manifest footgun** — `--refresh --only X` wipes manifest entries for wines outside the `--only` filter. Doesn't block extraction (stage 02 dispatches by file existence) but the manifest stats audit reports incorrect counts. Cosmetic. +- **Stage 04 write-time simplification is applied to the published geojson, not only the tiles** (found 2026-09-22 while verifying Saint-Estèphe) — `_simplify_for_tiles` (`TILE_SIMPLIFY_TOLERANCE` 0.0002° ≈ 22 m, 04_build_maps.py ~2660) runs before the feature is written, contrary to the comment at ~243-247; Saint-Estèphe ships 298 of 2,653 INAO vertices, Hausdorff 21.6 m, 1.13 % symmetric difference (palus lieux-dits INAO excludes appear partly inside), Grés de Montpellier −2 % area. `props.area` / `bbox` are computed *before* the pass, so they no longer describe the geometry they ship with (saint-estephe 0.0017848 vs 0.0017786 deg²). Either simplify per geom_source (parcellaire at a finer tolerance), or compute the props after, and fix the comment. Every FR parcellaire record is affected identically; ~3 px at the client's maxZoom 14. +- **`matched_via` defaults to "primary" for curator-pinned grape cards** — `scripts/_lib/lexicon_loading.py:170`; raw/wikipedia/grapes/fr/*.json written by the override path carry no `matched_via`, so an audit that trusts the field to find pinned cards misses the fr pins. Related guard worth adding to the grape audit: flag any slug whose Wikipedia card (page_url) is shared with a slug of a *different* VIVC berry colour — the fr "Trousseau" article was serving 9 slugs, one of them the gris (2026-09-22 Pineau feedback). +- **Grape-role disclaimer is PT-only** — app.js `role-disclaimer` is gated on `r.country === 'pt'`; FR records whose cahier lists no principal/accessoire split (Pineau des Charentes, all 14 shown as principal; blanc vs rosé/rouge lists and the 10 % trousseau-gris cap not surfaced) get no equivalent note. - **SSR content block omits HU dűlők / IT menzioni** ([scripts/_lib/content_block.py](scripts/_lib/content_block.py)) — the Phase-3 server-rendered `
` deliberately leaves out the Hungarian *dűlők* and Italian *menzioni / UGA* collapsible chip sections. They still render client-side via `renderDulok` / `renderMenzioni` in [scripts/_lib/map_template.py](scripts/_lib/map_template.py) (so users see them in the live panel; crawlers / no-JS do not). Port those two renderers to Python in `content_block.py` so the crawlable HTML matches the panel for HU/IT appellations. Low priority — niche chip data, the rest of the card is already server-rendered; left out initially because their per-record shape churns more than the stable fields. ## Portugal diff --git a/docs/analytics.md b/docs/analytics.md index 3ce8e35..227ef2e 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -52,7 +52,7 @@ option is inert, and a build with no `localhost` entry loads nothing there. | Event | Props | Fired when | |---|---|---| -| `Appellation Viewed` | `slug`, `country`, `kind`, `region`, `stacked`, `stack_size`, `via`, `locale` | the detail panel opens or the stack focus changes. `via` ∈ `map` (click on the map), `cycle` (re-click cycling a stack), `facet`, `omnisearch`, `panel-link` (parent/child link inside a card). **Not** fired for the page-load open of a `//` landing (the pageview already records it) nor for the localStorage restore. | +| `Appellation Viewed` | `slug`, `country`, `kind`, `region`, `stacked`, `stack_size`, `via`, `lod`, `zoom`, `locale` | the detail panel opens or the stack focus changes. `via` ∈ `map` (click on the map), `cycle` (re-click cycling a stack), `facet`, `omnisearch`, `panel-link` (parent/child link inside a card). **Not** fired for the page-load open of a `//` landing (the pageview already records it) nor for the localStorage restore. | | `Appellation Opened` | `slug`, `via` (`facet` / `omnisearch`), `locale` | an explicit pick — the cleanest interest signal | | `Filter Applied` | `facet`, `value`, `locale` | a facet checkbox / chip / country / region / appellation filter changes | | `Filters Reset` | `locale` | the reset button | @@ -63,9 +63,9 @@ option is inert, and a build with no `localhost` entry loads nothing there. | `Omnisearch Result Picked` | `type` (`grape` / `region` / `style` / `classification`), `locale` | a non-appellation suggestion is picked (appellation picks fire `Appellation Opened`) | | `Search Used` | `result_count`, `had_match`, `query_len`, `locale` | the legacy sidebar search (superseded by the omnisearch in June 2026) | | `Theme Changed` | `theme`, `locale` | light / dark toggle | -| `Feedback Flagged` | `slug`, `aspect` (`boundary` / `grapes` / `facts` / `name` / `sources` / `other`), `country`, `kind`, `geom_source`, `via` (`card` / `stub-help`), `locale` | one tap on an aspect chip in the **"Report a mistake" section at the bottom of every appellation card** (2026-09). No account, no form. Pressed chips are remembered per browser (localStorage) so a revisit does not re-fire. A `slug × aspect` breakdown is the curator queue; flags ÷ `Appellation Viewed` is a per-record trust score. | -| `Feedback Retracted` | `slug`, `aspect`, `locale` | a second tap on a pressed chip un-flags it (a misclick, or a change of mind). A flag cannot be recalled from Plausible, so net the two: flags − retractions per `slug × aspect`. | -| `Feedback Note` | `slug`, `aspect`, `note`, `locale` | the optional free-text note sent after a flag. `aspect` is every chip pressed on the card at send time, joined with `+` (`boundary+grapes`). **`note` is visitor text** — whitespace-collapsed, capped at 500 characters client-side (Plausible accepts up to 2,000), with an inline "no personal details" hint. Self-hosted, no visitor identity attached; `scripts/feedback_report.py` lists every note with its slug and date. | +| `Feedback Flagged` | `slug`, `aspect` (`boundary` / `grapes` / `facts` / `name` / `sources` / `other`), `country`, `kind`, `geom_source`, `view_mode` (`simple` / `advanced` — since the 2026-09-22 build; `geom_source` is the record's *advanced* provenance, so without the mode a boundary flag on a French appellation cannot be attributed to the commune outline or the parcellaire), `lod` (`footprint` / `parcels` / `zone` — what was on screen for this record: below the detail zoom a French parcel-level record draws its generalised vineyard footprint, from z12 its parcels, every other record one polygon at every zoom), `zoom` (the map zoom at the tap, one decimal), `via` (`card` / `stub-help`), `locale` | one tap on an aspect chip in the **"Report a mistake" section at the bottom of every appellation card** (2026-09). No account, no form. Pressed chips are remembered per browser (localStorage) so a revisit does not re-fire. A `slug × aspect` breakdown is the curator queue; flags ÷ `Appellation Viewed` is a per-record trust score. | +| `Feedback Retracted` | `slug`, `aspect`, `view_mode`, `lod`, `zoom`, `locale` | a second tap on a pressed chip un-flags it (a misclick, or a change of mind). A flag cannot be recalled from Plausible, so net the two: flags − retractions per `slug × aspect`. | +| `Feedback Note` | `slug`, `aspect`, `note`, `view_mode`, `lod`, `zoom`, `locale` | the optional free-text note sent after a flag. `aspect` is every chip pressed on the card at send time, joined with `+` (`boundary+grapes`). **`note` is visitor text** — whitespace-collapsed, capped at 500 characters client-side (Plausible accepts up to 2,000), with an inline "no personal details" hint. Self-hosted, no visitor identity attached; `scripts/feedback_report.py` lists every note with its slug and date. | | `Feedback Clicked` | `channel` (`email`), `locale` | the e-mail link in the sidebar disclaimer. The GitHub-issue link was removed 2026-09: months of clicks produced zero issues (a login wall + blank form). GitHub stays reachable from the About dialog for PRs. | | `Outbound Link: Click` | `url` | Plausible's own outbound-link tracking (source PDFs, Wikipedia, interprofessions, GitHub) | @@ -133,9 +133,9 @@ cannot net a flag against its retraction, so the curation view is three `Feedback *` goals through the Stats API and prints a Markdown report — the queue (net flags per appellation × aspect, `Appellation Viewed` over the same range as a trust ratio, geometry source) and every note verbatim with -its date. The key comes from the environment, never a file: +its date. The Stats API key is `PLAUSIBLE_API_KEY` in the repo-root `.env` +(gitignored, next to the deploy and CARTO keys; an exported variable wins): - export PLAUSIBLE_API_KEY= .venv/bin/python scripts/feedback_report.py # production, all time .venv/bin/python scripts/feedback_report.py --site beta.openwinemap.com --range 30d .venv/bin/python scripts/feedback_report.py --range 2026-09-01,2026-09-30 --json /tmp/fb.json diff --git a/locale/en/LC_MESSAGES/messages.po b/locale/en/LC_MESSAGES/messages.po index eba0c31..e36d7f7 100644 --- a/locale/en/LC_MESSAGES/messages.po +++ b/locale/en/LC_MESSAGES/messages.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-20 21:16+0200\n" +"POT-Creation-Date: 2026-09-23 08:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -127,8 +127,8 @@ msgstr "Principal grape varieties" msgid "Cépages accessoires" msgstr "Accessory grape varieties" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:189 -#: scripts/_lib/map_template.py:305 +#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:355 msgid "Cépages" msgstr "Grape varieties" @@ -304,11 +304,11 @@ msgstr "Styles" msgid "Variétés d'intérêt" msgstr "Varieties of interest" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:308 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 msgid "Sources" msgstr "Sources" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:306 +#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 msgid "Terroir" msgstr "Terroir" @@ -445,116 +445,191 @@ msgstr "" "plan of the demarcated area annexed to the product specification; this is" " not an official boundary." -#: scripts/_lib/map_template.py:165 +#: scripts/_lib/map_template.py:170 +#, python-brace-format +msgid "" +"Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " +"plans d'eau et terres non viticoles compris ; pas de délimitation " +"parcellaire pour cette dénomination." +msgstr "" +"Approximate area — the union of the {n} commune(s) of the delimited area " +"(INAO), water bodies and non-vineyard land included; no parcel " +"delimitation exists for this denomination." + +#: scripts/_lib/map_template.py:175 +#, python-brace-format +msgid "" +"Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " +"partir de la délimitation parcellaire INAO — pas la délimitation elle-" +"même ; les parcelles apparaissent en zoomant." +msgstr "" +"Overview: a silhouette of the vineyard, generalised at {radius} m from " +"the INAO parcel delimitation — not the delimitation itself; the parcels " +"appear as you zoom in." + +#: scripts/_lib/map_template.py:180 +#, python-brace-format +msgid "" +"Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " +"une silhouette de son vignoble généralisée à {radius} m à partir de la " +"délimitation parcellaire INAO ; les parcelles elles-mêmes apparaissent en" +" zoomant." +msgstr "" +"On the map, at overview zooms, this appellation is drawn as a silhouette " +"of its vineyard generalised at {radius} m from the INAO parcel " +"delimitation; the parcels themselves appear as you zoom in." + +#: scripts/_lib/map_template.py:186 +#, python-brace-format +msgid "Aire : zone de production officielle publiée par le géoportail ({region})." +msgstr "" +"Area: the official production zone published by the regional geoportal " +"({region})." + +#: scripts/_lib/map_template.py:190 +msgid "" +"Aire : zone de production officielle du MAPA (Zonas de Calidad " +"Diferenciada : Vinos)." +msgstr "" +"Area: the official MAPA production zone (Zonas de Calidad Diferenciada: " +"Vinos)." + +#: scripts/_lib/map_template.py:194 +msgid "" +"Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " +"la résolution des communes ; peut inclure des terres non viticoles." +msgstr "" +"Approximate area — the Bétard 2022 dataset polygon (EU PDOs), at " +"municipality resolution; may include non-vineyard land." + +#: scripts/_lib/map_template.py:199 +msgid "" +"Aire approchée — réunion des polygones des appellations membres (Bétard " +"2022), à la résolution des communes." +msgstr "" +"Approximate area — the union of the member appellations' polygons (Bétard" +" 2022), at municipality resolution." + +#: scripts/_lib/map_template.py:203 +msgid "" +"Aire approchée — réunion des unités administratives nommées dans le " +"document (limites Eurostat GISCO ou cadastre national), terres non " +"viticoles comprises." +msgstr "" +"Approximate area — the union of the administrative units named in the " +"document (Eurostat GISCO or national cadastre boundaries), non-vineyard " +"land included." + +#: scripts/_lib/map_template.py:207 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} appellations at this point" -#: scripts/_lib/map_template.py:166 +#: scripts/_lib/map_template.py:208 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Click again to cycle through the others" -#: scripts/_lib/map_template.py:167 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Product specification (BO Agri, PDF)" -#: scripts/_lib/map_template.py:168 +#: scripts/_lib/map_template.py:210 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "Product specification (EU GI register, PDF)" -#: scripts/_lib/map_template.py:169 +#: scripts/_lib/map_template.py:211 msgid "homologué" msgstr "approved" -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:212 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:171 +#: scripts/_lib/map_template.py:213 msgid "Texte officiel INAO (show_texte)" msgstr "Official INAO text (show_texte)" -#: scripts/_lib/map_template.py:172 +#: scripts/_lib/map_template.py:214 msgid "Fiche produit INAO" msgstr "INAO product entry" -#: scripts/_lib/map_template.py:173 +#: scripts/_lib/map_template.py:215 msgid "Site officiel de l'interprofession" msgstr "Official trade body site" -#: scripts/_lib/map_template.py:174 +#: scripts/_lib/map_template.py:216 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Specification (EUR-Lex, single document)" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:217 msgid "Pliego de condiciones (national, PDF)" msgstr "National pliego de condiciones (PDF)" -#: scripts/_lib/map_template.py:176 +#: scripts/_lib/map_template.py:218 msgid "variétés ajoutées" msgstr "varieties added" -#: scripts/_lib/map_template.py:177 +#: scripts/_lib/map_template.py:219 msgid "Cahier des charges national (PDF)" msgstr "National product specification (PDF)" -#: scripts/_lib/map_template.py:178 +#: scripts/_lib/map_template.py:220 msgid "Spécification du produit (IGP, PDF)" msgstr "Product specification (PGI, PDF)" -#: scripts/_lib/map_template.py:179 +#: scripts/_lib/map_template.py:221 msgid "Registre régional des cépages (PDF)" msgstr "Regional grape register (PDF)" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:222 msgid "Registre eAmbrosia (UE)" msgstr "eAmbrosia register (EU)" -#: scripts/_lib/map_template.py:181 +#: scripts/_lib/map_template.py:223 msgid "Numéro de dossier" msgstr "File number" -#: scripts/_lib/map_template.py:182 +#: scripts/_lib/map_template.py:224 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Cantonal wine regulation" -#: scripts/_lib/map_template.py:183 +#: scripts/_lib/map_template.py:225 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Swiss AOC register (FOAG/BLW)" -#: scripts/_lib/map_template.py:184 +#: scripts/_lib/map_template.py:226 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Product specification (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:185 +#: scripts/_lib/map_template.py:227 msgid "Registre des IG du Royaume-Uni" msgstr "UK GI register" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:228 msgid "Légende couleurs" msgstr "Colour legend" -#: scripts/_lib/map_template.py:187 +#: scripts/_lib/map_template.py:229 msgid "Bassin viticole" msgstr "Wine basin" -#: scripts/_lib/map_template.py:188 +#: scripts/_lib/map_template.py:230 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Smaller area = denser fill." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:232 msgid "principal — variété de la cuvée" msgstr "principal — main variety" -#: scripts/_lib/map_template.py:191 +#: scripts/_lib/map_template.py:233 msgid "accessoire — assemblage limité" msgstr "accessory — limited blending" -#: scripts/_lib/map_template.py:192 +#: scripts/_lib/map_template.py:234 msgid "intérêt — observation/conservation" msgstr "of interest — observation / conservation" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:236 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" @@ -564,7 +639,7 @@ msgstr "" "accessory varieties — every authorised casta is listed together in the " "caderno de especificações." -#: scripts/_lib/map_template.py:198 +#: scripts/_lib/map_template.py:240 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " @@ -574,20 +649,20 @@ msgstr "" "in line with the disciplinare of the Bianchello del Metauro DOP; the VIVC" " catalogue records it as a synonym of Trebbiano Toscano." -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:245 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "Open Wine Map has not yet located a {doc} for this appellation." -#: scripts/_lib/map_template.py:205 +#: scripts/_lib/map_template.py:247 msgid "aidez-nous à le trouver" msgstr "help us find it" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:251 msgid "Annulée" msgstr "Cancelled" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:253 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -598,141 +673,141 @@ msgstr "" "labels and teaching material may still mention it; it is kept here for " "reference." -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Acte national : {act}." msgstr "National act: {act}." -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "It is succeeded by {successor}." -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:259 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Registration cancelled on {date}" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:265 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "Delimited by {regulator} in its {doc}, which authorises {grapes}." -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "Delimited by {regulator} in its {doc}{extra}." -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:268 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "{names} and {n} other grape varieties" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:269 msgid "le registre eAmbrosia de l'UE" msgstr "the EU eAmbrosia register" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:270 #, python-brace-format msgid "le canton de {canton}" msgstr "the canton of {canton}" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:271 msgid "(français)" msgstr "(source: French)" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:272 msgid "Texte source en français" msgstr "Source text in French" -#: scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:273 msgid "(español)" msgstr "(source: Spanish)" -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:274 msgid "Texte source en espagnol" msgstr "Source text in Spanish" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:275 msgid "(português)" msgstr "(source: Portuguese)" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:276 msgid "Texte source en portugais" msgstr "Source text in Portuguese" -#: scripts/_lib/map_template.py:235 +#: scripts/_lib/map_template.py:277 msgid "Filtres" msgstr "Filters" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:278 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Translated from {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:237 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en anglais" msgstr "English Wikipedia" -#: scripts/_lib/map_template.py:238 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en français" msgstr "French Wikipedia" -#: scripts/_lib/map_template.py:239 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en espagnol" msgstr "Spanish Wikipedia" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en néerlandais" msgstr "Dutch Wikipedia" -#: scripts/_lib/map_template.py:241 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en portugais" msgstr "Portuguese Wikipedia" -#: scripts/_lib/map_template.py:242 +#: scripts/_lib/map_template.py:284 msgid "Wikipédia en croate" msgstr "Croatian Wikipedia" -#: scripts/_lib/map_template.py:243 +#: scripts/_lib/map_template.py:285 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "Vitis International Variety Catalogue (Julius Kühn-Institut)" -#: scripts/_lib/map_template.py:244 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "VIVC #{id}" msgstr "VIVC #{id}" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:287 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Machine translated from {source}" -#: scripts/_lib/map_template.py:246 +#: scripts/_lib/map_template.py:288 msgid "le cahier des charges" msgstr "the cahier des charges" -#: scripts/_lib/map_template.py:247 scripts/_lib/map_template.py:249 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le pliego de condiciones" msgstr "the specification" -#: scripts/_lib/map_template.py:248 scripts/_lib/map_template.py:250 +#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 msgid "le caderno de especificações" msgstr "the caderno de especificações" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:293 msgid "Dénomination géographique complémentaire de" msgstr "Sub-region of" -#: scripts/_lib/map_template.py:252 +#: scripts/_lib/map_template.py:294 msgid "À propos" msgstr "About" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:295 msgid "À propos d'Open Wine Map" msgstr "About Open Wine Map" -#: scripts/_lib/map_template.py:255 +#: scripts/_lib/map_template.py:297 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -744,7 +819,7 @@ msgstr "" " Swiss federal repertoire of cantonal AOCs and the UK register of " "geographical indications." -#: scripts/_lib/map_template.py:262 +#: scripts/_lib/map_template.py:304 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -764,12 +839,12 @@ msgstr "" "human translator, with a small machine-translated remainder. Each item " "carries its own attribution line in the panel." -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:313 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Made with ♡ by {devloed}." -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:315 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -793,12 +868,28 @@ msgstr "" "Röckel et al.). Every Wikipedia excerpt is flagged where it appears. Full" " source list and licences in the {readme}." -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:326 +#, python-brace-format +msgid "" +"Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" +" dotées d'une délimitation parcellaire INAO sont dessinées comme une " +"silhouette de leur vignoble, généralisée à {radius} m (fermeture " +"morphologique des parcelles) ; à partir du zoom {zoom}, ce sont les " +"parcelles elles-mêmes. Les autres appellations gardent le même contour à " +"tous les zooms." +msgstr "" +"Level of detail: below zoom {zoom}, French appellations with an INAO " +"parcel delimitation are drawn as a silhouette of their vineyard, " +"generalised at {radius} m (a morphological closing of the parcels); from " +"zoom {zoom} the parcels themselves are drawn. Every other appellation " +"keeps the same outline at every zoom." + +#: scripts/_lib/map_template.py:333 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Issues and pull requests welcome on {github}." -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:335 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -814,20 +905,20 @@ msgstr "" "identifiers, no IP address stored, no cross-site or cross-device tracking" " — which is why there is no consent banner to click through." -#: scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:342 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Follow updates on {bluesky}." -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:343 msgid "e-mail" msgstr "email" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:344 msgid "E-mail copié dans le presse-papiers" msgstr "Email copied to clipboard" -#: scripts/_lib/map_template.py:296 +#: scripts/_lib/map_template.py:346 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -836,55 +927,55 @@ msgstr "" "Auto-generated map — errors are possible. Flag them from the appellation " "card, or by {email}." -#: scripts/_lib/map_template.py:302 +#: scripts/_lib/map_template.py:352 msgid "Signaler une erreur" msgstr "Report a mistake" -#: scripts/_lib/map_template.py:303 +#: scripts/_lib/map_template.py:353 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "One tap tells us what to check — no account needed." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:354 msgid "Aire / contour" msgstr "Boundary" -#: scripts/_lib/map_template.py:307 +#: scripts/_lib/map_template.py:357 msgid "Nom / type" msgstr "Name / type" -#: scripts/_lib/map_template.py:309 +#: scripts/_lib/map_template.py:359 msgid "Autre" msgstr "Other" -#: scripts/_lib/map_template.py:310 +#: scripts/_lib/map_template.py:360 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Thanks, noted: {aspect}." -#: scripts/_lib/map_template.py:312 +#: scripts/_lib/map_template.py:362 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Add details if you like (optional — no personal details)" -#: scripts/_lib/map_template.py:314 +#: scripts/_lib/map_template.py:364 msgid "Envoyer" msgstr "Send" -#: scripts/_lib/map_template.py:317 +#: scripts/_lib/map_template.py:367 msgid "Géométrie" msgstr "Geometry" -#: scripts/_lib/map_template.py:318 +#: scripts/_lib/map_template.py:368 msgid "Envoyé, merci !" msgstr "Sent — thank you!" -#: scripts/_lib/map_template.py:320 +#: scripts/_lib/map_template.py:370 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." msgstr "{aspect} — your browser blocks our analytics; send an {email} instead." -#: scripts/_lib/map_template.py:324 +#: scripts/_lib/map_template.py:374 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -897,15 +988,15 @@ msgstr "" "quality and extend coverage beyond the EU, Switzerland and the United " "Kingdom." -#: scripts/_lib/map_template.py:329 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations" msgstr "All appellations" -#: scripts/_lib/map_template.py:330 +#: scripts/_lib/map_template.py:380 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "All wine appellations — Open Wine Map" -#: scripts/_lib/map_template.py:332 +#: scripts/_lib/map_template.py:382 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -916,192 +1007,192 @@ msgstr "" " PDOs and PGIs with their traditional terms (AOC, DOCG, DOQ…), Swiss AOCs" " and UK GIs." -#: scripts/_lib/map_template.py:337 +#: scripts/_lib/map_template.py:387 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " "{map_link}." msgstr "The {n} appellations below are grouped by country. Back to the {map_link}." -#: scripts/_lib/map_template.py:340 +#: scripts/_lib/map_template.py:390 msgid "carte interactive" msgstr "interactive map" -#: scripts/_lib/map_template.py:341 +#: scripts/_lib/map_template.py:391 msgid "Liste des appellations par pays" msgstr "List of appellations by country" -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:392 msgid "Appellation parente" msgstr "Parent appellation" -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:393 msgid "Dénominations rattachées" msgstr "Related denominations" -#: scripts/_lib/map_template.py:345 +#: scripts/_lib/map_template.py:395 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Browse the full list of appellations: {browse_link}." -#: scripts/_lib/map_template.py:347 +#: scripts/_lib/map_template.py:397 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Data updated on {date}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:412 msgid "BOURGOGNE" msgstr "Burgundy" -#: scripts/_lib/map_template.py:363 +#: scripts/_lib/map_template.py:413 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:414 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:365 +#: scripts/_lib/map_template.py:415 msgid "SAVOIE" msgstr "Savoie" -#: scripts/_lib/map_template.py:366 +#: scripts/_lib/map_template.py:416 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:417 msgid "ALSACE ET EST" msgstr "Alsace and East" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:418 msgid "VAL DE LOIRE" msgstr "Loire Valley" -#: scripts/_lib/map_template.py:369 +#: scripts/_lib/map_template.py:419 msgid "SUD-OUEST" msgstr "South-West" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:420 msgid "VALLEE DU RHÔNE" msgstr "Rhône Valley" -#: scripts/_lib/map_template.py:371 +#: scripts/_lib/map_template.py:421 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Roussillon" -#: scripts/_lib/map_template.py:372 +#: scripts/_lib/map_template.py:422 msgid "TOULOUSE-PYRENEES" msgstr "Toulouse-Pyrenees" -#: scripts/_lib/map_template.py:373 +#: scripts/_lib/map_template.py:423 msgid "PROVENCE-CORSE" msgstr "Provence-Corsica" -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:424 msgid "CHAMPAGNE" msgstr "Champagne" -#: scripts/_lib/map_template.py:375 +#: scripts/_lib/map_template.py:425 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Cider eaux-de-vie" -#: scripts/_lib/map_template.py:376 +#: scripts/_lib/map_template.py:426 msgid "VIN DOUX NATURELS" msgstr "Natural sweet wines" -#: scripts/_lib/map_template.py:377 +#: scripts/_lib/map_template.py:427 msgid "COGNAC" msgstr "Cognac" -#: scripts/_lib/map_template.py:378 +#: scripts/_lib/map_template.py:428 msgid "ARMAGNAC" msgstr "Armagnac" -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:429 msgid "RHUM" msgstr "Rum" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:469 msgid "France" msgstr "France" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:470 msgid "Espagne" msgstr "Spain" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:471 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:472 msgid "Italie" msgstr "Italy" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:473 msgid "Autriche" msgstr "Austria" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:474 msgid "Slovénie" msgstr "Slovenia" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:475 msgid "Croatie" msgstr "Croatia" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:476 msgid "Hongrie" msgstr "Hungary" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:477 msgid "Roumanie" msgstr "Romania" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:478 msgid "Bulgarie" msgstr "Bulgaria" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:479 msgid "Grèce" msgstr "Greece" -#: scripts/_lib/map_template.py:430 +#: scripts/_lib/map_template.py:480 msgid "Allemagne" msgstr "Germany" -#: scripts/_lib/map_template.py:431 +#: scripts/_lib/map_template.py:481 msgid "Slovaquie" msgstr "Slovakia" -#: scripts/_lib/map_template.py:432 +#: scripts/_lib/map_template.py:482 msgid "Suisse" msgstr "Switzerland" -#: scripts/_lib/map_template.py:433 +#: scripts/_lib/map_template.py:483 msgid "Tchéquie" msgstr "Czechia" -#: scripts/_lib/map_template.py:434 +#: scripts/_lib/map_template.py:484 msgid "Luxembourg" msgstr "Luxembourg" -#: scripts/_lib/map_template.py:435 +#: scripts/_lib/map_template.py:485 msgid "Belgique" msgstr "Belgium" -#: scripts/_lib/map_template.py:436 +#: scripts/_lib/map_template.py:486 msgid "Pays-Bas" msgstr "Netherlands" -#: scripts/_lib/map_template.py:437 +#: scripts/_lib/map_template.py:487 msgid "Malte" msgstr "Malta" -#: scripts/_lib/map_template.py:438 +#: scripts/_lib/map_template.py:488 msgid "Chypre" msgstr "Cyprus" -#: scripts/_lib/map_template.py:439 +#: scripts/_lib/map_template.py:489 msgid "Royaume-Uni" msgstr "United Kingdom" diff --git a/locale/es/LC_MESSAGES/messages.po b/locale/es/LC_MESSAGES/messages.po index b70fbb4..95cea34 100644 --- a/locale/es/LC_MESSAGES/messages.po +++ b/locale/es/LC_MESSAGES/messages.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-20 21:16+0200\n" +"POT-Creation-Date: 2026-09-23 08:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: es\n" @@ -127,8 +127,8 @@ msgstr "Variedades principales" msgid "Cépages accessoires" msgstr "Variedades complementarias" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:189 -#: scripts/_lib/map_template.py:305 +#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:355 msgid "Cépages" msgstr "Variedades de uva" @@ -304,11 +304,11 @@ msgstr "Estilos" msgid "Variétés d'intérêt" msgstr "Variedades de interés" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:308 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 msgid "Sources" msgstr "Fuentes" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:306 +#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 msgid "Terroir" msgstr "Terroir" @@ -445,116 +445,192 @@ msgstr "" "del plano de la zona delimitada anexo al pliego de condiciones; no es un " "límite oficial." -#: scripts/_lib/map_template.py:165 +#: scripts/_lib/map_template.py:170 +#, python-brace-format +msgid "" +"Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " +"plans d'eau et terres non viticoles compris ; pas de délimitation " +"parcellaire pour cette dénomination." +msgstr "" +"Área aproximada: unión de los {n} municipio(s) del área delimitada " +"(INAO), incluidas las masas de agua y las tierras no vitícolas; no existe" +" delimitación parcelaria para esta denominación." + +#: scripts/_lib/map_template.py:175 +#, python-brace-format +msgid "" +"Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " +"partir de la délimitation parcellaire INAO — pas la délimitation elle-" +"même ; les parcelles apparaissent en zoomant." +msgstr "" +"Vista general: silueta del viñedo, generalizada a {radius} m a partir de " +"la delimitación parcelaria del INAO; no es la delimitación en sí. Las " +"parcelas aparecen al acercar el zoom." + +#: scripts/_lib/map_template.py:180 +#, python-brace-format +msgid "" +"Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " +"une silhouette de son vignoble généralisée à {radius} m à partir de la " +"délimitation parcellaire INAO ; les parcelles elles-mêmes apparaissent en" +" zoomant." +msgstr "" +"En el mapa, en los niveles de zoom generales, esta denominación se dibuja" +" como una silueta de su viñedo generalizada a {radius} m a partir de la " +"delimitación parcelaria del INAO; las propias parcelas aparecen al " +"acercar el zoom." + +#: scripts/_lib/map_template.py:186 +#, python-brace-format +msgid "Aire : zone de production officielle publiée par le géoportail ({region})." +msgstr "" +"Área: zona de producción oficial publicada por el geoportal regional " +"({region})." + +#: scripts/_lib/map_template.py:190 +msgid "" +"Aire : zone de production officielle du MAPA (Zonas de Calidad " +"Diferenciada : Vinos)." +msgstr "" +"Área: zona de producción oficial del MAPA (Zonas de Calidad Diferenciada:" +" Vinos)." + +#: scripts/_lib/map_template.py:194 +msgid "" +"Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " +"la résolution des communes ; peut inclure des terres non viticoles." +msgstr "" +"Área aproximada: polígono del conjunto de datos Bétard 2022 (DOP de la " +"UE), con resolución municipal; puede incluir tierras no vitícolas." + +#: scripts/_lib/map_template.py:199 +msgid "" +"Aire approchée — réunion des polygones des appellations membres (Bétard " +"2022), à la résolution des communes." +msgstr "" +"Área aproximada: unión de los polígonos de las denominaciones miembro " +"(Bétard 2022), con resolución municipal." + +#: scripts/_lib/map_template.py:203 +msgid "" +"Aire approchée — réunion des unités administratives nommées dans le " +"document (limites Eurostat GISCO ou cadastre national), terres non " +"viticoles comprises." +msgstr "" +"Área aproximada: unión de las unidades administrativas citadas en el " +"documento (límites de Eurostat GISCO o del catastro nacional), incluidas " +"las tierras no vitícolas." + +#: scripts/_lib/map_template.py:207 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} denominaciones en este punto" -#: scripts/_lib/map_template.py:166 +#: scripts/_lib/map_template.py:208 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Vuelve a hacer clic para recorrer las demás" -#: scripts/_lib/map_template.py:167 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Pliego de condiciones (BO Agri, PDF)" -#: scripts/_lib/map_template.py:168 +#: scripts/_lib/map_template.py:210 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "Pliego de condiciones (registro de IG de la UE, PDF)" -#: scripts/_lib/map_template.py:169 +#: scripts/_lib/map_template.py:211 msgid "homologué" msgstr "homologado" -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:212 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:171 +#: scripts/_lib/map_template.py:213 msgid "Texte officiel INAO (show_texte)" msgstr "Texto oficial INAO (show_texte)" -#: scripts/_lib/map_template.py:172 +#: scripts/_lib/map_template.py:214 msgid "Fiche produit INAO" msgstr "Ficha de producto INAO" -#: scripts/_lib/map_template.py:173 +#: scripts/_lib/map_template.py:215 msgid "Site officiel de l'interprofession" msgstr "Sitio oficial de la interprofesión" -#: scripts/_lib/map_template.py:174 +#: scripts/_lib/map_template.py:216 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Pliego de condiciones (EUR-Lex, documento único)" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:217 msgid "Pliego de condiciones (national, PDF)" msgstr "Pliego de condiciones nacional (PDF)" -#: scripts/_lib/map_template.py:176 +#: scripts/_lib/map_template.py:218 msgid "variétés ajoutées" msgstr "variedades añadidas" -#: scripts/_lib/map_template.py:177 +#: scripts/_lib/map_template.py:219 msgid "Cahier des charges national (PDF)" msgstr "Pliego de condiciones nacional (PDF)" -#: scripts/_lib/map_template.py:178 +#: scripts/_lib/map_template.py:220 msgid "Spécification du produit (IGP, PDF)" msgstr "Pliego de condiciones (IGP, PDF)" -#: scripts/_lib/map_template.py:179 +#: scripts/_lib/map_template.py:221 msgid "Registre régional des cépages (PDF)" msgstr "Registro regional de variedades (PDF)" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:222 msgid "Registre eAmbrosia (UE)" msgstr "Registro eAmbrosia (UE)" -#: scripts/_lib/map_template.py:181 +#: scripts/_lib/map_template.py:223 msgid "Numéro de dossier" msgstr "Número de expediente" -#: scripts/_lib/map_template.py:182 +#: scripts/_lib/map_template.py:224 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Reglamento cantonal de la viña y el vino" -#: scripts/_lib/map_template.py:183 +#: scripts/_lib/map_template.py:225 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Registro suizo de AOC (OFAG/BLW)" -#: scripts/_lib/map_template.py:184 +#: scripts/_lib/map_template.py:226 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Pliego de condiciones (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:185 +#: scripts/_lib/map_template.py:227 msgid "Registre des IG du Royaume-Uni" msgstr "Registro de IG del Reino Unido" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:228 msgid "Légende couleurs" msgstr "Leyenda de colores" -#: scripts/_lib/map_template.py:187 +#: scripts/_lib/map_template.py:229 msgid "Bassin viticole" msgstr "Cuenca vitícola" -#: scripts/_lib/map_template.py:188 +#: scripts/_lib/map_template.py:230 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Cuanto menor es el área, más densa es la tonalidad." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:232 msgid "principal — variété de la cuvée" msgstr "principal — variedad de la mezcla" -#: scripts/_lib/map_template.py:191 +#: scripts/_lib/map_template.py:233 msgid "accessoire — assemblage limité" msgstr "accesoria — mezcla limitada" -#: scripts/_lib/map_template.py:192 +#: scripts/_lib/map_template.py:234 msgid "intérêt — observation/conservation" msgstr "de interés — observación/conservación" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:236 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" @@ -564,7 +640,7 @@ msgstr "" "accesorias — todas las castas autorizadas se enumeran juntas en el " "caderno de especificações." -#: scripts/_lib/map_template.py:198 +#: scripts/_lib/map_template.py:240 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " @@ -574,20 +650,20 @@ msgstr "" "conforme al disciplinare de la DOP Bianchello del Metauro; el catálogo " "VIVC lo registra como sinónimo de Trebbiano Toscano." -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:245 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "Open Wine Map todavía no ha localizado un {doc} para esta denominación." -#: scripts/_lib/map_template.py:205 +#: scripts/_lib/map_template.py:247 msgid "aidez-nous à le trouver" msgstr "ayúdanos a encontrarlo" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:251 msgid "Annulée" msgstr "Cancelada" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:253 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -598,141 +674,141 @@ msgstr "" "etiquetas y materiales didácticos antiguos pueden seguir mencionándola; " "se conserva aquí como referencia." -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Acte national : {act}." msgstr "Acto nacional: {act}." -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "Le sucede {successor}." -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:259 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Registro cancelado el {date}" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:265 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "Delimitada por {regulator} en su {doc}, que autoriza {grapes}." -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "Delimitada por {regulator} en su {doc}{extra}." -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:268 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "{names} y otras {n} variedades de uva" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:269 msgid "le registre eAmbrosia de l'UE" msgstr "el registro eAmbrosia de la UE" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:270 #, python-brace-format msgid "le canton de {canton}" msgstr "el cantón de {canton}" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:271 msgid "(français)" msgstr "(fuente: francés)" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:272 msgid "Texte source en français" msgstr "Texto fuente en francés" -#: scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:273 msgid "(español)" msgstr "(fuente: español)" -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:274 msgid "Texte source en espagnol" msgstr "Texto fuente en español" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:275 msgid "(português)" msgstr "(fuente: portugués)" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:276 msgid "Texte source en portugais" msgstr "Texto fuente en portugués" -#: scripts/_lib/map_template.py:235 +#: scripts/_lib/map_template.py:277 msgid "Filtres" msgstr "Filtros" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:278 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Traducido de {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:237 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en anglais" msgstr "Wikipedia en inglés" -#: scripts/_lib/map_template.py:238 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en français" msgstr "Wikipedia en francés" -#: scripts/_lib/map_template.py:239 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en espagnol" msgstr "Wikipedia en español" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en néerlandais" msgstr "Wikipedia en neerlandés" -#: scripts/_lib/map_template.py:241 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en portugais" msgstr "Wikipedia en portugués" -#: scripts/_lib/map_template.py:242 +#: scripts/_lib/map_template.py:284 msgid "Wikipédia en croate" msgstr "Wikipedia en croata" -#: scripts/_lib/map_template.py:243 +#: scripts/_lib/map_template.py:285 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "Vitis International Variety Catalogue (Julius Kühn-Institut)" -#: scripts/_lib/map_template.py:244 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "VIVC #{id}" msgstr "VIVC #{id}" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:287 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Traducción automática desde {source}" -#: scripts/_lib/map_template.py:246 +#: scripts/_lib/map_template.py:288 msgid "le cahier des charges" msgstr "el pliego de condiciones" -#: scripts/_lib/map_template.py:247 scripts/_lib/map_template.py:249 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le pliego de condiciones" msgstr "el pliego de condiciones" -#: scripts/_lib/map_template.py:248 scripts/_lib/map_template.py:250 +#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 msgid "le caderno de especificações" msgstr "el caderno de especificações" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:293 msgid "Dénomination géographique complémentaire de" msgstr "Subzona de" -#: scripts/_lib/map_template.py:252 +#: scripts/_lib/map_template.py:294 msgid "À propos" msgstr "Acerca de" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:295 msgid "À propos d'Open Wine Map" msgstr "Acerca de Open Wine Map" -#: scripts/_lib/map_template.py:255 +#: scripts/_lib/map_template.py:297 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -745,7 +821,7 @@ msgstr "" "suizo de AOC cantonales y el registro británico de indicaciones " "geográficas." -#: scripts/_lib/map_template.py:262 +#: scripts/_lib/map_template.py:304 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -766,12 +842,12 @@ msgstr "" "salvo un pequeño resto traducido automáticamente. Cada elemento lleva su " "propia línea de atribución en el panel." -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:313 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Hecho con ♡ por {devloed}." -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:315 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -796,12 +872,29 @@ msgstr "" "extracto de Wikipedia se indica allí donde aparece. Detalles y licencias " "en el {readme}." -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:326 +#, python-brace-format +msgid "" +"Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" +" dotées d'une délimitation parcellaire INAO sont dessinées comme une " +"silhouette de leur vignoble, généralisée à {radius} m (fermeture " +"morphologique des parcelles) ; à partir du zoom {zoom}, ce sont les " +"parcelles elles-mêmes. Les autres appellations gardent le même contour à " +"tous les zooms." +msgstr "" +"Nivel de detalle: por debajo del zoom {zoom}, las denominaciones " +"francesas con delimitación parcelaria del INAO se dibujan como una " +"silueta de su viñedo, generalizada a {radius} m (cierre morfológico de " +"las parcelas); a partir del zoom {zoom} se dibujan las propias parcelas. " +"Las demás denominaciones conservan el mismo contorno en todos los niveles" +" de zoom." + +#: scripts/_lib/map_template.py:333 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Sugerencias y pull requests bienvenidos en {github}." -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:335 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -818,20 +911,20 @@ msgstr "" "seguimiento entre sitios ni entre dispositivos — por eso no hay banner de" " consentimiento." -#: scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:342 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Sigue las novedades en {bluesky}." -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:343 msgid "e-mail" msgstr "correo" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:344 msgid "E-mail copié dans le presse-papiers" msgstr "Correo copiado al portapapeles" -#: scripts/_lib/map_template.py:296 +#: scripts/_lib/map_template.py:346 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -840,48 +933,48 @@ msgstr "" "Mapa generado automáticamente — puede contener errores. Señálalos desde " "la ficha de la denominación, o por {email}." -#: scripts/_lib/map_template.py:302 +#: scripts/_lib/map_template.py:352 msgid "Signaler une erreur" msgstr "Señalar un error" -#: scripts/_lib/map_template.py:303 +#: scripts/_lib/map_template.py:353 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "Un toque nos dice qué revisar — sin necesidad de cuenta." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:354 msgid "Aire / contour" msgstr "Zona / límites" -#: scripts/_lib/map_template.py:307 +#: scripts/_lib/map_template.py:357 msgid "Nom / type" msgstr "Nombre / tipo" -#: scripts/_lib/map_template.py:309 +#: scripts/_lib/map_template.py:359 msgid "Autre" msgstr "Otro" -#: scripts/_lib/map_template.py:310 +#: scripts/_lib/map_template.py:360 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Gracias, anotado: {aspect}." -#: scripts/_lib/map_template.py:312 +#: scripts/_lib/map_template.py:362 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Añade detalles si quieres (opcional, sin datos personales)" -#: scripts/_lib/map_template.py:314 +#: scripts/_lib/map_template.py:364 msgid "Envoyer" msgstr "Enviar" -#: scripts/_lib/map_template.py:317 +#: scripts/_lib/map_template.py:367 msgid "Géométrie" msgstr "Geometría" -#: scripts/_lib/map_template.py:318 +#: scripts/_lib/map_template.py:368 msgid "Envoyé, merci !" msgstr "Enviado, ¡gracias!" -#: scripts/_lib/map_template.py:320 +#: scripts/_lib/map_template.py:370 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " @@ -890,7 +983,7 @@ msgstr "" "{aspect} — tu navegador bloquea nuestra herramienta de medición; envía un" " {email} en su lugar." -#: scripts/_lib/map_template.py:324 +#: scripts/_lib/map_template.py:374 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -903,15 +996,15 @@ msgstr "" "iteraciones afinarán la calidad de los datos y ampliarán la cobertura más" " allá de la UE, Suiza y el Reino Unido." -#: scripts/_lib/map_template.py:329 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations" msgstr "Todas las denominaciones" -#: scripts/_lib/map_template.py:330 +#: scripts/_lib/map_template.py:380 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "Todas las denominaciones vitivinícolas — Open Wine Map" -#: scripts/_lib/map_template.py:332 +#: scripts/_lib/map_template.py:382 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -922,7 +1015,7 @@ msgstr "" " Map, por país: DOP e IGP de la UE con sus términos tradicionales (DOCa, " "DOQ, DOCG…), AOC suizas e IG británicas." -#: scripts/_lib/map_template.py:337 +#: scripts/_lib/map_template.py:387 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " @@ -931,185 +1024,185 @@ msgstr "" "Las {n} denominaciones siguientes están agrupadas por país. Volver al " "{map_link}." -#: scripts/_lib/map_template.py:340 +#: scripts/_lib/map_template.py:390 msgid "carte interactive" msgstr "mapa interactivo" -#: scripts/_lib/map_template.py:341 +#: scripts/_lib/map_template.py:391 msgid "Liste des appellations par pays" msgstr "Lista de denominaciones por país" -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:392 msgid "Appellation parente" msgstr "Denominación matriz" -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:393 msgid "Dénominations rattachées" msgstr "Denominaciones vinculadas" -#: scripts/_lib/map_template.py:345 +#: scripts/_lib/map_template.py:395 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Explore la lista completa de denominaciones: {browse_link}." -#: scripts/_lib/map_template.py:347 +#: scripts/_lib/map_template.py:397 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Datos actualizados el {date}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:412 msgid "BOURGOGNE" msgstr "Borgoña" -#: scripts/_lib/map_template.py:363 +#: scripts/_lib/map_template.py:413 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:414 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:365 +#: scripts/_lib/map_template.py:415 msgid "SAVOIE" msgstr "Saboya" -#: scripts/_lib/map_template.py:366 +#: scripts/_lib/map_template.py:416 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:417 msgid "ALSACE ET EST" msgstr "Alsacia y Este" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:418 msgid "VAL DE LOIRE" msgstr "Valle del Loira" -#: scripts/_lib/map_template.py:369 +#: scripts/_lib/map_template.py:419 msgid "SUD-OUEST" msgstr "Suroeste" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:420 msgid "VALLEE DU RHÔNE" msgstr "Valle del Ródano" -#: scripts/_lib/map_template.py:371 +#: scripts/_lib/map_template.py:421 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Rosellón" -#: scripts/_lib/map_template.py:372 +#: scripts/_lib/map_template.py:422 msgid "TOULOUSE-PYRENEES" msgstr "Tolosa-Pirineos" -#: scripts/_lib/map_template.py:373 +#: scripts/_lib/map_template.py:423 msgid "PROVENCE-CORSE" msgstr "Provenza-Córcega" -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:424 msgid "CHAMPAGNE" msgstr "Champaña" -#: scripts/_lib/map_template.py:375 +#: scripts/_lib/map_template.py:425 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Aguardientes de sidra" -#: scripts/_lib/map_template.py:376 +#: scripts/_lib/map_template.py:426 msgid "VIN DOUX NATURELS" msgstr "Vinos dulces naturales" -#: scripts/_lib/map_template.py:377 +#: scripts/_lib/map_template.py:427 msgid "COGNAC" msgstr "Coñac" -#: scripts/_lib/map_template.py:378 +#: scripts/_lib/map_template.py:428 msgid "ARMAGNAC" msgstr "Armañac" -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:429 msgid "RHUM" msgstr "Ron" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:469 msgid "France" msgstr "Francia" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:470 msgid "Espagne" msgstr "España" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:471 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:472 msgid "Italie" msgstr "Italia" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:473 msgid "Autriche" msgstr "Austria" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:474 msgid "Slovénie" msgstr "Eslovenia" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:475 msgid "Croatie" msgstr "Croacia" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:476 msgid "Hongrie" msgstr "Hungría" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:477 msgid "Roumanie" msgstr "Rumanía" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:478 msgid "Bulgarie" msgstr "Bulgaria" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:479 msgid "Grèce" msgstr "Grecia" -#: scripts/_lib/map_template.py:430 +#: scripts/_lib/map_template.py:480 msgid "Allemagne" msgstr "Alemania" -#: scripts/_lib/map_template.py:431 +#: scripts/_lib/map_template.py:481 msgid "Slovaquie" msgstr "Eslovaquia" -#: scripts/_lib/map_template.py:432 +#: scripts/_lib/map_template.py:482 msgid "Suisse" msgstr "Suiza" -#: scripts/_lib/map_template.py:433 +#: scripts/_lib/map_template.py:483 msgid "Tchéquie" msgstr "Chequia" -#: scripts/_lib/map_template.py:434 +#: scripts/_lib/map_template.py:484 msgid "Luxembourg" msgstr "Luxemburgo" -#: scripts/_lib/map_template.py:435 +#: scripts/_lib/map_template.py:485 msgid "Belgique" msgstr "Bélgica" -#: scripts/_lib/map_template.py:436 +#: scripts/_lib/map_template.py:486 msgid "Pays-Bas" msgstr "Países Bajos" -#: scripts/_lib/map_template.py:437 +#: scripts/_lib/map_template.py:487 msgid "Malte" msgstr "Malta" -#: scripts/_lib/map_template.py:438 +#: scripts/_lib/map_template.py:488 msgid "Chypre" msgstr "Chipre" -#: scripts/_lib/map_template.py:439 +#: scripts/_lib/map_template.py:489 msgid "Royaume-Uni" msgstr "Reino Unido" diff --git a/locale/fr/LC_MESSAGES/messages.po b/locale/fr/LC_MESSAGES/messages.po index 76052eb..9043015 100644 --- a/locale/fr/LC_MESSAGES/messages.po +++ b/locale/fr/LC_MESSAGES/messages.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-20 21:16+0200\n" +"POT-Creation-Date: 2026-09-23 08:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: fr\n" @@ -126,8 +126,8 @@ msgstr "Cépages principaux" msgid "Cépages accessoires" msgstr "Cépages accessoires" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:189 -#: scripts/_lib/map_template.py:305 +#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:355 msgid "Cépages" msgstr "Cépages" @@ -303,11 +303,11 @@ msgstr "Styles" msgid "Variétés d'intérêt" msgstr "Variétés d'intérêt" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:308 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 msgid "Sources" msgstr "Sources" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:306 +#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 msgid "Terroir" msgstr "Terroir" @@ -438,143 +438,198 @@ msgstr "" "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " "limite officielle." -#: scripts/_lib/map_template.py:165 +#: scripts/_lib/map_template.py:170 +#, python-brace-format +msgid "" +"Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " +"plans d'eau et terres non viticoles compris ; pas de délimitation " +"parcellaire pour cette dénomination." +msgstr "" + +#: scripts/_lib/map_template.py:175 +#, python-brace-format +msgid "" +"Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " +"partir de la délimitation parcellaire INAO — pas la délimitation elle-" +"même ; les parcelles apparaissent en zoomant." +msgstr "" + +#: scripts/_lib/map_template.py:180 +#, python-brace-format +msgid "" +"Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " +"une silhouette de son vignoble généralisée à {radius} m à partir de la " +"délimitation parcellaire INAO ; les parcelles elles-mêmes apparaissent en" +" zoomant." +msgstr "" + +#: scripts/_lib/map_template.py:186 +#, python-brace-format +msgid "Aire : zone de production officielle publiée par le géoportail ({region})." +msgstr "" + +#: scripts/_lib/map_template.py:190 +msgid "" +"Aire : zone de production officielle du MAPA (Zonas de Calidad " +"Diferenciada : Vinos)." +msgstr "" + +#: scripts/_lib/map_template.py:194 +msgid "" +"Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " +"la résolution des communes ; peut inclure des terres non viticoles." +msgstr "" + +#: scripts/_lib/map_template.py:199 +msgid "" +"Aire approchée — réunion des polygones des appellations membres (Bétard " +"2022), à la résolution des communes." +msgstr "" + +#: scripts/_lib/map_template.py:203 +msgid "" +"Aire approchée — réunion des unités administratives nommées dans le " +"document (limites Eurostat GISCO ou cadastre national), terres non " +"viticoles comprises." +msgstr "" + +#: scripts/_lib/map_template.py:207 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} appellations à ce point" -#: scripts/_lib/map_template.py:166 +#: scripts/_lib/map_template.py:208 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Cliquer à nouveau pour parcourir les autres" -#: scripts/_lib/map_template.py:167 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Cahier des charges (BO Agri, PDF)" -#: scripts/_lib/map_template.py:168 +#: scripts/_lib/map_template.py:210 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "" -#: scripts/_lib/map_template.py:169 +#: scripts/_lib/map_template.py:211 msgid "homologué" msgstr "homologué" -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:212 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:171 +#: scripts/_lib/map_template.py:213 msgid "Texte officiel INAO (show_texte)" msgstr "Texte officiel INAO (show_texte)" -#: scripts/_lib/map_template.py:172 +#: scripts/_lib/map_template.py:214 msgid "Fiche produit INAO" msgstr "Fiche produit INAO" -#: scripts/_lib/map_template.py:173 +#: scripts/_lib/map_template.py:215 msgid "Site officiel de l'interprofession" msgstr "Site officiel de l'interprofession" -#: scripts/_lib/map_template.py:174 +#: scripts/_lib/map_template.py:216 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Cahier des charges (EUR-Lex, document unique)" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:217 msgid "Pliego de condiciones (national, PDF)" msgstr "Pliego de condiciones (national, PDF)" -#: scripts/_lib/map_template.py:176 +#: scripts/_lib/map_template.py:218 msgid "variétés ajoutées" msgstr "variétés ajoutées" -#: scripts/_lib/map_template.py:177 +#: scripts/_lib/map_template.py:219 msgid "Cahier des charges national (PDF)" msgstr "Cahier des charges national (PDF)" -#: scripts/_lib/map_template.py:178 +#: scripts/_lib/map_template.py:220 msgid "Spécification du produit (IGP, PDF)" msgstr "Spécification du produit (IGP, PDF)" -#: scripts/_lib/map_template.py:179 +#: scripts/_lib/map_template.py:221 msgid "Registre régional des cépages (PDF)" msgstr "Registre régional des cépages (PDF)" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:222 msgid "Registre eAmbrosia (UE)" msgstr "Registre eAmbrosia (UE)" -#: scripts/_lib/map_template.py:181 +#: scripts/_lib/map_template.py:223 msgid "Numéro de dossier" msgstr "Numéro de dossier" -#: scripts/_lib/map_template.py:182 +#: scripts/_lib/map_template.py:224 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Règlement cantonal sur la vigne et le vin" -#: scripts/_lib/map_template.py:183 +#: scripts/_lib/map_template.py:225 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Répertoire suisse des AOC (OFAG/BLW)" -#: scripts/_lib/map_template.py:184 +#: scripts/_lib/map_template.py:226 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Cahier des charges (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:185 +#: scripts/_lib/map_template.py:227 msgid "Registre des IG du Royaume-Uni" msgstr "Registre des IG du Royaume-Uni" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:228 msgid "Légende couleurs" msgstr "Légende couleurs" -#: scripts/_lib/map_template.py:187 +#: scripts/_lib/map_template.py:229 msgid "Bassin viticole" msgstr "Bassin viticole" -#: scripts/_lib/map_template.py:188 +#: scripts/_lib/map_template.py:230 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Plus l'aire est petite, plus la teinte est dense." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:232 msgid "principal — variété de la cuvée" msgstr "principal — variété de la cuvée" -#: scripts/_lib/map_template.py:191 +#: scripts/_lib/map_template.py:233 msgid "accessoire — assemblage limité" msgstr "accessoire — assemblage limité" -#: scripts/_lib/map_template.py:192 +#: scripts/_lib/map_template.py:234 msgid "intérêt — observation/conservation" msgstr "intérêt — observation/conservation" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:236 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" " dans le caderno de especificações." msgstr "" -#: scripts/_lib/map_template.py:198 +#: scripts/_lib/map_template.py:240 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " "catalogue VIVC le recense comme synonyme du Trebbiano Toscano." msgstr "" -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:245 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "" -#: scripts/_lib/map_template.py:205 +#: scripts/_lib/map_template.py:247 msgid "aidez-nous à le trouver" msgstr "aidez-nous à le trouver" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:251 msgid "Annulée" msgstr "Annulée" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:253 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -585,142 +640,142 @@ msgstr "" "bouteilles, étiquettes et supports de formation plus anciens peuvent " "encore la mentionner ; elle est conservée ici à titre de référence." -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Acte national : {act}." msgstr "Acte national : {act}." -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "Elle est remplacée par {successor}." -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:259 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Enregistrement annulé le {date}" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:265 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:268 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:269 #, fuzzy msgid "le registre eAmbrosia de l'UE" msgstr "Registre eAmbrosia (UE)" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:270 #, python-brace-format msgid "le canton de {canton}" msgstr "" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:271 msgid "(français)" msgstr "(français)" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:272 msgid "Texte source en français" msgstr "Texte source en français" -#: scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:273 msgid "(español)" msgstr "(español)" -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:274 msgid "Texte source en espagnol" msgstr "Texte source en espagnol" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:275 msgid "(português)" msgstr "" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:276 msgid "Texte source en portugais" msgstr "Texte source en portugais" -#: scripts/_lib/map_template.py:235 +#: scripts/_lib/map_template.py:277 msgid "Filtres" msgstr "Filtres" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:278 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Traduit de {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:237 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en anglais" msgstr "Wikipédia en anglais" -#: scripts/_lib/map_template.py:238 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en français" msgstr "Wikipédia en français" -#: scripts/_lib/map_template.py:239 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en espagnol" msgstr "Wikipédia en espagnol" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en néerlandais" msgstr "Wikipédia en néerlandais" -#: scripts/_lib/map_template.py:241 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en portugais" msgstr "Wikipédia en portugais" -#: scripts/_lib/map_template.py:242 +#: scripts/_lib/map_template.py:284 msgid "Wikipédia en croate" msgstr "Wikipédia en croate" -#: scripts/_lib/map_template.py:243 +#: scripts/_lib/map_template.py:285 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "" -#: scripts/_lib/map_template.py:244 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "VIVC #{id}" msgstr "" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:287 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Traduction automatique depuis {source}" -#: scripts/_lib/map_template.py:246 +#: scripts/_lib/map_template.py:288 msgid "le cahier des charges" msgstr "le cahier des charges" -#: scripts/_lib/map_template.py:247 scripts/_lib/map_template.py:249 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le pliego de condiciones" msgstr "le pliego de condiciones" -#: scripts/_lib/map_template.py:248 scripts/_lib/map_template.py:250 +#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 msgid "le caderno de especificações" msgstr "" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:293 msgid "Dénomination géographique complémentaire de" msgstr "Dénomination géographique complémentaire de" -#: scripts/_lib/map_template.py:252 +#: scripts/_lib/map_template.py:294 msgid "À propos" msgstr "À propos" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:295 msgid "À propos d'Open Wine Map" msgstr "À propos d'Open Wine Map" -#: scripts/_lib/map_template.py:255 +#: scripts/_lib/map_template.py:297 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -732,7 +787,7 @@ msgstr "" "et IGP, les régulateurs nationaux, le répertoire fédéral suisse des AOC " "cantonales et le registre britannique des indications géographiques." -#: scripts/_lib/map_template.py:262 +#: scripts/_lib/map_template.py:304 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -754,12 +809,12 @@ msgstr "" "automatiquement. Chaque élément porte sa propre ligne d'attribution dans " "le panneau." -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:313 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Réalisé avec ♡ par {devloed}." -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:315 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -784,12 +839,23 @@ msgstr "" "et al.). Tout extrait Wikipedia est signalé sur place. Détails et " "licences dans le {readme}." -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:326 +#, python-brace-format +msgid "" +"Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" +" dotées d'une délimitation parcellaire INAO sont dessinées comme une " +"silhouette de leur vignoble, généralisée à {radius} m (fermeture " +"morphologique des parcelles) ; à partir du zoom {zoom}, ce sont les " +"parcelles elles-mêmes. Les autres appellations gardent le même contour à " +"tous les zooms." +msgstr "" + +#: scripts/_lib/map_template.py:333 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Suggestions et pull requests bienvenues sur {github}." -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:335 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -806,20 +872,20 @@ msgstr "" "adresse IP conservée, pas de suivi entre sites ni entre appareils — d'où " "l'absence de bandeau de consentement." -#: scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:342 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Suivez les mises à jour sur {bluesky}." -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:343 msgid "e-mail" msgstr "e-mail" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:344 msgid "E-mail copié dans le presse-papiers" msgstr "E-mail copié dans le presse-papiers" -#: scripts/_lib/map_template.py:296 +#: scripts/_lib/map_template.py:346 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -828,48 +894,48 @@ msgstr "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " "depuis la fiche de l'appellation, ou par {email}." -#: scripts/_lib/map_template.py:302 +#: scripts/_lib/map_template.py:352 msgid "Signaler une erreur" msgstr "Signaler une erreur" -#: scripts/_lib/map_template.py:303 +#: scripts/_lib/map_template.py:353 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "Un clic nous dit quoi vérifier — sans compte." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:354 msgid "Aire / contour" msgstr "Aire / contour" -#: scripts/_lib/map_template.py:307 +#: scripts/_lib/map_template.py:357 msgid "Nom / type" msgstr "Nom / type" -#: scripts/_lib/map_template.py:309 +#: scripts/_lib/map_template.py:359 msgid "Autre" msgstr "Autre" -#: scripts/_lib/map_template.py:310 +#: scripts/_lib/map_template.py:360 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Merci, c'est noté : {aspect}." -#: scripts/_lib/map_template.py:312 +#: scripts/_lib/map_template.py:362 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Précisez si vous voulez (facultatif, sans données personnelles)" -#: scripts/_lib/map_template.py:314 +#: scripts/_lib/map_template.py:364 msgid "Envoyer" msgstr "Envoyer" -#: scripts/_lib/map_template.py:317 +#: scripts/_lib/map_template.py:367 msgid "Géométrie" msgstr "Géométrie" -#: scripts/_lib/map_template.py:318 +#: scripts/_lib/map_template.py:368 msgid "Envoyé, merci !" msgstr "Envoyé, merci !" -#: scripts/_lib/map_template.py:320 +#: scripts/_lib/map_template.py:370 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " @@ -878,7 +944,7 @@ msgstr "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." -#: scripts/_lib/map_template.py:324 +#: scripts/_lib/map_template.py:374 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -891,15 +957,15 @@ msgstr "" "supplémentaires affineront la qualité des données et étendront la " "couverture au-delà de l'UE, de la Suisse et du Royaume-Uni." -#: scripts/_lib/map_template.py:329 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations" msgstr "Toutes les appellations" -#: scripts/_lib/map_template.py:330 +#: scripts/_lib/map_template.py:380 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "Toutes les appellations viticoles — Open Wine Map" -#: scripts/_lib/map_template.py:332 +#: scripts/_lib/map_template.py:382 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -910,7 +976,7 @@ msgstr "" "classées par pays : AOP et IGP de l'UE avec leurs termes traditionnels " "(AOC, DOCG, DOQ…), AOC suisses et IG britanniques." -#: scripts/_lib/map_template.py:337 +#: scripts/_lib/map_template.py:387 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " @@ -919,185 +985,185 @@ msgstr "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " "{map_link}." -#: scripts/_lib/map_template.py:340 +#: scripts/_lib/map_template.py:390 msgid "carte interactive" msgstr "carte interactive" -#: scripts/_lib/map_template.py:341 +#: scripts/_lib/map_template.py:391 msgid "Liste des appellations par pays" msgstr "Liste des appellations par pays" -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:392 msgid "Appellation parente" msgstr "Appellation parente" -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:393 msgid "Dénominations rattachées" msgstr "Dénominations rattachées" -#: scripts/_lib/map_template.py:345 +#: scripts/_lib/map_template.py:395 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Parcourir la liste complète des appellations : {browse_link}." -#: scripts/_lib/map_template.py:347 +#: scripts/_lib/map_template.py:397 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Données mises à jour le {date}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:412 msgid "BOURGOGNE" msgstr "Bourgogne" -#: scripts/_lib/map_template.py:363 +#: scripts/_lib/map_template.py:413 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:414 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:365 +#: scripts/_lib/map_template.py:415 msgid "SAVOIE" msgstr "Savoie" -#: scripts/_lib/map_template.py:366 +#: scripts/_lib/map_template.py:416 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:417 msgid "ALSACE ET EST" msgstr "Alsace et Est" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:418 msgid "VAL DE LOIRE" msgstr "Val de Loire" -#: scripts/_lib/map_template.py:369 +#: scripts/_lib/map_template.py:419 msgid "SUD-OUEST" msgstr "Sud-Ouest" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:420 msgid "VALLEE DU RHÔNE" msgstr "Vallée du Rhône" -#: scripts/_lib/map_template.py:371 +#: scripts/_lib/map_template.py:421 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Roussillon" -#: scripts/_lib/map_template.py:372 +#: scripts/_lib/map_template.py:422 msgid "TOULOUSE-PYRENEES" msgstr "Toulouse-Pyrénées" -#: scripts/_lib/map_template.py:373 +#: scripts/_lib/map_template.py:423 msgid "PROVENCE-CORSE" msgstr "Provence-Corse" -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:424 msgid "CHAMPAGNE" msgstr "Champagne" -#: scripts/_lib/map_template.py:375 +#: scripts/_lib/map_template.py:425 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Eaux-de-vie de cidre" -#: scripts/_lib/map_template.py:376 +#: scripts/_lib/map_template.py:426 msgid "VIN DOUX NATURELS" msgstr "Vins doux naturels" -#: scripts/_lib/map_template.py:377 +#: scripts/_lib/map_template.py:427 msgid "COGNAC" msgstr "Cognac" -#: scripts/_lib/map_template.py:378 +#: scripts/_lib/map_template.py:428 msgid "ARMAGNAC" msgstr "Armagnac" -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:429 msgid "RHUM" msgstr "Rhum" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:469 msgid "France" msgstr "France" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:470 msgid "Espagne" msgstr "Espagne" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:471 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:472 msgid "Italie" msgstr "Italie" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:473 msgid "Autriche" msgstr "Autriche" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:474 msgid "Slovénie" msgstr "Slovénie" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:475 msgid "Croatie" msgstr "Croatie" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:476 msgid "Hongrie" msgstr "Hongrie" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:477 msgid "Roumanie" msgstr "Roumanie" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:478 msgid "Bulgarie" msgstr "Bulgarie" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:479 msgid "Grèce" msgstr "Grèce" -#: scripts/_lib/map_template.py:430 +#: scripts/_lib/map_template.py:480 msgid "Allemagne" msgstr "Allemagne" -#: scripts/_lib/map_template.py:431 +#: scripts/_lib/map_template.py:481 msgid "Slovaquie" msgstr "Slovaquie" -#: scripts/_lib/map_template.py:432 +#: scripts/_lib/map_template.py:482 msgid "Suisse" msgstr "Suisse" -#: scripts/_lib/map_template.py:433 +#: scripts/_lib/map_template.py:483 msgid "Tchéquie" msgstr "Tchéquie" -#: scripts/_lib/map_template.py:434 +#: scripts/_lib/map_template.py:484 msgid "Luxembourg" msgstr "Luxembourg" -#: scripts/_lib/map_template.py:435 +#: scripts/_lib/map_template.py:485 msgid "Belgique" msgstr "Belgique" -#: scripts/_lib/map_template.py:436 +#: scripts/_lib/map_template.py:486 msgid "Pays-Bas" msgstr "Pays-Bas" -#: scripts/_lib/map_template.py:437 +#: scripts/_lib/map_template.py:487 msgid "Malte" msgstr "Malte" -#: scripts/_lib/map_template.py:438 +#: scripts/_lib/map_template.py:488 msgid "Chypre" msgstr "Chypre" -#: scripts/_lib/map_template.py:439 +#: scripts/_lib/map_template.py:489 msgid "Royaume-Uni" msgstr "Royaume-Uni" diff --git a/locale/messages.pot b/locale/messages.pot index df950b4..43d340f 100644 --- a/locale/messages.pot +++ b/locale/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-20 21:16+0200\n" +"POT-Creation-Date: 2026-09-23 08:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -127,8 +127,8 @@ msgstr "" msgid "Cépages accessoires" msgstr "" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:189 -#: scripts/_lib/map_template.py:305 +#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:355 msgid "Cépages" msgstr "" @@ -304,11 +304,11 @@ msgstr "" msgid "Variétés d'intérêt" msgstr "" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:308 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 msgid "Sources" msgstr "" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:306 +#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 msgid "Terroir" msgstr "" @@ -428,143 +428,198 @@ msgid "" "limite officielle." msgstr "" -#: scripts/_lib/map_template.py:165 +#: scripts/_lib/map_template.py:170 +#, python-brace-format +msgid "" +"Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " +"plans d'eau et terres non viticoles compris ; pas de délimitation " +"parcellaire pour cette dénomination." +msgstr "" + +#: scripts/_lib/map_template.py:175 +#, python-brace-format +msgid "" +"Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " +"partir de la délimitation parcellaire INAO — pas la délimitation elle-" +"même ; les parcelles apparaissent en zoomant." +msgstr "" + +#: scripts/_lib/map_template.py:180 +#, python-brace-format +msgid "" +"Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " +"une silhouette de son vignoble généralisée à {radius} m à partir de la " +"délimitation parcellaire INAO ; les parcelles elles-mêmes apparaissent en" +" zoomant." +msgstr "" + +#: scripts/_lib/map_template.py:186 +#, python-brace-format +msgid "Aire : zone de production officielle publiée par le géoportail ({region})." +msgstr "" + +#: scripts/_lib/map_template.py:190 +msgid "" +"Aire : zone de production officielle du MAPA (Zonas de Calidad " +"Diferenciada : Vinos)." +msgstr "" + +#: scripts/_lib/map_template.py:194 +msgid "" +"Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " +"la résolution des communes ; peut inclure des terres non viticoles." +msgstr "" + +#: scripts/_lib/map_template.py:199 +msgid "" +"Aire approchée — réunion des polygones des appellations membres (Bétard " +"2022), à la résolution des communes." +msgstr "" + +#: scripts/_lib/map_template.py:203 +msgid "" +"Aire approchée — réunion des unités administratives nommées dans le " +"document (limites Eurostat GISCO ou cadastre national), terres non " +"viticoles comprises." +msgstr "" + +#: scripts/_lib/map_template.py:207 #, python-brace-format msgid "{n} appellations à ce point" msgstr "" -#: scripts/_lib/map_template.py:166 +#: scripts/_lib/map_template.py:208 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "" -#: scripts/_lib/map_template.py:167 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (BO Agri, PDF)" msgstr "" -#: scripts/_lib/map_template.py:168 +#: scripts/_lib/map_template.py:210 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "" -#: scripts/_lib/map_template.py:169 +#: scripts/_lib/map_template.py:211 msgid "homologué" msgstr "" -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:212 msgid "JORF" msgstr "" -#: scripts/_lib/map_template.py:171 +#: scripts/_lib/map_template.py:213 msgid "Texte officiel INAO (show_texte)" msgstr "" -#: scripts/_lib/map_template.py:172 +#: scripts/_lib/map_template.py:214 msgid "Fiche produit INAO" msgstr "" -#: scripts/_lib/map_template.py:173 +#: scripts/_lib/map_template.py:215 msgid "Site officiel de l'interprofession" msgstr "" -#: scripts/_lib/map_template.py:174 +#: scripts/_lib/map_template.py:216 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:217 msgid "Pliego de condiciones (national, PDF)" msgstr "" -#: scripts/_lib/map_template.py:176 +#: scripts/_lib/map_template.py:218 msgid "variétés ajoutées" msgstr "" -#: scripts/_lib/map_template.py:177 +#: scripts/_lib/map_template.py:219 msgid "Cahier des charges national (PDF)" msgstr "" -#: scripts/_lib/map_template.py:178 +#: scripts/_lib/map_template.py:220 msgid "Spécification du produit (IGP, PDF)" msgstr "" -#: scripts/_lib/map_template.py:179 +#: scripts/_lib/map_template.py:221 msgid "Registre régional des cépages (PDF)" msgstr "" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:222 msgid "Registre eAmbrosia (UE)" msgstr "" -#: scripts/_lib/map_template.py:181 +#: scripts/_lib/map_template.py:223 msgid "Numéro de dossier" msgstr "" -#: scripts/_lib/map_template.py:182 +#: scripts/_lib/map_template.py:224 msgid "Règlement cantonal sur la vigne et le vin" msgstr "" -#: scripts/_lib/map_template.py:183 +#: scripts/_lib/map_template.py:225 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "" -#: scripts/_lib/map_template.py:184 +#: scripts/_lib/map_template.py:226 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "" -#: scripts/_lib/map_template.py:185 +#: scripts/_lib/map_template.py:227 msgid "Registre des IG du Royaume-Uni" msgstr "" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:228 msgid "Légende couleurs" msgstr "" -#: scripts/_lib/map_template.py:187 +#: scripts/_lib/map_template.py:229 msgid "Bassin viticole" msgstr "" -#: scripts/_lib/map_template.py:188 +#: scripts/_lib/map_template.py:230 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "" -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:232 msgid "principal — variété de la cuvée" msgstr "" -#: scripts/_lib/map_template.py:191 +#: scripts/_lib/map_template.py:233 msgid "accessoire — assemblage limité" msgstr "" -#: scripts/_lib/map_template.py:192 +#: scripts/_lib/map_template.py:234 msgid "intérêt — observation/conservation" msgstr "" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:236 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" " dans le caderno de especificações." msgstr "" -#: scripts/_lib/map_template.py:198 +#: scripts/_lib/map_template.py:240 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " "catalogue VIVC le recense comme synonyme du Trebbiano Toscano." msgstr "" -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:245 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "" -#: scripts/_lib/map_template.py:205 +#: scripts/_lib/map_template.py:247 msgid "aidez-nous à le trouver" msgstr "" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:251 msgid "Annulée" msgstr "" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:253 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -572,141 +627,141 @@ msgid "" "encore la mentionner ; elle est conservée ici à titre de référence." msgstr "" -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Acte national : {act}." msgstr "" -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "" -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:259 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:265 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:268 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:269 msgid "le registre eAmbrosia de l'UE" msgstr "" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:270 #, python-brace-format msgid "le canton de {canton}" msgstr "" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:271 msgid "(français)" msgstr "" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:272 msgid "Texte source en français" msgstr "" -#: scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:273 msgid "(español)" msgstr "" -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:274 msgid "Texte source en espagnol" msgstr "" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:275 msgid "(português)" msgstr "" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:276 msgid "Texte source en portugais" msgstr "" -#: scripts/_lib/map_template.py:235 +#: scripts/_lib/map_template.py:277 msgid "Filtres" msgstr "" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:278 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "" -#: scripts/_lib/map_template.py:237 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en anglais" msgstr "" -#: scripts/_lib/map_template.py:238 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en français" msgstr "" -#: scripts/_lib/map_template.py:239 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en espagnol" msgstr "" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en néerlandais" msgstr "" -#: scripts/_lib/map_template.py:241 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en portugais" msgstr "" -#: scripts/_lib/map_template.py:242 +#: scripts/_lib/map_template.py:284 msgid "Wikipédia en croate" msgstr "" -#: scripts/_lib/map_template.py:243 +#: scripts/_lib/map_template.py:285 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "" -#: scripts/_lib/map_template.py:244 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "VIVC #{id}" msgstr "" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:287 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "" -#: scripts/_lib/map_template.py:246 +#: scripts/_lib/map_template.py:288 msgid "le cahier des charges" msgstr "" -#: scripts/_lib/map_template.py:247 scripts/_lib/map_template.py:249 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le pliego de condiciones" msgstr "" -#: scripts/_lib/map_template.py:248 scripts/_lib/map_template.py:250 +#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 msgid "le caderno de especificações" msgstr "" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:293 msgid "Dénomination géographique complémentaire de" msgstr "" -#: scripts/_lib/map_template.py:252 +#: scripts/_lib/map_template.py:294 msgid "À propos" msgstr "" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:295 msgid "À propos d'Open Wine Map" msgstr "" -#: scripts/_lib/map_template.py:255 +#: scripts/_lib/map_template.py:297 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -714,7 +769,7 @@ msgid "" "cantonales et le registre britannique des indications géographiques." msgstr "" -#: scripts/_lib/map_template.py:262 +#: scripts/_lib/map_template.py:304 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -727,12 +782,12 @@ msgid "" "le panneau." msgstr "" -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:313 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "" -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:315 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -747,12 +802,23 @@ msgid "" "licences dans le {readme}." msgstr "" -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:326 +#, python-brace-format +msgid "" +"Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" +" dotées d'une délimitation parcellaire INAO sont dessinées comme une " +"silhouette de leur vignoble, généralisée à {radius} m (fermeture " +"morphologique des parcelles) ; à partir du zoom {zoom}, ce sont les " +"parcelles elles-mêmes. Les autres appellations gardent le même contour à " +"tous les zooms." +msgstr "" + +#: scripts/_lib/map_template.py:333 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "" -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:335 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -763,75 +829,75 @@ msgid "" "l'absence de bandeau de consentement." msgstr "" -#: scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:342 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "" -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:343 msgid "e-mail" msgstr "" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:344 msgid "E-mail copié dans le presse-papiers" msgstr "" -#: scripts/_lib/map_template.py:296 +#: scripts/_lib/map_template.py:346 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " "depuis la fiche de l'appellation, ou par {email}." msgstr "" -#: scripts/_lib/map_template.py:302 +#: scripts/_lib/map_template.py:352 msgid "Signaler une erreur" msgstr "" -#: scripts/_lib/map_template.py:303 +#: scripts/_lib/map_template.py:353 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "" -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:354 msgid "Aire / contour" msgstr "" -#: scripts/_lib/map_template.py:307 +#: scripts/_lib/map_template.py:357 msgid "Nom / type" msgstr "" -#: scripts/_lib/map_template.py:309 +#: scripts/_lib/map_template.py:359 msgid "Autre" msgstr "" -#: scripts/_lib/map_template.py:310 +#: scripts/_lib/map_template.py:360 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "" -#: scripts/_lib/map_template.py:312 +#: scripts/_lib/map_template.py:362 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "" -#: scripts/_lib/map_template.py:314 +#: scripts/_lib/map_template.py:364 msgid "Envoyer" msgstr "" -#: scripts/_lib/map_template.py:317 +#: scripts/_lib/map_template.py:367 msgid "Géométrie" msgstr "" -#: scripts/_lib/map_template.py:318 +#: scripts/_lib/map_template.py:368 msgid "Envoyé, merci !" msgstr "" -#: scripts/_lib/map_template.py:320 +#: scripts/_lib/map_template.py:370 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." msgstr "" -#: scripts/_lib/map_template.py:324 +#: scripts/_lib/map_template.py:374 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -840,15 +906,15 @@ msgid "" "couverture au-delà de l'UE, de la Suisse et du Royaume-Uni." msgstr "" -#: scripts/_lib/map_template.py:329 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations" msgstr "" -#: scripts/_lib/map_template.py:330 +#: scripts/_lib/map_template.py:380 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "" -#: scripts/_lib/map_template.py:332 +#: scripts/_lib/map_template.py:382 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -856,192 +922,192 @@ msgid "" "(AOC, DOCG, DOQ…), AOC suisses et IG britanniques." msgstr "" -#: scripts/_lib/map_template.py:337 +#: scripts/_lib/map_template.py:387 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " "{map_link}." msgstr "" -#: scripts/_lib/map_template.py:340 +#: scripts/_lib/map_template.py:390 msgid "carte interactive" msgstr "" -#: scripts/_lib/map_template.py:341 +#: scripts/_lib/map_template.py:391 msgid "Liste des appellations par pays" msgstr "" -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:392 msgid "Appellation parente" msgstr "" -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:393 msgid "Dénominations rattachées" msgstr "" -#: scripts/_lib/map_template.py:345 +#: scripts/_lib/map_template.py:395 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "" -#: scripts/_lib/map_template.py:347 +#: scripts/_lib/map_template.py:397 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "" -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:412 msgid "BOURGOGNE" msgstr "" -#: scripts/_lib/map_template.py:363 +#: scripts/_lib/map_template.py:413 msgid "BEAUJOLAIS" msgstr "" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:414 msgid "JURA" msgstr "" -#: scripts/_lib/map_template.py:365 +#: scripts/_lib/map_template.py:415 msgid "SAVOIE" msgstr "" -#: scripts/_lib/map_template.py:366 +#: scripts/_lib/map_template.py:416 msgid "BUGEY" msgstr "" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:417 msgid "ALSACE ET EST" msgstr "" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:418 msgid "VAL DE LOIRE" msgstr "" -#: scripts/_lib/map_template.py:369 +#: scripts/_lib/map_template.py:419 msgid "SUD-OUEST" msgstr "" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:420 msgid "VALLEE DU RHÔNE" msgstr "" -#: scripts/_lib/map_template.py:371 +#: scripts/_lib/map_template.py:421 msgid "LANGUEDOC-ROUSSILLON" msgstr "" -#: scripts/_lib/map_template.py:372 +#: scripts/_lib/map_template.py:422 msgid "TOULOUSE-PYRENEES" msgstr "" -#: scripts/_lib/map_template.py:373 +#: scripts/_lib/map_template.py:423 msgid "PROVENCE-CORSE" msgstr "" -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:424 msgid "CHAMPAGNE" msgstr "" -#: scripts/_lib/map_template.py:375 +#: scripts/_lib/map_template.py:425 msgid "EAUX-DE-VIE DE CIDRE" msgstr "" -#: scripts/_lib/map_template.py:376 +#: scripts/_lib/map_template.py:426 msgid "VIN DOUX NATURELS" msgstr "" -#: scripts/_lib/map_template.py:377 +#: scripts/_lib/map_template.py:427 msgid "COGNAC" msgstr "" -#: scripts/_lib/map_template.py:378 +#: scripts/_lib/map_template.py:428 msgid "ARMAGNAC" msgstr "" -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:429 msgid "RHUM" msgstr "" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:469 msgid "France" msgstr "" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:470 msgid "Espagne" msgstr "" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:471 msgid "Portugal" msgstr "" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:472 msgid "Italie" msgstr "" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:473 msgid "Autriche" msgstr "" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:474 msgid "Slovénie" msgstr "" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:475 msgid "Croatie" msgstr "" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:476 msgid "Hongrie" msgstr "" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:477 msgid "Roumanie" msgstr "" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:478 msgid "Bulgarie" msgstr "" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:479 msgid "Grèce" msgstr "" -#: scripts/_lib/map_template.py:430 +#: scripts/_lib/map_template.py:480 msgid "Allemagne" msgstr "" -#: scripts/_lib/map_template.py:431 +#: scripts/_lib/map_template.py:481 msgid "Slovaquie" msgstr "" -#: scripts/_lib/map_template.py:432 +#: scripts/_lib/map_template.py:482 msgid "Suisse" msgstr "" -#: scripts/_lib/map_template.py:433 +#: scripts/_lib/map_template.py:483 msgid "Tchéquie" msgstr "" -#: scripts/_lib/map_template.py:434 +#: scripts/_lib/map_template.py:484 msgid "Luxembourg" msgstr "" -#: scripts/_lib/map_template.py:435 +#: scripts/_lib/map_template.py:485 msgid "Belgique" msgstr "" -#: scripts/_lib/map_template.py:436 +#: scripts/_lib/map_template.py:486 msgid "Pays-Bas" msgstr "" -#: scripts/_lib/map_template.py:437 +#: scripts/_lib/map_template.py:487 msgid "Malte" msgstr "" -#: scripts/_lib/map_template.py:438 +#: scripts/_lib/map_template.py:488 msgid "Chypre" msgstr "" -#: scripts/_lib/map_template.py:439 +#: scripts/_lib/map_template.py:489 msgid "Royaume-Uni" msgstr "" diff --git a/locale/nl/LC_MESSAGES/messages.po b/locale/nl/LC_MESSAGES/messages.po index bee3bd6..7614c1b 100644 --- a/locale/nl/LC_MESSAGES/messages.po +++ b/locale/nl/LC_MESSAGES/messages.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-20 21:16+0200\n" +"POT-Creation-Date: 2026-09-23 08:12+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: nl\n" @@ -127,8 +127,8 @@ msgstr "Hoofddruivenrassen" msgid "Cépages accessoires" msgstr "Aanvullende druivenrassen" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:189 -#: scripts/_lib/map_template.py:305 +#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:355 msgid "Cépages" msgstr "Druivenrassen" @@ -304,11 +304,11 @@ msgstr "Stijlen" msgid "Variétés d'intérêt" msgstr "Variëteiten van belang" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:308 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 msgid "Sources" msgstr "Bronnen" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:306 +#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 msgid "Terroir" msgstr "Terroir" @@ -445,116 +445,192 @@ msgstr "" "het plan van het afgebakende gebied bij het productdossier; dit is geen " "officiële grens." -#: scripts/_lib/map_template.py:165 +#: scripts/_lib/map_template.py:170 +#, python-brace-format +msgid "" +"Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " +"plans d'eau et terres non viticoles compris ; pas de délimitation " +"parcellaire pour cette dénomination." +msgstr "" +"Benaderd gebied — de vereniging van de {n} gemeente(n) van het " +"afgebakende gebied (INAO), inclusief waterpartijen en niet-wijnbouwgrond;" +" voor deze benaming bestaat geen perceelsgewijze afbakening." + +#: scripts/_lib/map_template.py:175 +#, python-brace-format +msgid "" +"Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " +"partir de la délimitation parcellaire INAO — pas la délimitation elle-" +"même ; les parcelles apparaissent en zoomant." +msgstr "" +"Overzicht: een silhouet van de wijngaard, veralgemeend op {radius} m op " +"basis van de perceelsafbakening van het INAO — niet de afbakening zelf; " +"de percelen verschijnen bij inzoomen." + +#: scripts/_lib/map_template.py:180 +#, python-brace-format +msgid "" +"Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " +"une silhouette de son vignoble généralisée à {radius} m à partir de la " +"délimitation parcellaire INAO ; les parcelles elles-mêmes apparaissent en" +" zoomant." +msgstr "" +"Op de kaart wordt deze appellation bij overzichtszooms getekend als een " +"silhouet van haar wijngaard, veralgemeend op {radius} m op basis van de " +"perceelsafbakening van het INAO; de percelen zelf verschijnen bij " +"inzoomen." + +#: scripts/_lib/map_template.py:186 +#, python-brace-format +msgid "Aire : zone de production officielle publiée par le géoportail ({region})." +msgstr "" +"Gebied: de officiële productiezone gepubliceerd door het regionale " +"geoportaal ({region})." + +#: scripts/_lib/map_template.py:190 +msgid "" +"Aire : zone de production officielle du MAPA (Zonas de Calidad " +"Diferenciada : Vinos)." +msgstr "" +"Gebied: de officiële productiezone van het MAPA (Zonas de Calidad " +"Diferenciada: Vinos)." + +#: scripts/_lib/map_template.py:194 +msgid "" +"Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " +"la résolution des communes ; peut inclure des terres non viticoles." +msgstr "" +"Benaderd gebied — polygoon uit de dataset Bétard 2022 (EU-BOB's), op " +"gemeenteniveau; kan niet-wijnbouwgrond omvatten." + +#: scripts/_lib/map_template.py:199 +msgid "" +"Aire approchée — réunion des polygones des appellations membres (Bétard " +"2022), à la résolution des communes." +msgstr "" +"Benaderd gebied — de vereniging van de polygonen van de deelnemende " +"appellations (Bétard 2022), op gemeenteniveau." + +#: scripts/_lib/map_template.py:203 +msgid "" +"Aire approchée — réunion des unités administratives nommées dans le " +"document (limites Eurostat GISCO ou cadastre national), terres non " +"viticoles comprises." +msgstr "" +"Benaderd gebied — de vereniging van de in het document genoemde " +"bestuurlijke eenheden (grenzen van Eurostat GISCO of het nationale " +"kadaster), inclusief niet-wijnbouwgrond." + +#: scripts/_lib/map_template.py:207 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} appellaties op dit punt" -#: scripts/_lib/map_template.py:166 +#: scripts/_lib/map_template.py:208 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Klik opnieuw om door de andere te bladeren" -#: scripts/_lib/map_template.py:167 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Productdossier (BO Agri, PDF)" -#: scripts/_lib/map_template.py:168 +#: scripts/_lib/map_template.py:210 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "Productdossier (EU GI-register, PDF)" -#: scripts/_lib/map_template.py:169 +#: scripts/_lib/map_template.py:211 msgid "homologué" msgstr "goedgekeurd" -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:212 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:171 +#: scripts/_lib/map_template.py:213 msgid "Texte officiel INAO (show_texte)" msgstr "Officiële INAO-tekst (show_texte)" -#: scripts/_lib/map_template.py:172 +#: scripts/_lib/map_template.py:214 msgid "Fiche produit INAO" msgstr "INAO-productfiche" -#: scripts/_lib/map_template.py:173 +#: scripts/_lib/map_template.py:215 msgid "Site officiel de l'interprofession" msgstr "Officiële site van de wijnorganisatie" -#: scripts/_lib/map_template.py:174 +#: scripts/_lib/map_template.py:216 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Productspecificatie (EUR-Lex, enig document)" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:217 msgid "Pliego de condiciones (national, PDF)" msgstr "Nationaal pliego de condiciones (PDF)" -#: scripts/_lib/map_template.py:176 +#: scripts/_lib/map_template.py:218 msgid "variétés ajoutées" msgstr "rassen toegevoegd" -#: scripts/_lib/map_template.py:177 +#: scripts/_lib/map_template.py:219 msgid "Cahier des charges national (PDF)" msgstr "Nationaal productdossier (PDF)" -#: scripts/_lib/map_template.py:178 +#: scripts/_lib/map_template.py:220 msgid "Spécification du produit (IGP, PDF)" msgstr "Productdossier (BGA, PDF)" -#: scripts/_lib/map_template.py:179 +#: scripts/_lib/map_template.py:221 msgid "Registre régional des cépages (PDF)" msgstr "Regionaal druivenrasregister (PDF)" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:222 msgid "Registre eAmbrosia (UE)" msgstr "eAmbrosia-register (EU)" -#: scripts/_lib/map_template.py:181 +#: scripts/_lib/map_template.py:223 msgid "Numéro de dossier" msgstr "Dossiernummer" -#: scripts/_lib/map_template.py:182 +#: scripts/_lib/map_template.py:224 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Kantonaal wijnreglement" -#: scripts/_lib/map_template.py:183 +#: scripts/_lib/map_template.py:225 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Zwitsers AOC-register (OFAG/BLW)" -#: scripts/_lib/map_template.py:184 +#: scripts/_lib/map_template.py:226 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Productdossier (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:185 +#: scripts/_lib/map_template.py:227 msgid "Registre des IG du Royaume-Uni" msgstr "Register van geografische aanduidingen (VK)" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:228 msgid "Légende couleurs" msgstr "Legende" -#: scripts/_lib/map_template.py:187 +#: scripts/_lib/map_template.py:229 msgid "Bassin viticole" msgstr "Wijnstreek" -#: scripts/_lib/map_template.py:188 +#: scripts/_lib/map_template.py:230 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Hoe kleiner het gebied, hoe dichter de tint." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:232 msgid "principal — variété de la cuvée" msgstr "hoofdras — variëteit van de cuvée" -#: scripts/_lib/map_template.py:191 +#: scripts/_lib/map_template.py:233 msgid "accessoire — assemblage limité" msgstr "bijras — beperkte assemblage" -#: scripts/_lib/map_template.py:192 +#: scripts/_lib/map_template.py:234 msgid "intérêt — observation/conservation" msgstr "interesse — observatie/behoud" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:236 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" @@ -564,7 +640,7 @@ msgstr "" "en bijrassen — alle toegelaten druivenrassen worden samen vermeld in de " "caderno de especificações." -#: scripts/_lib/map_template.py:198 +#: scripts/_lib/map_template.py:240 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " @@ -575,20 +651,20 @@ msgstr "" "Metauro; de VIVC-catalogus vermeldt het als synoniem van Trebbiano " "Toscano." -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:245 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "Open Wine Map heeft nog geen {doc} gevonden voor deze benaming." -#: scripts/_lib/map_template.py:205 +#: scripts/_lib/map_template.py:247 msgid "aidez-nous à le trouver" msgstr "help ons het te vinden" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:251 msgid "Annulée" msgstr "Geannuleerd" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:253 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -599,141 +675,141 @@ msgstr "" "flessen, etiketten en cursusmateriaal kunnen de naam nog vermelden; ze " "blijft hier ter referentie staan." -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Acte national : {act}." msgstr "Nationaal besluit: {act}." -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "Ze is opgevolgd door {successor}." -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:259 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Registratie geannuleerd op {date}" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:265 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "Afgebakend door {regulator} in zijn {doc}, dat {grapes} toestaat." -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "Afgebakend door {regulator} in zijn {doc}{extra}." -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:268 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "{names} en {n} andere druivenrassen" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:269 msgid "le registre eAmbrosia de l'UE" msgstr "het eAmbrosia-register van de EU" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:270 #, python-brace-format msgid "le canton de {canton}" msgstr "het kanton {canton}" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:271 msgid "(français)" msgstr "(bron: Frans)" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:272 msgid "Texte source en français" msgstr "Brontekst in het Frans" -#: scripts/_lib/map_template.py:231 +#: scripts/_lib/map_template.py:273 msgid "(español)" msgstr "(bron: Spaans)" -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:274 msgid "Texte source en espagnol" msgstr "Brontekst in het Spaans" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:275 msgid "(português)" msgstr "(bron: Portugees)" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:276 msgid "Texte source en portugais" msgstr "Brontekst in het Portugees" -#: scripts/_lib/map_template.py:235 +#: scripts/_lib/map_template.py:277 msgid "Filtres" msgstr "Filters" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:278 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Vertaald uit {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:237 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en anglais" msgstr "Engelstalige Wikipedia" -#: scripts/_lib/map_template.py:238 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en français" msgstr "Franstalige Wikipedia" -#: scripts/_lib/map_template.py:239 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en espagnol" msgstr "Spaanstalige Wikipedia" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en néerlandais" msgstr "Nederlandstalige Wikipedia" -#: scripts/_lib/map_template.py:241 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en portugais" msgstr "Portugeestalige Wikipedia" -#: scripts/_lib/map_template.py:242 +#: scripts/_lib/map_template.py:284 msgid "Wikipédia en croate" msgstr "Kroatische Wikipedia" -#: scripts/_lib/map_template.py:243 +#: scripts/_lib/map_template.py:285 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "Vitis International Variety Catalogue (Julius Kühn-Institut)" -#: scripts/_lib/map_template.py:244 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "VIVC #{id}" msgstr "VIVC #{id}" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:287 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Automatisch vertaald uit {source}" -#: scripts/_lib/map_template.py:246 +#: scripts/_lib/map_template.py:288 msgid "le cahier des charges" msgstr "het cahier des charges" -#: scripts/_lib/map_template.py:247 scripts/_lib/map_template.py:249 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le pliego de condiciones" msgstr "de productspecificatie" -#: scripts/_lib/map_template.py:248 scripts/_lib/map_template.py:250 +#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 msgid "le caderno de especificações" msgstr "de caderno de especificações" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:293 msgid "Dénomination géographique complémentaire de" msgstr "Deelgebied van" -#: scripts/_lib/map_template.py:252 +#: scripts/_lib/map_template.py:294 msgid "À propos" msgstr "Over" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:295 msgid "À propos d'Open Wine Map" msgstr "Over Open Wine Map" -#: scripts/_lib/map_template.py:255 +#: scripts/_lib/map_template.py:297 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -745,7 +821,7 @@ msgstr "" " het Zwitserse federale repertorium van kantonnale AOC's en het Britse " "register van geografische aanduidingen." -#: scripts/_lib/map_template.py:262 +#: scripts/_lib/map_template.py:304 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -766,12 +842,12 @@ msgstr "" "menselijke vertaler vertaald, op een klein machinaal vertaald restant na." " Elk onderdeel heeft zijn eigen bronvermelding in het paneel." -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:313 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Met ♡ gemaakt door {devloed}." -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:315 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -795,12 +871,28 @@ msgstr "" "druivennamen en rasnummers (citaat: Röckel et al.). Elk Wikipedia-" "fragment wordt ter plaatse vermeld. Details en licenties in de {readme}." -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:326 +#, python-brace-format +msgid "" +"Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" +" dotées d'une délimitation parcellaire INAO sont dessinées comme une " +"silhouette de leur vignoble, généralisée à {radius} m (fermeture " +"morphologique des parcelles) ; à partir du zoom {zoom}, ce sont les " +"parcelles elles-mêmes. Les autres appellations gardent le même contour à " +"tous les zooms." +msgstr "" +"Detailniveau: onder zoomniveau {zoom} worden Franse appellations met een " +"perceelsafbakening van het INAO getekend als een silhouet van hun " +"wijngaard, veralgemeend op {radius} m (een morfologische sluiting van de " +"percelen); vanaf zoomniveau {zoom} worden de percelen zelf getekend. Alle" +" andere appellations behouden dezelfde omtrek op elk zoomniveau." + +#: scripts/_lib/map_template.py:333 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Issues en pull requests welkom op {github}." -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:335 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -817,20 +909,20 @@ msgstr "" "adressen, geen tracking over sites of apparaten heen — daarom is er geen " "cookiebanner." -#: scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:342 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Volg de updates op {bluesky}." -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:343 msgid "e-mail" msgstr "e-mail" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:344 msgid "E-mail copié dans le presse-papiers" msgstr "E-mail gekopieerd naar klembord" -#: scripts/_lib/map_template.py:296 +#: scripts/_lib/map_template.py:346 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -839,55 +931,55 @@ msgstr "" "Automatisch gegenereerde kaart — fouten zijn mogelijk. Meld ze op de " "fiche van de appellatie, of per {email}." -#: scripts/_lib/map_template.py:302 +#: scripts/_lib/map_template.py:352 msgid "Signaler une erreur" msgstr "Een fout melden" -#: scripts/_lib/map_template.py:303 +#: scripts/_lib/map_template.py:353 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "Eén tik vertelt ons wat we moeten nakijken — geen account nodig." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:354 msgid "Aire / contour" msgstr "Gebied / grens" -#: scripts/_lib/map_template.py:307 +#: scripts/_lib/map_template.py:357 msgid "Nom / type" msgstr "Naam / type" -#: scripts/_lib/map_template.py:309 +#: scripts/_lib/map_template.py:359 msgid "Autre" msgstr "Anders" -#: scripts/_lib/map_template.py:310 +#: scripts/_lib/map_template.py:360 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Bedankt, genoteerd: {aspect}." -#: scripts/_lib/map_template.py:312 +#: scripts/_lib/map_template.py:362 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Licht toe als je wilt (optioneel, geen persoonsgegevens)" -#: scripts/_lib/map_template.py:314 +#: scripts/_lib/map_template.py:364 msgid "Envoyer" msgstr "Verzenden" -#: scripts/_lib/map_template.py:317 +#: scripts/_lib/map_template.py:367 msgid "Géométrie" msgstr "Geometrie" -#: scripts/_lib/map_template.py:318 +#: scripts/_lib/map_template.py:368 msgid "Envoyé, merci !" msgstr "Verzonden, bedankt!" -#: scripts/_lib/map_template.py:320 +#: scripts/_lib/map_template.py:370 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." msgstr "{aspect} — je browser blokkeert onze meettool; stuur liever een {email}." -#: scripts/_lib/map_template.py:324 +#: scripts/_lib/map_template.py:374 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -900,15 +992,15 @@ msgstr "" "verfijnen de datakwaliteit en breiden de dekking uit tot buiten de EU, " "Zwitserland en het Verenigd Koninkrijk." -#: scripts/_lib/map_template.py:329 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations" msgstr "Alle appellaties" -#: scripts/_lib/map_template.py:330 +#: scripts/_lib/map_template.py:380 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "Alle wijnappellaties — Open Wine Map" -#: scripts/_lib/map_template.py:332 +#: scripts/_lib/map_template.py:382 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -919,7 +1011,7 @@ msgstr "" "BGA's van de EU met hun traditionele aanduidingen (AOC, DOCG, DOQ…), " "Zwitserse AOC's en Britse GA's." -#: scripts/_lib/map_template.py:337 +#: scripts/_lib/map_template.py:387 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " @@ -928,185 +1020,185 @@ msgstr "" "De {n} onderstaande appellaties zijn gegroepeerd per land. Terug naar de " "{map_link}." -#: scripts/_lib/map_template.py:340 +#: scripts/_lib/map_template.py:390 msgid "carte interactive" msgstr "interactieve kaart" -#: scripts/_lib/map_template.py:341 +#: scripts/_lib/map_template.py:391 msgid "Liste des appellations par pays" msgstr "Lijst van appellaties per land" -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:392 msgid "Appellation parente" msgstr "Bovenliggende appellatie" -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:393 msgid "Dénominations rattachées" msgstr "Gekoppelde benamingen" -#: scripts/_lib/map_template.py:345 +#: scripts/_lib/map_template.py:395 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Blader door de volledige lijst van appellaties: {browse_link}." -#: scripts/_lib/map_template.py:347 +#: scripts/_lib/map_template.py:397 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Gegevens bijgewerkt op {date}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:412 msgid "BOURGOGNE" msgstr "Bourgogne" -#: scripts/_lib/map_template.py:363 +#: scripts/_lib/map_template.py:413 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:414 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:365 +#: scripts/_lib/map_template.py:415 msgid "SAVOIE" msgstr "Savoie" -#: scripts/_lib/map_template.py:366 +#: scripts/_lib/map_template.py:416 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:417 msgid "ALSACE ET EST" msgstr "Elzas en Oost" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:418 msgid "VAL DE LOIRE" msgstr "Loirestreek" -#: scripts/_lib/map_template.py:369 +#: scripts/_lib/map_template.py:419 msgid "SUD-OUEST" msgstr "Zuidwest-Frankrijk" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:420 msgid "VALLEE DU RHÔNE" msgstr "Rhônestreek" -#: scripts/_lib/map_template.py:371 +#: scripts/_lib/map_template.py:421 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Roussillon" -#: scripts/_lib/map_template.py:372 +#: scripts/_lib/map_template.py:422 msgid "TOULOUSE-PYRENEES" msgstr "Toulouse-Pyreneeën" -#: scripts/_lib/map_template.py:373 +#: scripts/_lib/map_template.py:423 msgid "PROVENCE-CORSE" msgstr "Provence-Corsica" -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:424 msgid "CHAMPAGNE" msgstr "Champagne" -#: scripts/_lib/map_template.py:375 +#: scripts/_lib/map_template.py:425 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Cider-eaux-de-vie" -#: scripts/_lib/map_template.py:376 +#: scripts/_lib/map_template.py:426 msgid "VIN DOUX NATURELS" msgstr "Natuurlijk zoete wijnen" -#: scripts/_lib/map_template.py:377 +#: scripts/_lib/map_template.py:427 msgid "COGNAC" msgstr "Cognac" -#: scripts/_lib/map_template.py:378 +#: scripts/_lib/map_template.py:428 msgid "ARMAGNAC" msgstr "Armagnac" -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:429 msgid "RHUM" msgstr "Rum" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:469 msgid "France" msgstr "Frankrijk" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:470 msgid "Espagne" msgstr "Spanje" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:471 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:472 msgid "Italie" msgstr "Italië" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:473 msgid "Autriche" msgstr "Oostenrijk" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:474 msgid "Slovénie" msgstr "Slovenië" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:475 msgid "Croatie" msgstr "Kroatië" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:476 msgid "Hongrie" msgstr "Hongarije" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:477 msgid "Roumanie" msgstr "Roemenië" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:478 msgid "Bulgarie" msgstr "Bulgarije" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:479 msgid "Grèce" msgstr "Griekenland" -#: scripts/_lib/map_template.py:430 +#: scripts/_lib/map_template.py:480 msgid "Allemagne" msgstr "Duitsland" -#: scripts/_lib/map_template.py:431 +#: scripts/_lib/map_template.py:481 msgid "Slovaquie" msgstr "Slowakije" -#: scripts/_lib/map_template.py:432 +#: scripts/_lib/map_template.py:482 msgid "Suisse" msgstr "Zwitserland" -#: scripts/_lib/map_template.py:433 +#: scripts/_lib/map_template.py:483 msgid "Tchéquie" msgstr "Tsjechië" -#: scripts/_lib/map_template.py:434 +#: scripts/_lib/map_template.py:484 msgid "Luxembourg" msgstr "Luxemburg" -#: scripts/_lib/map_template.py:435 +#: scripts/_lib/map_template.py:485 msgid "Belgique" msgstr "België" -#: scripts/_lib/map_template.py:436 +#: scripts/_lib/map_template.py:486 msgid "Pays-Bas" msgstr "Nederland" -#: scripts/_lib/map_template.py:437 +#: scripts/_lib/map_template.py:487 msgid "Malte" msgstr "Malta" -#: scripts/_lib/map_template.py:438 +#: scripts/_lib/map_template.py:488 msgid "Chypre" msgstr "Cyprus" -#: scripts/_lib/map_template.py:439 +#: scripts/_lib/map_template.py:489 msgid "Royaume-Uni" msgstr "Verenigd Koninkrijk" diff --git a/scripts/04_build_maps.py b/scripts/04_build_maps.py index 44cecd1..8a6c97d 100644 --- a/scripts/04_build_maps.py +++ b/scripts/04_build_maps.py @@ -21,6 +21,7 @@ import datetime as dt import hashlib import json +import math import os import re import shutil @@ -183,6 +184,15 @@ ) from _lib.summaries import derive_summary from _lib.terroir_normalize import normalize_aocs +from _lib.vineyard_envelope import ( + ENVELOPE_GEOM_SOURCE, + ENVELOPE_RADIUS_M, + ENVELOPE_SIMPLIFY_M, + is_envelope_source, + lod_config, + shape_metrics, +) +from _lib.vineyard_envelope import envelope as vineyard_envelope from shapely.geometry import mapping, shape from tqdm import tqdm from unidecode import unidecode as _unidecode @@ -236,8 +246,22 @@ VENDOR_SRC = ROOT / "scripts" / "_lib" / "vendor" GEOJSON_OUT = MAP_DATA / "appellations.geojson" PMTILES_OUT = MAP_DATA / "appellations.pmtiles" +# Low-zoom tileset: the generalised vineyard footprint for French parcel-level +# records (scripts/_lib/vineyard_envelope.py), the same polygon as the detail +# tileset for everything else. Drawn below LOD_OVERVIEW_MAX_ZOOM; the detail +# tileset takes over from LOD_DETAIL_MIN_ZOOM (crossfade in between). +GEOJSON_OVERVIEW_OUT = MAP_DATA / "appellations-overview.geojson" +PMTILES_OVERVIEW_OUT = MAP_DATA / "appellations-overview.pmtiles" +# Commune-level polygons (aires-CSV / cahier commune unions). No longer tiled: +# written for scripts/audit_geometry_overlaps.py, whose sliver heuristic is +# calibrated on commune borders. GEOJSON_VILLAGES_OUT = MAP_DATA / "appellations-villages.geojson" -PMTILES_VILLAGES_OUT = MAP_DATA / "appellations-villages.pmtiles" +# The pre-LOD tileset name; unlinked so a stale copy is never served. +PMTILES_VILLAGES_STALE = MAP_DATA / "appellations-villages.pmtiles" +# A footprint this much larger than its parcels is reported after the build +# (the corpus maximum at 250 m is 2.4×; a larger ratio means parcels on both +# sides of something wide were bridged — check the water audit). +ENVELOPE_RATIO_WARN = 3.0 WIKIDATA_QIDS = ROOT / "raw" / "wikidata" / "qids-by-slug.json" # Geometry fed to tippecanoe is Douglas-Peucker simplified to cap the absurd @@ -619,6 +643,24 @@ def _bbox_area(g) -> float: return float((maxx - minx) * (maxy - miny)) +def _check_envelope(slug: str, parcels, footprint) -> None: + """A closing never leaves the r-dilation of its input (plus the final + simplify), so the footprint's bbox must sit inside the parcels' bbox grown + by r + simplify. Anything else is a bug in the envelope code, not data — + fatal, because the map would draw the footprint as the vineyard.""" + slack_m = ENVELOPE_RADIUS_M + ENVELOPE_SIMPLIFY_M + 5.0 + minx, miny, maxx, maxy = parcels.bounds + lat = math.radians((miny + maxy) / 2.0) + dlat = slack_m / 111_320.0 + dlon = dlat / max(math.cos(lat), 0.2) + ominx, ominy, omaxx, omaxy = footprint.bounds + if ominx < minx - dlon or ominy < miny - dlat or omaxx > maxx + dlon or omaxy > maxy + dlat: + raise SystemExit( + f"[geo] FATAL: footprint of {slug} leaves its parcels' bbox by more than " + f"{slack_m:.0f} m: parcels {parcels.bounds} vs footprint {footprint.bounds}" + ) + + def communes_containing(needle, insee_idx: dict[str, dict]) -> set[str]: """Return the INSEE codes of every IGN commune intersecting `needle`. @@ -695,6 +737,15 @@ def main() -> int: features: list[dict] = [] village_features: list[dict] = [] + overview_features: list[dict] = [] + # slug -> (overview geometry, its provenance) for every record with a + # polygon, so a sub-denomination that inherits its detail polygon from a + # parent / umbrella sibling inherits the same overview shape (a child must + # never draw a larger low-zoom shape than its parent). + overview_by_slug: dict[str, tuple[object, str]] = {} + envelope_stats: dict[str, float] = {} + envelope_hits = envelope_inherited = 0 + envelope_ratio_warnings: list[tuple[str, float]] = [] skipped = 0 coverage: list[tuple[str, int, int]] = [] parcel_hits = aires_hits = commune_hits = 0 @@ -2290,6 +2341,37 @@ def main() -> int: if geom is None or geom.is_empty: skipped += 1 continue + # Overview (low-zoom) geometry — what the map draws below the detail + # zoom. Parcel-level French records get the generalised vineyard + # footprint; a sub-denomination that inherits its polygon inherits the + # donor's footprint; everything else draws the same polygon as the + # commune-level villages layer (identical to the detail polygon outside + # France). Computed AFTER the outlier clip, on the tile-simplified + # geometry (the closing's own 30 m simplify swallows the difference and + # the cache key is then a function of what is tiled). + tile_geom = _simplify_for_tiles(geom) + o_geom, o_source = v_geom, v_source + if is_envelope_source(country, geom_source): + o_geom = vineyard_envelope(tile_geom, stats=envelope_stats) + o_source = ENVELOPE_GEOM_SOURCE + envelope_hits += 1 + _check_envelope(record["slug"], geom, o_geom) + if geom.area > 0 and o_geom.area / geom.area > ENVELOPE_RATIO_WARN: + envelope_ratio_warnings.append((record["slug"], o_geom.area / geom.area)) + elif geom_source == "parent-appellation" and record.get("parent_slug") in overview_by_slug: + o_geom, _donor_source = overview_by_slug[record["parent_slug"]] + envelope_inherited += 1 if _donor_source == ENVELOPE_GEOM_SOURCE else 0 + # The donor's shape was clipped under the donor's slug; honour an + # outlier override keyed on this record too (as v_geom was). + o_geom = geom_overrides.clip(record["slug"], o_geom, geom_source).geom + elif geom_source == "sibling-dgc" and sib_slug in overview_by_slug: + o_geom, _donor_source = overview_by_slug[sib_slug] + envelope_inherited += 1 if _donor_source == ENVELOPE_GEOM_SOURCE else 0 + o_geom = geom_overrides.clip(record["slug"], o_geom, geom_source).geom + if o_geom is None or o_geom.is_empty: + # Never let a record exist only above the detail zoom. + o_geom, o_source = geom, geom_source + overview_by_slug[record["slug"]] = (o_geom, o_source) if not is_sub_denomination: parent_geom_by_slug[record["slug"]] = geom if v_geom is not None and not v_geom.is_empty: @@ -2654,13 +2736,19 @@ def main() -> int: "area": area_deg2, "bbox": ",".join(f"{v:.5f}" for v in bbox), } + # Outline paint reads `frag` (perimeter/area, m⁻¹ — ink per fill) and + # `parts`, measured on the geometry that is actually tiled, so a + # 112-parcel record and a compact one of the same area get different + # stroke widths (scripts/_lib/map_template.py, _build_source_block). + common_props["parts"], common_props["frag"] = shape_metrics(tile_geom) features.append( { "type": "Feature", - "geometry": mapping(_simplify_for_tiles(geom)), + "geometry": mapping(tile_geom), "properties": common_props, } ) + v_tile = None if v_geom is not None and not v_geom.is_empty: v_minx, v_miny, v_maxx, v_maxy = v_geom.bounds v_bbox = [float(v_minx), float(v_miny), float(v_maxx), float(v_maxy)] @@ -2668,10 +2756,14 @@ def main() -> int: village_props["geom_source"] = v_source village_props["area"] = float(v_geom.area) village_props["bbox"] = ",".join(f"{v:.5f}" for v in v_bbox) + # Shape metrics describe the parcels, not this commune union. + village_props.pop("parts", None) + village_props.pop("frag", None) + v_tile = _simplify_for_tiles(v_geom) village_features.append( { "type": "Feature", - "geometry": mapping(_simplify_for_tiles(v_geom)), + "geometry": mapping(v_tile), "properties": village_props, } ) @@ -2681,16 +2773,64 @@ def main() -> int: village_commune_hits += 1 else: village_skipped += 1 + # Overview feature: same properties (`area` stays the parcellaire area + # — fill-sort-key and both paint ramps read it and must not step at the + # crossfade), its own provenance, bbox and shape metrics. + if o_geom is v_geom and v_tile is not None: + o_tile = v_tile + elif o_geom is geom: + o_tile = tile_geom + else: + o_tile = _simplify_for_tiles(o_geom) + o_minx, o_miny, o_maxx, o_maxy = o_geom.bounds + overview_props = dict(common_props) + overview_props["geom_source"] = o_source + overview_props["area_drawn"] = float(o_geom.area) + overview_props["bbox"] = ",".join( + f"{v:.5f}" for v in (float(o_minx), float(o_miny), float(o_maxx), float(o_maxy)) + ) + overview_props["parts"], overview_props["frag"] = shape_metrics(o_tile) + overview_features.append( + { + "type": "Feature", + "geometry": mapping(o_tile), + "properties": overview_props, + } + ) fc = {"type": "FeatureCollection", "features": features} GEOJSON_OUT.write_text(json.dumps(fc, ensure_ascii=False), encoding="utf-8") village_fc = {"type": "FeatureCollection", "features": village_features} GEOJSON_VILLAGES_OUT.write_text(json.dumps(village_fc, ensure_ascii=False), encoding="utf-8") + overview_fc = {"type": "FeatureCollection", "features": overview_features} + GEOJSON_OVERVIEW_OUT.write_text(json.dumps(overview_fc, ensure_ascii=False), encoding="utf-8") + if PMTILES_VILLAGES_STALE.exists(): + PMTILES_VILLAGES_STALE.unlink() + print(f"[geo] removed stale {PMTILES_VILLAGES_STALE.relative_to(ROOT)}", file=sys.stderr) + + # QA: every appellation must exist in BOTH zoom bands, or it vanishes on + # one side of the detail zoom with no error (before the LOD split two + # records had no low-zoom feature at all). Cannot fire with today's + # control flow (both appends are unconditional in the same iteration) — + # it is here to catch a future early `continue` between them. Fatal. + detail_slugs = [f["properties"]["slug"] for f in features] + overview_slugs = {f["properties"]["slug"] for f in overview_features} + missing_low = [s_ for s_ in detail_slugs if s_ not in overview_slugs] + if missing_low or len(overview_features) != len(features): + raise SystemExit( + f"[geo] FATAL: overview/detail feature mismatch — {len(features)} detail vs " + f"{len(overview_features)} overview features; missing below the detail zoom: " + f"{missing_low[:10]}" + ) # QA: tile-geometry simplification must never drop an appellation. Every # emitted feature must keep a non-empty geometry (a collapse would silently # hide that appellation from the map). Fatal so a regression can't ship. - for label, feats in (("appellations", features), ("villages", village_features)): + for label, feats in ( + ("appellations", features), + ("overview", overview_features), + ("villages", village_features), + ): empties = [ f["properties"].get("slug") for f in feats @@ -2707,6 +2847,23 @@ def main() -> int: f"aires-csv={village_aires_hits} commune-text={village_commune_hits} skipped={village_skipped}", file=sys.stderr, ) + print( + f"[geo] overview: {len(overview_features)} polygons → {GEOJSON_OVERVIEW_OUT.relative_to(ROOT)} " + f"({GEOJSON_OVERVIEW_OUT.stat().st_size // (1<<20)} MB), " + f"footprints={envelope_hits} (r={ENVELOPE_RADIUS_M} m; cache hits=" + f"{envelope_stats.get('hits', 0)}, computed={envelope_stats.get('misses', 0)} in " + f"{envelope_stats.get('secs', 0.0):.0f} s) inherited={envelope_inherited}", + file=sys.stderr, + ) + if envelope_ratio_warnings: + worst = sorted(envelope_ratio_warnings, key=lambda t: -t[1])[:10] + print( + f"[geo] WARNING: {len(envelope_ratio_warnings)} footprint(s) exceed " + f"{ENVELOPE_RATIO_WARN}× their parcels — bridged something wide? " + + ", ".join(f"{s_} {r_:.1f}×" for s_, r_ in worst) + + " — run scripts/audit_vineyard_envelopes.py", + file=sys.stderr, + ) print( f"[geo] {len(features)} appellation polygons → {GEOJSON_OUT.relative_to(ROOT)} " f"({GEOJSON_OUT.stat().st_size // (1<<20)} MB), " @@ -2757,19 +2914,26 @@ def main() -> int: # Skip the slow tippecanoe pass but still re-emit the HTML so # template/metadata changes propagate. Use the existing pmtiles # if it's on disk, otherwise fall back to the geojson source. - if PMTILES_OUT.exists() and PMTILES_VILLAGES_OUT.exists(): + if PMTILES_OUT.exists() and PMTILES_OVERVIEW_OUT.exists(): emit_html( - features, village_features, + features, overview_features, layer_url=_fingerprint("/map-data/appellations.pmtiles", PMTILES_OUT), - villages_layer_url=_fingerprint("/map-data/appellations-villages.pmtiles", PMTILES_VILLAGES_OUT), + overview_layer_url=_fingerprint("/map-data/appellations-overview.pmtiles", PMTILES_OVERVIEW_OUT), source_type="pmtiles", use_translations=not args.no_translations, ) else: + print( + "warn: --no-tippecanoe but wiki/map-data/appellations{,-overview}.pmtiles " + "are not both on disk (a checkout built before the zoom-LOD split has " + "only the old villages tileset) — emitting GeoJSON sources, which " + "deploy.py does not upload. Run a full build once.", + file=sys.stderr, + ) emit_html( - features, village_features, + features, overview_features, layer_url=_fingerprint("/map-data/appellations.geojson", GEOJSON_OUT), - villages_layer_url=_fingerprint("/map-data/appellations-villages.geojson", GEOJSON_VILLAGES_OUT), + overview_layer_url=_fingerprint("/map-data/appellations-overview.geojson", GEOJSON_OVERVIEW_OUT), source_type="geojson", use_translations=not args.no_translations, ) @@ -2778,30 +2942,42 @@ def main() -> int: if shutil.which("tippecanoe") is None: print("warn: tippecanoe not on PATH (brew install tippecanoe) — skipping pmtiles", file=sys.stderr) emit_html( - features, village_features, + features, overview_features, layer_url=_fingerprint("/map-data/appellations.geojson", GEOJSON_OUT), - villages_layer_url=_fingerprint("/map-data/appellations-villages.geojson", GEOJSON_VILLAGES_OUT), + overview_layer_url=_fingerprint("/map-data/appellations-overview.geojson", GEOJSON_OVERVIEW_OUT), source_type="geojson", use_translations=not args.no_translations, ) return 0 - for src_geojson, dst_pmtiles, layer_id in ( - (GEOJSON_OUT, PMTILES_OUT, "appellations"), - (GEOJSON_VILLAGES_OUT, PMTILES_VILLAGES_OUT, "appellations"), + # Two tilesets, one zoom band each (scripts/_lib/vineyard_envelope.py + # LOD_*): the overview tileset carries z3–z11 and is drawn below z12 + # (overzoomed across the crossfade); the detail tileset carries z11–z12 + # and is drawn from z11 (overzoomed to the client's maxZoom 14). The + # same `-l` layer name on both so one source-layer id serves both sources. + for src_geojson, dst_pmtiles, zoom_flags in ( + ( + GEOJSON_OUT, + PMTILES_OUT, + [ + "--minimum-zoom=11", + "--maximum-zoom=12", + # z12 is overzoomed to z14: keep every real parcel shape + # instead of reducing sub-pixel ones to squares / dropping them. + "--no-tiny-polygon-reduction-at-maximum-zoom", + ], + ), + (GEOJSON_OVERVIEW_OUT, PMTILES_OVERVIEW_OUT, ["--minimum-zoom=3", "--maximum-zoom=11"]), ): if dst_pmtiles.exists(): dst_pmtiles.unlink() cmd = [ "tippecanoe", "-o", str(dst_pmtiles), - "-l", layer_id, - # z3 (was z4): the default "simple" view renders the villages - # source at all zooms, so both sources must reach the continental - # overview level or polygons vanish on zoom-out (paired with the - # client's minZoom: 3). - "--minimum-zoom=3", - "--maximum-zoom=12", + "-l", "appellations", + # Overview starts at z3: paired with the client's minZoom 3 so the + # continental view never zooms out into a polygon-less void. + *zoom_flags, "--coalesce-densest-as-needed", "--extend-zooms-if-still-dropping", "--no-feature-limit", @@ -2823,9 +2999,9 @@ def main() -> int: ) emit_html( - features, village_features, + features, overview_features, layer_url=_fingerprint("/map-data/appellations.pmtiles", PMTILES_OUT), - villages_layer_url=_fingerprint("/map-data/appellations-villages.pmtiles", PMTILES_VILLAGES_OUT), + overview_layer_url=_fingerprint("/map-data/appellations-overview.pmtiles", PMTILES_OVERVIEW_OUT), source_type="pmtiles", use_translations=not args.no_translations, ) @@ -3572,10 +3748,10 @@ def _fingerprint(url: str, file: Path) -> str: def emit_html( features: list[dict], - village_features: list[dict], + overview_features: list[dict], *, layer_url: str, - villages_layer_url: str, + overview_layer_url: str, source_type: str, use_translations: bool = True, ) -> None: @@ -3592,8 +3768,10 @@ def emit_html( gi_term_display: dict[str, tuple[str, str]] = {} grapes_all_counts: dict[str, int] = {} simple_style_counts: dict[str, int] = {} + # `bbox_villages` on the startup record = the bbox of what is drawn below + # the detail zoom (the footprint / zone), read by localityRank. village_bbox_by_slug: dict[str, list[float]] = {} - for feat in village_features: + for feat in overview_features: p = feat["properties"] bbox_str = p.get("bbox") or "" if bbox_str: @@ -3927,8 +4105,9 @@ def sort_facet(d: dict[str, int]) -> list[tuple[str, int]]: facets = dict( layer_url=layer_url, - villages_layer_url=villages_layer_url, + overview_layer_url=overview_layer_url, source_type=source_type, + lod=lod_config(), aocs=aocs, facet_styles_tree=facet_styles_tree, style_descendants=style_descendants, diff --git a/scripts/_lib/assets/app.js b/scripts/_lib/assets/app.js index ffe95da..0878420 100644 --- a/scripts/_lib/assets/app.js +++ b/scripts/_lib/assets/app.js @@ -13,6 +13,12 @@ const FACET_STYLES_TREE = __OWM_styles_tree_json__; const STYLE_DESCENDANTS = __OWM_style_descendants_json__; const FACET_STYLES_SIMPLE = __OWM_styles_simple_json__; + // Level of detail (scripts/_lib/vineyard_envelope.py): below + // LOD.overview_max_zoom the map draws the generalised vineyard footprint of + // a French parcel-level record (its 250 m closing), from LOD.detail_min_zoom + // the INAO parcels; every other record keeps one polygon at every zoom. + const LOD = __OWM_lod_json__; + const LOD_SOURCES = new Set(LOD.sources || []); const FACET_PRINCIPAL = __OWM_principal_json__; const FACET_ACCESSORY = __OWM_accessory_json__; const FACET_GRAPES_ALL = __OWM_grapes_all_json__; @@ -672,7 +678,7 @@ function applyFilter(opts) { const expr = buildFilterExpr(); for (const id of ['appellations-fill', 'appellations-outline', - 'appellations-fill-villages', 'appellations-outline-villages']) { + 'appellations-fill-overview', 'appellations-outline-overview']) { if (map.getLayer(id)) map.setFilter(id, expr); } updateStatus(); @@ -738,12 +744,64 @@ refreshAllGrapeChipFilters(); } + // Which polygon of a record is on screen depends on the zoom: the footprint + // / zone (`bbox_villages` = the overview feature's bbox) below + // LOD.overview_max_zoom, the parcels (`bbox`) from there. Ranking a click + // stack must look at what was clicked; framing a record fits its parcels + // (the footprint contains them within the closing radius). + function drawnBbox(r) { + if (!r) return null; + const overview = map.getZoom() < LOD.footprint_max_zoom; + return overview ? (r.bbox_villages || r.bbox) : (r.bbox || r.bbox_villages); + } + function fitBbox(r) { + return r ? (r.bbox || r.bbox_villages) : null; + } + // The record whose parcels a footprint generalises: the record itself, or + // the parent / umbrella sibling it inherited its polygon from (those two are + // panel-payload fields, so before hydration an inherited DGC reads as + // 'zone'). + function envelopeDonor(r) { + if (!r || (LOD.countries || []).indexOf(r.country || 'fr') < 0) return null; + if (LOD_SOURCES.has(r.geom_source)) return r; + if (r.geom_source === 'parent-appellation' && r.parent_slug) { + const p = AOCS[r.parent_slug]; + return p && LOD_SOURCES.has(p.geom_source) ? p : null; + } + if (r.geom_source === 'sibling-dgc' && r.geom_fallback_slug) { + const u = AOCS[r.geom_fallback_slug]; + return u && LOD_SOURCES.has(u.geom_source) ? u : null; + } + return null; + } + function hasFootprint(r) { return !!envelopeDonor(r); } + // What the visitor is looking at for this record: 'footprint' (generalised + // parcels, below the detail zoom), 'parcels', or 'zone' (one polygon at + // every zoom). Reported on the feedback and view events so a boundary flag + // can be read against the shape that was on screen. + function lodBand(r) { + if (!hasFootprint(r)) return 'zone'; + // footprint_max_zoom = the end of the crossfade: the overview layers stop + // there, so "footprint" is claimed only while some of it is on screen. + return map.getZoom() < LOD.footprint_max_zoom ? 'footprint' : 'parcels'; + } + function zoomStr() { return String(Math.round(map.getZoom() * 10) / 10); } + // The footprint note in an open card is a zoom-dependent statement: show it + // below the detail zoom, hide it above. Patch the node in place — never + // re-render the card from a zoom, that would wipe pressed feedback chips + // and a half-typed note. + function refreshLodLines() { + const overview = map.getZoom() < LOD.footprint_max_zoom; + document.querySelectorAll('#panel [data-lod-line]').forEach(el => { el.hidden = !overview; }); + } + map.on('zoomend', refreshLodLines); + function fitToFiltered() { let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; let any = false; for (const slug in AOCS) { const rec = AOCS[slug]; - const b = (viewMode === 'simple' && rec.bbox_villages) ? rec.bbox_villages : rec.bbox; + const b = fitBbox(rec); if (!b) continue; if (!matchesClient(rec, slug)) continue; if (b[0] < minX) minX = b[0]; @@ -1119,9 +1177,11 @@ stackFocusIndex = 0; renderPanelStack([slug], 0, undefined, 'facet'); track('Appellation Opened', { slug: slug, via: 'facet', locale: LANG }); - const b = (viewMode === 'simple' && AOCS[slug].bbox_villages) ? AOCS[slug].bbox_villages : AOCS[slug].bbox; + const b = fitBbox(AOCS[slug]); if (b && typeof map.fitBounds === 'function') { - map.fitBounds([[b[0], b[1]], [b[2], b[3]]], { padding: 40, maxZoom: 11, duration: 500 }); + // maxZoom = LOD.overview_max_zoom: "show me this appellation" lands on + // its parcels, not on the half-faded footprint at the crossfade. + map.fitBounds([[b[0], b[1]], [b[2], b[3]]], { padding: 40, maxZoom: LOD.overview_max_zoom, duration: 500 }); } }); @@ -1210,7 +1270,9 @@ const modes = el.dataset.modes.split(/\s+/); el.classList.toggle('mode-hidden', !modes.includes(viewMode)); }); - swapMapLayers(); + // The geometry is no longer the mode's business: zoom decides between the + // overview and detail sources (see the source block). The mode only + // gates the sidebar (facet depth, spirits). // The appellation tree's contents depend on spiritsVisible(), which // depends on viewMode — rebuild on every mode switch (optional-chain the // children check defensively). @@ -1219,23 +1281,6 @@ } } - function swapMapLayers() { - // Halo layers must flip with their fill/outline siblings: the selection - // halo is a dark, wide stroke drawn UNDER the cream outline. If the - // inactive mode's halo stays visible it sits above the active mode's cream - // outline (later in draw order) and dims the selection — so simple mode - // looked far less clearly selected than advanced. Toggle all three. - const advLayers = ['appellations-fill', 'appellations-halo', 'appellations-outline']; - const vilLayers = ['appellations-fill-villages', 'appellations-halo-villages', 'appellations-outline-villages']; - const showAdv = viewMode === 'advanced'; - for (const id of advLayers) { - if (map.getLayer(id)) map.setLayoutProperty(id, 'visibility', showAdv ? 'visible' : 'none'); - } - for (const id of vilLayers) { - if (map.getLayer(id)) map.setLayoutProperty(id, 'visibility', showAdv ? 'none' : 'visible'); - } - } - document.querySelectorAll('#mode-toggle .mode-btn').forEach(b => { b.addEventListener('click', () => { const next = b.dataset.mode; @@ -1742,8 +1787,8 @@ stackFocusIndex = 0; renderPanelStack([key], 0, undefined, 'omnisearch'); track('Appellation Opened', { slug: key, via: 'omnisearch', locale: LANG }); - const b = (viewMode === 'simple' && AOCS[key].bbox_villages) ? AOCS[key].bbox_villages : AOCS[key].bbox; - if (b && typeof map.fitBounds === 'function') map.fitBounds([[b[0], b[1]], [b[2], b[3]]], { padding: 40, maxZoom: 11, duration: 500 }); + const b = fitBbox(AOCS[key]); + if (b && typeof map.fitBounds === 'function') map.fitBounds([[b[0], b[1]], [b[2], b[3]]], { padding: 40, maxZoom: LOD.overview_max_zoom, duration: 500 }); } } @@ -2286,6 +2331,25 @@ return `

${sentence}

`; } + // Provenance sentence for the zone-level sources (the other countries' + // regulators): what the polygon is and at what resolution. Mirrored by + // _geom_source_line in scripts/_lib/content_block.py. + function geomSourceLine(r) { + const gs = r.geom_source || ''; + if (gs.startsWith('geoportal-zone:') || gs.startsWith('geoportal-canton:')) { + let region = gs.slice(gs.indexOf(':') + 1); + region = gs.startsWith('geoportal-canton:') + ? region.toUpperCase() + : region.split('+').map(p => p.charAt(0).toUpperCase() + p.slice(1)).join(' + '); + return escapeHtml(fmt(LABELS.geom_src_geoportal, { region: region })); + } + if (gs === 'mapa-zone') return escapeHtml(LABELS.geom_src_mapa); + if (gs === 'figshare-pdo' || gs === 'figshare-pdo-alias') return escapeHtml(LABELS.geom_src_betard); + if (gs === 'region-pdo-union') return escapeHtml(LABELS.geom_src_pdo_union); + if (/^(gisco-|caop-|swissboundaries-|ons-)/.test(gs) || gs === 'nuts2-province' || gs === 'geometry-research-municipios') return escapeHtml(LABELS.geom_src_admin_union); + return ''; + } + function renderAocCard(slug, isPrimary) { const r = AOCS[slug]; if (!r) return ''; @@ -2369,7 +2433,21 @@ } else if (r.geom_source === 'cadastre-lieu-dit-dgc' && r.cadastre_lieu_dit) { const src = `${escapeHtml(LABELS.geom_approx_cadastre_source_label)}`; approxLine = `
${fmt(LABELS.geom_approx_cadastre, { lieu_dit: escapeHtml(r.cadastre_lieu_dit), commune: escapeHtml(r.cadastre_commune || ''), source: src })}
`; - } + } else if (r.geom_source === 'aires-csv' || r.geom_source === 'dgc-village-override' || r.geom_source === 'communes') { + // A commune union: no parcel delimitation for this record, so the + // polygon includes water and non-vineyard land — say so (every French + // IGP and Champagne draw this way). + approxLine = `
${escapeHtml(fmt(LABELS.geom_approx_aires_union, { n: r.communes_matched || 0 }))}
`; + } else { + const src = geomSourceLine(r); + if (src) approxLine = `
${src}
`; + } + // Zoom-dependent: below the detail zoom the map shows a generalised + // footprint of this record's parcels, not the delimitation. Hidden above + // it; refreshLodLines() flips the node on zoomend. + const lodLine = hasFootprint(r) + ? `
${escapeHtml(fmt(LABELS.geom_lod_footprint, { radius: LOD.radius_m }))}
` + : ''; const stubLine = r.is_stub ? `
${fmt(LABELS.stub_message, { doc: '' + escapeHtml(STUB_DOC_NAMES[r.country] || STUB_DOC_NAMES.fr) + '' })} ${escapeHtml(LABELS.stub_help_label)}
` : ''; @@ -2398,6 +2476,7 @@ ${cancelledLine(r)} ${dgcLine} ${approxLine} + ${lodLine} ${stubLine} ${styleChips ? '

' + LABELS.panel_styles_h + '

' + styleChips + '
' : ''} ${principal ? '

' + LABELS.facet_principal_h + '

' + principal + '
' : ''} @@ -2499,7 +2578,7 @@ // retraction next to it and net the two when reading. chip.setAttribute('aria-pressed', 'false'); rememberFeedback(slug, aspect, false); - track('Feedback Retracted', { slug: slug, aspect: aspect, locale: LANG }); + track('Feedback Retracted', { slug: slug, aspect: aspect, view_mode: viewMode, lod: lodBand(r), zoom: zoomStr(), locale: LANG }); const left = pressedAspects(row); const status = box.querySelector('.fb-status'); if (left.length && status && box.querySelector('textarea.fb-note')) { @@ -2515,7 +2594,8 @@ rememberFeedback(slug, aspect, true); track('Feedback Flagged', { slug: slug, aspect: aspect, country: r.country || '', kind: r.kind || '', - geom_source: r.geom_source || '', via: via, locale: LANG, + geom_source: r.geom_source || '', view_mode: viewMode, lod: lodBand(r), zoom: zoomStr(), + via: via, locale: LANG, }); } box.hidden = false; @@ -2538,7 +2618,7 @@ const note = ta ? ta.value.replace(/\s+/g, ' ').trim().slice(0, FEEDBACK_NOTE_MAX) : ''; // `aspect` is the pressed set joined with '+', so one note about two // things reads `boundary+grapes` in the breakdown rather than only the last tap. - if (note) track('Feedback Note', { slug: slug, aspect: aspects.join('+') || 'other', note: note, locale: LANG }); + if (note) track('Feedback Note', { slug: slug, aspect: aspects.join('+') || 'other', note: note, view_mode: viewMode, lod: lodBand(AOCS[slug]), zoom: zoomStr(), locale: LANG }); box.innerHTML = `${note ? escapeHtml(LABELS.feedback_sent) : fmt(LABELS.feedback_noted_html, { aspect: aspectsHtml(aspects) })}`; } @@ -2556,10 +2636,8 @@ // polygon area is smaller. const r = AOCS[slug]; if (!r) return Infinity; - const primary = viewMode === 'advanced' ? r.bbox : r.bbox_villages; - const fallback = viewMode === 'advanced' ? r.bbox_villages : r.bbox; - const a = bboxArea(primary); - return Number.isFinite(a) ? a : bboxArea(fallback); + const a = bboxArea(drawnBbox(r)); + return Number.isFinite(a) ? a : bboxArea(r.bbox); } // Tab title for an open appellation — mirrors the server-rendered entity @@ -2743,6 +2821,8 @@ stacked: sorted.length > 1 ? 'true' : 'false', stack_size: String(sorted.length), via: via || 'map', + lod: lodBand(fr), + zoom: zoomStr(), locale: LANG, }); } @@ -2762,11 +2842,11 @@ } // ----- selection highlight + persistence (across reload / language switch) ----- - // Selection is mirrored into both `appellations` (advanced/parcellaire) and - // `appellations-villages` (simple/commune) sources so the highlight follows - // the user across mode toggles. setFeatureState calls before map.on('load') - // throw because the source isn't registered yet — we swallow and re-apply - // at the end of map.on('load'). + // Selection is mirrored into both `appellations` (the parcels, detail zoom) + // and `appellations-overview` (the footprint / zone below it) so the + // highlight survives zooming across the crossfade. setFeatureState calls + // before map.on('load') throw because the source isn't registered yet — we + // swallow and re-apply at the end of map.on('load'). let selectedSlugs = []; // The element that opened the panel (a facet "open" button), so focus can // return there on close (WCAG 2.4.3). Null for map clicks / in-panel @@ -2780,7 +2860,7 @@ let stackFocusIndex = 0; function setSelectedState(slug, selected) { - for (const source of ['appellations', 'appellations-villages']) { + for (const source of ['appellations', 'appellations-overview']) { const opts = { source: source, id: slug }; if (SOURCE_TYPE === 'pmtiles') opts.sourceLayer = 'appellations'; try { map.setFeatureState(opts, { selected: selected }); } catch (e) {} @@ -2866,10 +2946,8 @@ // page-entry snapshot, not the live hash — maplibre has already written // the default camera into location.hash by now. if (!INITIAL_CAMERA_HASH) { - // Mode-aware like fitToFiltered / the facet-open handler: simple mode - // renders the villages geometry, so frame that extent, not parcellaire. - const b = (viewMode === 'simple' && AOCS[urlSlug].bbox_villages) ? AOCS[urlSlug].bbox_villages : AOCS[urlSlug].bbox; - if (b) map.once('load', () => map.fitBounds([[b[0], b[1]], [b[2], b[3]]], { padding: 60, maxZoom: 11, duration: 0 })); + const b = fitBbox(AOCS[urlSlug]); + if (b) map.once('load', () => map.fitBounds([[b[0], b[1]], [b[2], b[3]]], { padding: 60, maxZoom: LOD.overview_max_zoom, duration: 0 })); } return; } @@ -3052,7 +3130,7 @@ map.on('load', () => { __OWM_source_block__ for (const id of ['appellations-fill', 'appellations-outline', - 'appellations-fill-villages', 'appellations-outline-villages']) { + 'appellations-fill-overview', 'appellations-outline-overview']) { map.on('mousemove', id, e => { if (!e.features.length) return; map.getCanvas().style.cursor = 'pointer'; @@ -3077,8 +3155,10 @@ __OWM_source_block__ // typical zoom; a point-only hit-test misses them. const r = 4; const bbox = [[e.point.x - r, e.point.y - r], [e.point.x + r, e.point.y + r]]; + // Both fill layers: inside the crossfade band a record is present in + // both sources (the same slug twice — deduped below). const features = map.queryRenderedFeatures(bbox, { - layers: ['appellations-fill', 'appellations-fill-villages'], + layers: ['appellations-fill', 'appellations-fill-overview'], }); if (!features.length) { closePanel(false); return; } // Dedupe by slug, and drop DGCs that share another AOC's polygon diff --git a/scripts/_lib/content_block.py b/scripts/_lib/content_block.py index 20de7e0..185b030 100644 --- a/scripts/_lib/content_block.py +++ b/scripts/_lib/content_block.py @@ -100,6 +100,9 @@ class RenderCtx: # Tooltip payload for the two naming tokens (scheme id / ':'), # see _lib/gi_terms.build_terms_info. Empty = plain text, no . terms_info: dict = field(default_factory=dict) + # Level-of-detail config (scripts/_lib/vineyard_envelope.py lod_config): + # which records draw a generalised footprint below the detail zoom. + lod: dict = field(default_factory=dict) # ---------------------------------------------------------------- primitives @@ -794,28 +797,82 @@ def _span(text: str, key: str, cls: str) -> str: return _span(label, scheme, "gi-scheme") +_ADMIN_UNION_PREFIXES = ("gisco-", "caop-", "swissboundaries-", "ons-") + + +def _geom_source_line(rec: dict, lab: dict) -> str: + """Provenance sentence for the zone-level sources (no parcel delimitation): + the regional geoportal / MAPA production zone, Bétard 2022, or an + administrative union. Mirrors ``geomSourceLine`` in app.js.""" + gs = rec.get("geom_source") or "" + if gs.startswith("geoportal-zone:") or gs.startswith("geoportal-canton:"): + region = gs.split(":", 1)[1] + if gs.startswith("geoportal-canton:"): + region = region.upper() + else: + region = " + ".join(part[:1].upper() + part[1:] for part in region.split("+")) + return esc(fmt(lab["geom_src_geoportal"], {"region": region})) + if gs == "mapa-zone": + return esc(lab["geom_src_mapa"]) + if gs in ("figshare-pdo", "figshare-pdo-alias"): + return esc(lab["geom_src_betard"]) + if gs == "region-pdo-union": + return esc(lab["geom_src_pdo_union"]) + if gs.startswith(_ADMIN_UNION_PREFIXES) or gs in ("nuts2-province", "geometry-research-municipios"): + return esc(lab["geom_src_admin_union"]) + return "" + + +def has_footprint(rec: dict, lod: dict) -> bool: + """Does the map draw a generalised footprint of this record below the + detail zoom? Same rule as stage 04 (`is_envelope_source`) and + ``hasFootprint`` in app.js, minus the inherited-donor case, which needs + the donor record and is resolved client-side only.""" + if not lod: + return False + return (rec.get("country") or "fr") in (lod.get("countries") or []) and ( + rec.get("geom_source") in (lod.get("sources") or []) + ) + + def _approx_line(rec: dict, ctx: RenderCtx) -> str: lab = ctx.labels gs = rec.get("geom_source") + line = "" if gs == "sibling-dgc" and rec.get("geom_fallback_slug"): u = ( f'' f'{esc(rec.get("geom_fallback_name") or rec["geom_fallback_slug"])}' ) - return f'
{fmt(lab["geom_approx_within"], {"umbrella": u})}
' - if gs == "parent-appellation": - return f'
{esc(lab["geom_approx_parent"])}
' - if gs == "aires-csv-dgc": - return f'
{esc(lab["geom_approx_aires"])}
' - if gs == "cadastre-lieu-dit-dgc" and rec.get("cadastre_lieu_dit"): + line = fmt(lab["geom_approx_within"], {"umbrella": u}) + elif gs == "parent-appellation": + line = esc(lab["geom_approx_parent"]) + elif gs == "aires-csv-dgc": + line = esc(lab["geom_approx_aires"]) + elif gs == "pdo-plan-parcel-hull-approx": + line = esc(lab["geom_approx_pdo_plan"]) + elif gs == "cadastre-lieu-dit-dgc" and rec.get("cadastre_lieu_dit"): src = ( '' f'{esc(lab["geom_approx_cadastre_source_label"])}' ) - return ( - f'
{fmt(lab["geom_approx_cadastre"], {"lieu_dit": esc(rec["cadastre_lieu_dit"]), "commune": esc(rec.get("cadastre_commune") or ""), "source": src})}
' + line = fmt( + lab["geom_approx_cadastre"], + { + "lieu_dit": esc(rec["cadastre_lieu_dit"]), + "commune": esc(rec.get("cadastre_commune") or ""), + "source": src, + }, ) - return "" + elif gs in ("aires-csv", "dgc-village-override", "communes"): + line = esc(fmt(lab["geom_approx_aires_union"], {"n": rec.get("communes_matched") or 0})) + else: + line = _geom_source_line(rec, lab) + out = f'
{line}
' if line else "" + if has_footprint(rec, ctx.lod): + note = fmt(lab["geom_lod_footprint_static"], {"radius": ctx.lod.get("radius_m", "")}) + out += f'
{esc(note)}
' + return out # ---------------------------------------------------------------- entry point diff --git a/scripts/_lib/map_template.py b/scripts/_lib/map_template.py index c1d0d43..ea9d71b 100644 --- a/scripts/_lib/map_template.py +++ b/scripts/_lib/map_template.py @@ -162,6 +162,48 @@ def build_labels(_: Callable[[str], str]) -> dict[str, str]: "parcellaires du plan de l'aire délimitée annexé au cahier des " "charges ; ce n'est pas une limite officielle." ), + # Provenance lines for the polygons that are not a parcel delimitation: + # a commune union (French records without an INAO parcellaire — every + # IGP, Champagne), the zone-level sources of the other countries, and + # the zoom-dependent footprint (scripts/_lib/vineyard_envelope.py). + "geom_approx_aires_union": _( + "Aire approchée — réunion des {n} commune(s) de l'aire délimitée " + "(INAO), plans d'eau et terres non viticoles compris ; pas de " + "délimitation parcellaire pour cette dénomination." + ), + "geom_lod_footprint": _( + "Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m " + "à partir de la délimitation parcellaire INAO — pas la délimitation " + "elle-même ; les parcelles apparaissent en zoomant." + ), + "geom_lod_footprint_static": _( + "Sur la carte, aux zooms d'ensemble, cette appellation est dessinée " + "comme une silhouette de son vignoble généralisée à {radius} m à " + "partir de la délimitation parcellaire INAO ; les parcelles " + "elles-mêmes apparaissent en zoomant." + ), + "geom_src_geoportal": _( + "Aire : zone de production officielle publiée par le géoportail " + "({region})." + ), + "geom_src_mapa": _( + "Aire : zone de production officielle du MAPA (Zonas de Calidad " + "Diferenciada : Vinos)." + ), + "geom_src_betard": _( + "Aire approchée — polygone du jeu de données Bétard 2022 (AOP de " + "l'UE), à la résolution des communes ; peut inclure des terres non " + "viticoles." + ), + "geom_src_pdo_union": _( + "Aire approchée — réunion des polygones des appellations membres " + "(Bétard 2022), à la résolution des communes." + ), + "geom_src_admin_union": _( + "Aire approchée — réunion des unités administratives nommées dans " + "le document (limites Eurostat GISCO ou cadastre national), terres " + "non viticoles comprises." + ), "stack_header": _("{n} appellations à ce point"), "stack_cycle_hint": _("Cliquer à nouveau pour parcourir les autres"), "src_cahier": _("Cahier des charges (BO Agri, PDF)"), @@ -280,6 +322,14 @@ def build_labels(_: Callable[[str], str]) -> dict[str, str]: "canoniques et numéros de cépage (citation Röckel et al.). Tout extrait " "Wikipedia est signalé sur place. Détails et licences dans le {readme}." ), + "about_lod_html": _( + "Niveau de détail : en dessous du zoom {zoom}, les appellations " + "françaises dotées d'une délimitation parcellaire INAO sont dessinées " + "comme une silhouette de leur vignoble, généralisée à {radius} m " + "(fermeture morphologique des parcelles) ; à partir du zoom {zoom}, " + "ce sont les parcelles elles-mêmes. Les autres appellations gardent " + "le même contour à tous les zooms." + ), "about_contrib_html": _("Suggestions et pull requests bienvenues sur {github}."), "about_privacy_html": _( "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -563,6 +613,7 @@ def _build_about_dialog( data_updated_html: str = "", corpus_counts: dict | None = None, locale: str = "en", + lod: dict | None = None, ) -> str: devloed = _ext_link(_DEVLOED_URL, "devloed.com") github = _ext_link(_GITHUB_URL, "GitHub") @@ -582,6 +633,13 @@ def _build_about_dialog( labels["about_data_html"].format( inao=inao, ign=ign, wikipedia=wikipedia, vivc=vivc, readme=readme ), + *( + [labels["about_lod_html"].format( + zoom=lod["overview_max_zoom"], radius=lod["radius_m"] + )] + if lod + else [] + ), labels["about_llm_html"], roadmap, labels["about_contrib_html"].format(github=github), @@ -651,160 +709,261 @@ def _bassin_match_expr() -> str: def _build_source_block( *, layer_url: str, - villages_layer_url: str, + overview_layer_url: str, source_type: str, area_q1: float, area_q3: float, + lod: dict | None = None, ) -> str: - """Build the JS that adds appellation sources (detailed + villages) and - twin fill/outline layers. The mode toggle on the client flips visibility - between the two sets of layers; the bassin underlay is shared. + """Build the JS that adds the two appellation sources and their layers. + + Zoom, not a toggle, decides which source is drawn (see + scripts/_lib/vineyard_envelope.py): `appellations-overview` — the + generalised vineyard footprint for French parcel-level records, the same + polygon as the detail source for everything else — below + `overview_max_zoom`; `appellations` (the parcels) from `detail_min_zoom`; + both across the crossfade band with complementary opacity. The fill, halo + and outline layers of the two sources are interleaved so the selection + halo always sits above both fills, and one `source-layer` id serves both. """ - bassin_expr = _bassin_match_expr() - - def _layer_block(suffix: str, source_id: str, layer_meta: str, *, with_bassin: bool) -> str: - bassin = "" - if with_bassin: - bassin = ( - " map.addLayer({\n" - f" id: 'appellations-bassin{suffix}', type: 'fill', source: '{source_id}',\n" - + layer_meta - + " paint: {\n" - + f" 'fill-color': {bassin_expr},\n" - + " 'fill-opacity': [\n" - + " 'interpolate', ['linear'], ['zoom'],\n" - + " 5, 0.35,\n" - + " 8, 0.0\n" - + " ],\n" - + " 'fill-outline-color': 'rgba(0,0,0,0)'\n" - + " }\n" - + " });\n" - ) + lod = lod or {} + z_detail_min = lod.get("detail_min_zoom", 11) + fade0, fade1 = lod.get("crossfade", (11.0, 11.9)) + # The overview layers end where the crossfade ends, so they are neither + # drawn nor hit-testable once the parcels are at full opacity. + z_over_max = lod.get("footprint_max_zoom", fade1) + # The zoom stops below are literals around the configurable band; a retune + # that pulls the crossfade under them would emit a non-ascending + # interpolate and MapLibre would refuse the whole style. + assert 10 < fade0 < fade1 <= z_over_max, (fade0, fade1, z_over_max) + assert z_detail_min <= fade0, (z_detail_min, fade0) + sel = "['boolean', ['feature-state', 'selected'], false]" + + def ramp(small: float, large: float) -> str: + return ( + f"['interpolate', ['linear'], ['get', 'area'], " + f"{area_q1}, {small}, {area_q3}, {large}]" + ) + + # Fill: selected 0.60, else the area ramp (small polygons more opaque so + # DGCs / climats read on top of their parent; large ones translucent so the + # overview isn't flooded by regional fills). + def fill_op(small: float) -> str: + return f"['case', {sel}, 0.60, {ramp(small, 0.20)}]" + + # At the continental overview a footprint is a few pixels across and a + # region stacks five to ten of them (regional AOC, village AOCs, crus); + # drawn at full ramp opacity with dark outlines they compound into black + # speckle. Footprints only: fill scaled down below z8, no outline below + # z6.5 (the frag / area laws take over from z8). Everything else keeps + # today's overview paint. + is_env = "['==', ['get', 'geom_source'], 'parcellaire-envelope']" + + def fill_op_over(small: float, env_scale: float) -> str: + return ( + f"['case', {sel}, 0.60, ['case', {is_env}, " + f"['*', {env_scale}, {ramp(small, 0.20)}], {ramp(small, 0.20)}]]" + ) + + # Outline — constant ink. `frag` is perimeter/area in m⁻¹, so + # frag × width_px × m_per_px is the stroke area per fill area; capping that + # ratio at 0.35 gives width = K_z / frag with K_z = 0.35 / m_per_px(z), + # m_per_px(z) = 156543 · cos 46° / 2^z. A 112-parcel record draws a + # 0.4–0.8 px edge at z11–z12 where a single-part one keeps the area ramp + # (the clamp only ever lowers the width). A missing / zero `frag` falls + # through to the ramp. + def ink(z: float, w_min: float) -> str: + k = 0.35 * (2 ** z) / 108_747.0 return ( - bassin - + " map.addLayer({\n" - + f" id: 'appellations-fill{suffix}', type: 'fill', source: '{source_id}',\n" - + layer_meta - # Smaller polygons (DGCs, lieux-dits, grand crus) must render - # on top of their containing parent so the user can see them. - # MapLibre's fill-sort-key sorts ascending; -area makes the - # smallest polygon get the highest sort key (drawn last/on top). - + " layout: {\n" - + " 'fill-sort-key': ['-', 0, ['get', 'area']]\n" - + " },\n" - + " paint: {\n" - + " 'fill-color': [\n" - + " 'case',\n" - + " ['==', ['get', 'kind'], 'IGP'], '#6e7546',\n" - + " '#934050'\n" - + " ],\n" - # Zoom-aware so small appellations stay legible at the continental - # overview (z3) — their opacity floor is lifted there, reverting to - # the detail-zoom value by z8. The large-area end is unchanged so the - # overview isn't flooded with heavy regional fills. - # NOTE: a ['zoom'] expression must be the *top-level* input — it - # cannot be nested under the feature-state ['case']. So zoom is the - # outer interpolate and the selected-override + area ramp live inside - # each zoom stop (the selected value is constant across stops). - + " 'fill-opacity': [\n" - + " 'interpolate', ['linear'], ['zoom'],\n" - + " 3, ['case',\n" - + " ['boolean', ['feature-state', 'selected'], false], 0.60,\n" - + f" ['interpolate', ['linear'], ['get', 'area'], {area_q1}, 0.60, {area_q3}, 0.20]],\n" - + " 8, ['case',\n" - + " ['boolean', ['feature-state', 'selected'], false], 0.60,\n" - + f" ['interpolate', ['linear'], ['get', 'area'], {area_q1}, 0.50, {area_q3}, 0.20]]\n" - + " ]\n" - + " }\n" - + " });\n" - # Halo line drawn under the outline so the cream selection stroke - # has a dark edge against the cream basemap. Width 0 / fully - # transparent unless `selected` feature-state is set. - + " map.addLayer({\n" - + f" id: 'appellations-halo{suffix}', type: 'line', source: '{source_id}',\n" - + layer_meta - + " layout: {\n" - + " 'line-sort-key': ['-', 0, ['get', 'area']],\n" - + " 'line-join': 'round'\n" - + " },\n" - + " paint: {\n" - + " 'line-color': ['case', ['boolean', ['feature-state', 'selected'], false], '#1a0810', 'rgba(0,0,0,0)'],\n" - + " 'line-width': ['case', ['boolean', ['feature-state', 'selected'], false], 4.5, 0],\n" - + " 'line-opacity': ['case', ['boolean', ['feature-state', 'selected'], false], 0.85, 0]\n" - + " }\n" - + " });\n" - + " map.addLayer({\n" - + f" id: 'appellations-outline{suffix}', type: 'line', source: '{source_id}',\n" - + layer_meta - + " layout: {\n" - + " 'line-sort-key': ['-', 0, ['get', 'area']]\n" - + " },\n" - + " paint: {\n" - + " 'line-color': ['case', ['boolean', ['feature-state', 'selected'], false], '#fff8e8', '#2a1014'],\n" - # Zoom-aware (see fill-opacity above): a slightly heavier outline on - # small appellations at the overview keeps them as crisp marks when - # the fill is sub-pixel; large-area outlines stay thin to avoid a - # cluttered web of borders at z3. ['zoom'] is top-level (see note - # above) — the selected-override + area ramp live inside each stop. - + " 'line-width': [\n" - + " 'interpolate', ['linear'], ['zoom'],\n" - + " 3, ['case',\n" - + " ['boolean', ['feature-state', 'selected'], false], 2.5,\n" - + f" ['interpolate', ['linear'], ['get', 'area'], {area_q1}, 1.4, {area_q3}, 0.3]],\n" - + " 8, ['case',\n" - + " ['boolean', ['feature-state', 'selected'], false], 2.5,\n" - + f" ['interpolate', ['linear'], ['get', 'area'], {area_q1}, 1.2, {area_q3}, 0.3]]\n" - + " ]\n" - + " }\n" - + " });\n" + f"['max', {w_min}, ['/', {k:.5g}, " + f"['max', 1e-6, ['coalesce', ['get', 'frag'], 0]]]]" ) + def width(z: float, small: float, large: float, w_min: float | None = None) -> str: + base = ramp(small, large) + if w_min is None: + return f"['case', {sel}, 2.5, {base}]" + return f"['case', {sel}, 2.5, ['min', {base}, {ink(z, w_min)}]]" + + # Below the detail zoom a sub-half-pixel stroke still renders as a + # hairline, so the shredded shapes that survive generalisation (a + # regional footprint in hundreds of patches, the few multi-hundred-part + # zones) lose their outline through opacity, not width. Keyed on `parts`, + # which is scale-free — perimeter/area would dim every small compact + # climat too. Selected always draws in full. + parts_op = ( + "['interpolate', ['linear'], ['coalesce', ['get', 'parts'], 1], " + "20, 1.0, 200, 0.3]" + ) + line_op_over = f"['case', {sel}, 1.0, {parts_op}]" + line_op_over_low = f"['case', {sel}, 1.0, ['case', {is_env}, 0, {parts_op}]]" + if source_type == "pmtiles": - adv_decl = ( + decl = ( " map.addSource('appellations', {\n" " type: 'vector',\n" f" url: 'pmtiles://{layer_url}',\n" " promoteId: 'slug',\n" " });\n" - ) - vil_decl = ( - " map.addSource('appellations-villages', {\n" + " map.addSource('appellations-overview', {\n" " type: 'vector',\n" - f" url: 'pmtiles://{villages_layer_url}',\n" + f" url: 'pmtiles://{overview_layer_url}',\n" " promoteId: 'slug',\n" " });\n" ) layer_meta = " 'source-layer': 'appellations',\n" else: - adv_decl = ( + decl = ( " map.addSource('appellations', {\n" " type: 'geojson',\n" f" data: '{layer_url}',\n" " promoteId: 'slug'\n" " });\n" - ) - vil_decl = ( - " map.addSource('appellations-villages', {\n" + " map.addSource('appellations-overview', {\n" " type: 'geojson',\n" - f" data: '{villages_layer_url}',\n" + f" data: '{overview_layer_url}',\n" " promoteId: 'slug'\n" " });\n" ) layer_meta = "" + over_meta = layer_meta + f" maxzoom: {z_over_max},\n" + detail_meta = layer_meta + f" minzoom: {z_detail_min},\n" + # MapLibre needs ['zoom'] as the TOP-LEVEL interpolate input — it cannot + # be nested under the feature-state ['case'] — so zoom is the outer + # interpolate and the selected override + area / frag ramps live inside + # each stop. + fill_sort = " layout: {\n 'fill-sort-key': ['-', 0, ['get', 'area']]\n },\n" + fill_color = ( + " 'fill-color': [\n" + " 'case',\n" + " ['==', ['get', 'kind'], 'IGP'], '#6e7546',\n" + " '#934050'\n" + " ],\n" + ) + halo_layout = ( + " layout: {\n" + " 'line-sort-key': ['-', 0, ['get', 'area']],\n" + " 'line-join': 'round'\n" + " },\n" + ) + halo_color = f" 'line-color': ['case', {sel}, '#1a0810', 'rgba(0,0,0,0)'],\n" + halo_width = f" 'line-width': ['case', {sel}, 4.5, 0],\n" + outline_layout = " layout: {\n 'line-sort-key': ['-', 0, ['get', 'area']]\n },\n" + outline_color = f" 'line-color': ['case', {sel}, '#fff8e8', '#2a1014'],\n" + return ( - adv_decl - + vil_decl - # Villages layers are added first so advanced overlays cleanly when - # toggled on. The bassin underlay is shared across modes; we only - # add it once (to the villages source — visibility unaffected by - # which appellation layer is active because bassin is a fill of - # `region` polygons, not appellation outlines). - # Bassin underlay temporarily disabled: the regional colour fill made - # it hard to tell which appellations are actually visible. Flip back to - # with_bassin=True to restore it. - + _layer_block("-villages", "appellations-villages", layer_meta, with_bassin=False) - + _layer_block("", "appellations", layer_meta, with_bassin=False) + decl + # ---- fills (overview under detail) ---- + + " map.addLayer({\n" + + " id: 'appellations-fill-overview', type: 'fill', source: 'appellations-overview',\n" + + over_meta + + fill_sort + + " paint: {\n" + + fill_color + + " 'fill-opacity': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" 3, {fill_op_over(0.60, 0.5)},\n" + + f" 6, {fill_op_over(0.55, 0.65)},\n" + + f" 8, {fill_op(0.50)},\n" + + f" {fade0}, {fill_op(0.50)},\n" + + f" {fade1}, 0\n" + + " ]\n" + + " }\n" + + " });\n" + + " map.addLayer({\n" + + " id: 'appellations-fill', type: 'fill', source: 'appellations',\n" + + detail_meta + + fill_sort + + " paint: {\n" + + fill_color + + " 'fill-opacity': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" {fade0}, 0,\n" + + f" {fade1}, {fill_op(0.50)}\n" + + " ]\n" + + " }\n" + + " });\n" + # ---- selection halos (dark, wide, under the cream outline; above + # BOTH fills so a half-faded detail fill never tints the stroke) ---- + + " map.addLayer({\n" + + " id: 'appellations-halo-overview', type: 'line', source: 'appellations-overview',\n" + + over_meta + + halo_layout + + " paint: {\n" + + halo_color + + halo_width + + " 'line-opacity': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" {fade0}, ['case', {sel}, 0.85, 0],\n" + + f" {fade1}, 0\n" + + " ]\n" + + " }\n" + + " });\n" + + " map.addLayer({\n" + + " id: 'appellations-halo', type: 'line', source: 'appellations',\n" + + detail_meta + + halo_layout + + " paint: {\n" + + halo_color + + halo_width + + " 'line-opacity': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" {fade0}, 0,\n" + + f" {fade1}, ['case', {sel}, 0.85, 0]\n" + + " ]\n" + + " }\n" + + " });\n" + # ---- outlines ---- + # A slightly heavier outline on small appellations at the overview + # keeps them as crisp marks when the fill is sub-pixel; large-area + # outlines stay thin. From z10 the constant-ink clamp applies. + + " map.addLayer({\n" + + " id: 'appellations-outline-overview', type: 'line', source: 'appellations-overview',\n" + + over_meta + + outline_layout + + " paint: {\n" + + outline_color + + " 'line-width': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" 3, {width(3, 1.4, 0.3)},\n" + + f" 8, {width(8, 1.2, 0.3)},\n" + + f" 10, {width(10, 1.2, 0.3, 0.25)},\n" + + f" 12, {width(12, 1.2, 0.3, 0.5)}\n" + + " ],\n" + + " 'line-opacity': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" 3, {line_op_over_low},\n" + + f" 6.5, {line_op_over_low},\n" + + f" 8, {line_op_over},\n" + + f" {fade0}, {line_op_over},\n" + + f" {fade1}, 0\n" + + " ]\n" + + " }\n" + + " });\n" + + " map.addLayer({\n" + + " id: 'appellations-outline', type: 'line', source: 'appellations',\n" + + detail_meta + + outline_layout + + " paint: {\n" + + outline_color + + " 'line-width': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + # The floors keep a small compact climat visible (constant ink alone + # would give a 1.8 ha grand cru a hairline at z12); Saint-Bris sits + # above them (0.4 px at z11, 0.8 px at z12) so they never bind for the + # fragmented records the clamp exists for. + + f" {fade0}, {width(fade0, 1.2, 0.3, 0.35)},\n" + + f" 12, {width(12, 1.2, 0.3, 0.5)},\n" + + f" 14, {width(14, 1.4, 0.4, 0.7)}\n" + + " ],\n" + + " 'line-opacity': [\n" + + " 'interpolate', ['linear'], ['zoom'],\n" + + f" {fade0}, 0,\n" + + f" {fade1}, 1.0\n" + + " ]\n" + + " }\n" + + " });\n" ) @@ -1543,7 +1702,7 @@ def _country_name(cc: str) -> str: def render( *, layer_url: str, - villages_layer_url: str, + overview_layer_url: str, source_type: str, aocs: dict, facet_styles_tree: list[dict], @@ -1567,6 +1726,7 @@ def render( entity_out_dir=None, children_map: dict[str, list[str]] | None = None, build_date: str = "", + lod: dict | None = None, ) -> tuple[str, str, bytes, dict[str, str]]: """Render the full map page (index.html) for one locale. @@ -1595,10 +1755,11 @@ def render( area_q1, area_q3 = area_quartiles source_block = _build_source_block( layer_url=layer_url, - villages_layer_url=villages_layer_url, + overview_layer_url=overview_layer_url, source_type=source_type, area_q1=area_q1, area_q3=area_q3, + lod=lod, ) simple_style_labels = { @@ -1821,6 +1982,7 @@ def _merged_facet(field: str) -> list[tuple[str, int]]: country_flag_emoji_json=json.dumps(_COUNTRY_FLAG_EMOJI, ensure_ascii=False), carto_key_json=json.dumps(carto_basemap_key()), carto_keys_json=json.dumps(carto_basemap_keys(), sort_keys=True), + lod_json=json.dumps(lod or {}, sort_keys=True), ) style_body = _STYLE_CSS.replace("{{", "{").replace("}}", "}") @@ -1865,6 +2027,7 @@ def _fill(**per_page) -> str: browse_path=browse_path, corpus_counts=corpus_counts, locale=locale, + lod=lod, data_updated_html=( labels["about_updated_html"].format(date=esc(build_date)) if build_date else "" ), @@ -1897,10 +2060,10 @@ def _fill(**per_page) -> str: country_labels=country_labels, country_flag_emoji=_COUNTRY_FLAG_EMOJI, grapes_info=grapes_info or {}, styles_info=styles_info or {}, style_labels=style_labels, github_new_issue_url=_GITHUB_NEW_ISSUE_URL, - terms_info=terms_info or {}, + terms_info=terms_info or {}, lod=lod or {}, ) entity_about_html = _build_about_dialog( - labels, browse_path=browse_path, corpus_counts=corpus_counts, locale=locale + labels, browse_path=browse_path, corpus_counts=corpus_counts, locale=locale, lod=lod ) def _emit(slug: str, meta: dict, ssr: str, has_card: bool) -> None: diff --git a/scripts/_lib/vineyard_envelope.py b/scripts/_lib/vineyard_envelope.py new file mode 100644 index 0000000..a403de9 --- /dev/null +++ b/scripts/_lib/vineyard_envelope.py @@ -0,0 +1,241 @@ +"""Generalised vineyard footprint of an INAO parcellaire — the low-zoom +geometry of a French appellation. + +Below the detail zoom the map draws not the parcel delimitation itself but a +*morphological closing* of it: dilate by ``ENVELOPE_RADIUS_M``, erode by the +same amount, simplify. Parcels closer than twice the radius merge into one +silhouette, holes narrower than that fill, and the result stays inside the +dilation of the parcels — so it is a generalisation of the regulator's +delimitation, never a different area. It replaces the commune union that the +default view used to draw for these records (5–8× the vineyard, water and +forest included) with a shape ~1.3× the vineyard that follows it. + +Cost is kept linear by decomposing the parcellaire into connected components +first: two parts can only interact in a closing when they are within ``2r`` +of each other, so grouping parts whose ``r``-dilated bounding boxes touch +(an STRtree query plus union-find) is a conservative, exact decomposition and +each component closes on its own. Languedoc (31,488 parts) goes from 832 s +monolithic to ~20 s. + +Results are cached on disk under ``raw/cache/vineyard-envelopes``, keyed by a +digest of the *input* geometry (plus the parameter version), so the cache is a +pure function of the INAO data: a fresh checkout rebuilds it, a new +parcellaire release invalidates exactly the records that changed, and records +sharing one polygon (Banyuls / Banyuls Grand Cru / Collioure) close once. +""" + +from __future__ import annotations + +import hashlib +import math +import sys +import time +from pathlib import Path + +import numpy as np +import shapely +from pyproj import Transformer +from shapely.geometry.base import BaseGeometry +from shapely.ops import transform as shp_transform + +ROOT = Path(__file__).resolve().parents[2] + +# Closing radius in metres. 250 m keeps the shape of the vineyard (Saint-Bris' +# three lobes, Sancerre's detached south-western parcels) at a median area +# inflation of 1.33× over the parcels; 500 m already turns Saint-Bris into a +# ring around its village. Rivers narrower than 2r between parcels on both +# banks are bridged — see scripts/audit_vineyard_envelopes.py --water. +ENVELOPE_RADIUS_M = 250 +# Douglas-Peucker tolerance applied to the closed shape (metres). The input +# is already tile-simplified (~22 m); 30 m removes the buffer arcs' vertices. +ENVELOPE_SIMPLIFY_M = 30 +# Segments per quarter circle for the buffers. 3 is enough for a shape that +# is simplified afterwards; more only adds vertices to erode again. +ENVELOPE_QUAD_SEGS = 3 +# Bump whenever radius / simplify / quad_segs / the algorithm change: it is +# part of the cache key, so stale envelopes can never be served. +ENVELOPE_VERSION = 2 + +# Countries whose parcel-level delimitation gets a footprint. Only France +# publishes one (INAO parcellaire); the rule is on the record's country AND +# its geometry provenance (see `is_envelope_source`). +ENVELOPE_COUNTRIES = frozenset({"fr"}) +# Detail-geometry provenances that are parcel-level. A record inheriting one +# of these (parent-appellation / sibling-dgc) is decided by its donor. +ENVELOPE_SOURCES = frozenset({"parcellaire", "parcellaire-dgc"}) +# The value written to the overview feature's `geom_source`. +ENVELOPE_GEOM_SOURCE = "parcellaire-envelope" +# Zoom bands, shared with the map template and app.js (through the build +# tokens) so Python and JS never disagree about where the footprint stops. +LOD_DETAIL_MIN_ZOOM = 11 # parcel layers exist from this zoom +LOD_CROSSFADE = (11.0, 11.9) # parcels fade in / footprint fades out +# The overview layers stop at the end of the crossfade (their maxzoom), so +# what the card says ("footprint") and what is drawn agree to the pixel. +LOD_FOOTPRINT_MAX_ZOOM = LOD_CROSSFADE[1] +# Single-record fits stop here: from this zoom only the parcels are drawn. +LOD_OVERVIEW_MAX_ZOOM = 12 + +CACHE_DIR = ROOT / "raw" / "cache" / "vineyard-envelopes" + +_TO_3035 = Transformer.from_crs("EPSG:4326", "EPSG:3035", always_xy=True).transform +_TO_4326 = Transformer.from_crs("EPSG:3035", "EPSG:4326", always_xy=True).transform + + +def containment_tolerance_m(radius_m: float = ENVELOPE_RADIUS_M) -> float: + """How far the footprint may fall *inside* its parcels. A polygonal buffer + under-covers a corner by the chord sagitta r·(1 − cos(π/(4·quad_segs))) + (≈ 8.5 m at 250 m / 3 segments), and the final simplify may move an edge + by ENVELOPE_SIMPLIFY_M. Everything else is contained exactly: a closing + never leaves the r-dilation of its input.""" + sagitta = radius_m * (1.0 - math.cos(math.pi / (4 * ENVELOPE_QUAD_SEGS))) + return sagitta + ENVELOPE_SIMPLIFY_M + + +def is_envelope_source(country: str | None, geom_source: str | None) -> bool: + return (country or "fr") in ENVELOPE_COUNTRIES and geom_source in ENVELOPE_SOURCES + + +def cache_key(geom: BaseGeometry, radius_m: int = ENVELOPE_RADIUS_M) -> str: + h = hashlib.blake2b(digest_size=16) + h.update(f"v{ENVELOPE_VERSION}-r{radius_m}-s{ENVELOPE_SIMPLIFY_M}-q{ENVELOPE_QUAD_SEGS}|".encode()) + h.update(shapely.to_wkb(geom, flavor="iso")) + return h.hexdigest() + + +def _cache_path(key: str, radius_m: int) -> Path: + return CACHE_DIR / f"v{ENVELOPE_VERSION}-r{radius_m}" / key[:2] / f"{key}.wkb" + + +def _components(parts: list[BaseGeometry], r: float) -> list[list[int]]: + """Group parts whose r-grown bounding boxes intersect (union-find over an + STRtree query). Two parts can only touch in a closing when their + r-dilations meet, i.e. when they are within 2r; a bbox grown by r on every + side contains the part's r-dilation (a rounded buffer of the box would + under-cover the corners), so the grouping is a conservative superset and + the decomposition is exact for the closing that follows.""" + n = len(parts) + if n <= 1: + return [list(range(n))] + boxes = np.array([shapely.box(b[0] - r, b[1] - r, b[2] + r, b[3] + r) for b in (p.bounds for p in parts)]) + tree = shapely.STRtree(boxes) + left, right = tree.query(boxes, predicate="intersects") + parent = list(range(n)) + + def find(i: int) -> int: + while parent[i] != i: + parent[i] = parent[parent[i]] + i = parent[i] + return i + + for a, b in zip(left.tolist(), right.tolist()): + if a == b: + continue + ra, rb = find(a), find(b) + if ra != rb: + parent[rb] = ra + groups: dict[int, list[int]] = {} + for i in range(n): + groups.setdefault(find(i), []).append(i) + return list(groups.values()) + + +def _close(geom_3035: BaseGeometry, r: float) -> BaseGeometry: + qs = ENVELOPE_QUAD_SEGS + out = geom_3035.buffer(r, quad_segs=qs).buffer(-r, quad_segs=qs) + out = out.simplify(ENVELOPE_SIMPLIFY_M, preserve_topology=True) + return shapely.make_valid(out) + + +def envelope_3035(geom_3035: BaseGeometry, radius_m: float = ENVELOPE_RADIUS_M) -> BaseGeometry: + """Morphological closing of a (multi)polygon already in EPSG:3035.""" + if geom_3035.is_empty: + return geom_3035 + parts = list(geom_3035.geoms) if geom_3035.geom_type == "MultiPolygon" else [geom_3035] + pieces = [] + for idx in _components(parts, radius_m): + comp = shapely.union_all([parts[i] for i in idx]) if len(idx) > 1 else parts[idx[0]] + closed = _close(comp, radius_m) + if not closed.is_empty: + pieces.append(closed) + if not pieces: + return geom_3035 + out = shapely.union_all(pieces) if len(pieces) > 1 else pieces[0] + return shapely.make_valid(out) + + +def envelope( + geom_4326: BaseGeometry, + radius_m: int = ENVELOPE_RADIUS_M, + *, + use_cache: bool = True, + stats: dict | None = None, +) -> BaseGeometry: + """Footprint of a WGS84 (multi)polygon, cached on disk by input digest. + + `stats`, when given, is incremented in place: `hits`, `misses`, `secs`. + Never returns an empty geometry for a non-empty input: on the (theoretical) + degenerate case the input itself is returned, so a record can never vanish + from the overview layer. + """ + if geom_4326 is None or geom_4326.is_empty: + return geom_4326 + key = cache_key(geom_4326, radius_m) + path = _cache_path(key, radius_m) + if use_cache and path.exists(): + try: + out = shapely.from_wkb(path.read_bytes()) + if not out.is_empty: + if stats is not None: + stats["hits"] = stats.get("hits", 0) + 1 + return out + except Exception as exc: # a truncated file is just a miss + print(f"[envelope] unreadable cache {path.name}: {exc}", file=sys.stderr) + t0 = time.perf_counter() + g3035 = shp_transform(_TO_3035, geom_4326) + closed = envelope_3035(g3035, radius_m) + out = shp_transform(_TO_4326, closed) if not closed.is_empty else geom_4326 + if out.is_empty: + out = geom_4326 + if stats is not None: + stats["misses"] = stats.get("misses", 0) + 1 + stats["secs"] = stats.get("secs", 0.0) + (time.perf_counter() - t0) + if use_cache: + try: + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(".tmp") + tmp.write_bytes(shapely.to_wkb(out, flavor="iso")) + tmp.replace(path) + except OSError as exc: + print(f"[envelope] cache write failed {path.name}: {exc}", file=sys.stderr) + return out + + +def shape_metrics(geom_4326: BaseGeometry) -> tuple[int, float]: + """`(parts, frag)` for the outline paint: the number of polygon parts and + the perimeter/area ratio in m⁻¹ — ink per fill for a given stroke width. + An equirectangular rescale at the polygon's latitude is accurate to ~1 % + across the corpus and avoids reprojecting every polygon.""" + if geom_4326 is None or geom_4326.is_empty: + return 0, 0.0 + parts = len(geom_4326.geoms) if geom_4326.geom_type == "MultiPolygon" else 1 + lat = float(geom_4326.centroid.y) if not geom_4326.centroid.is_empty else 46.0 + kx = float(np.cos(np.radians(lat))) + local = shapely.transform(geom_4326, lambda c: c * np.array([kx, 1.0])) + area_deg2 = float(local.area) + if area_deg2 <= 0: + return parts, 0.0 + frag = float(local.length) / (area_deg2 * 111_320.0) + return parts, frag + + +def lod_config() -> dict: + """The LOD constants the client needs (rendered into app.js as one JSON).""" + return { + "radius_m": ENVELOPE_RADIUS_M, + "overview_max_zoom": LOD_OVERVIEW_MAX_ZOOM, + "footprint_max_zoom": LOD_FOOTPRINT_MAX_ZOOM, + "detail_min_zoom": LOD_DETAIL_MIN_ZOOM, + "crossfade": list(LOD_CROSSFADE), + "countries": sorted(ENVELOPE_COUNTRIES), + "sources": sorted(ENVELOPE_SOURCES), + } diff --git a/scripts/audit_vineyard_envelopes.py b/scripts/audit_vineyard_envelopes.py new file mode 100644 index 0000000..7529f3d --- /dev/null +++ b/scripts/audit_vineyard_envelopes.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 +"""Audit the generalised vineyard footprints the map draws below the detail +zoom (scripts/_lib/vineyard_envelope.py) against the parcels they generalise. + +Reads the two built GeoJSONs — `wiki/map-data/appellations.geojson` (the +parcels) and `wiki/map-data/appellations-overview.geojson` (what is drawn +below the detail zoom) — and checks, per French parcel-level record: + + parity the record exists in both zoom bands (else it vanishes on one + side of the detail zoom, silently); + containment the parcels lie inside the footprint (a closing only ever adds + area; the corner shave of a polygonal buffer is bounded by + `containment_tolerance_m`) — reported as km² of parcels outside; + bbox the footprint's bbox stays within the parcels' bbox + radius; + inflation footprint area / parcel area (the corpus maximum at 250 m is + ~2.4×; more means something wide was bridged); + bridging how much of the AREA THE FOOTPRINT ADDS (footprint − parcels) + lies on another appellation's parcels. Umbrella appellations + whose parcels contain this record's (Bourgogne over Chablis, + Haut-Médoc over Pauillac — ≥ 90 % of the record's parcels) and + the record's own parent / children are not "other": their + ground is legitimately shared. What remains is a real bridge + across a neighbour, worst first. + --water km² of IGN BD TOPO `surface_hydrographique` (every nature, + including `Ecoulement naturel` — rivers) inside the added area, + fetched per record from the Géoplateforme WFS and cached under + raw/ign/bdtopo-hydro/. The closing bridges any water narrower + than 2r between parcels on both banks; this is the measurement. + +Read-only. `--strict` exits non-zero on a parity / containment / bbox failure +or a bridge above `--bridge-max`. The build already gates parity and bbox +(stage 04); this audit is the full picture, run it after every stage-04 build +that touches the geometry. + + .venv/bin/python scripts/audit_vineyard_envelopes.py + .venv/bin/python scripts/audit_vineyard_envelopes.py --water --top 30 + .venv/bin/python scripts/audit_vineyard_envelopes.py --strict --json tmp/envelopes.json +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import sys +import time +import urllib.parse +import urllib.request +from pathlib import Path + +import shapely +from pyproj import Transformer +from shapely.geometry import shape +from shapely.ops import transform as shp_transform +from shapely.strtree import STRtree + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from _lib.vineyard_envelope import ( # noqa: E402 + ENVELOPE_GEOM_SOURCE, + ENVELOPE_RADIUS_M, + ENVELOPE_SIMPLIFY_M, + containment_tolerance_m, +) +from audit_geometry_outliers import stream_features # noqa: E402 + +ROOT = Path(__file__).resolve().parent.parent +DETAIL_GEOJSON = ROOT / "wiki" / "map-data" / "appellations.geojson" +OVERVIEW_GEOJSON = ROOT / "wiki" / "map-data" / "appellations-overview.geojson" +HYDRO_CACHE = ROOT / "raw" / "ign" / "bdtopo-hydro" + +WFS = "https://data.geopf.fr/wfs/ows?" +WFS_LAYER = "BDTOPO_V3:surface_hydrographique" +UA = {"User-Agent": "open-wine-map/1.0 (winemap@devloed.com)"} + +_to_3035 = Transformer.from_crs("EPSG:4326", "EPSG:3035", always_xy=True).transform + + +def _km2(g) -> float: + return float(g.area) / 1e6 if g is not None and not g.is_empty else 0.0 + + +def _valid(g): + return g if g.is_valid else shapely.make_valid(g) + + +def _clip(g, bounds): + """clip_by_rect can emit self-touching rings; repair before any overlay.""" + return _valid(shapely.clip_by_rect(g, *bounds)) + + +def _inter_km2(a, b) -> float: + try: + return _km2(a.intersection(b)) + except shapely.errors.GEOSException: + return _km2(_valid(a).intersection(_valid(b))) + + +def _load_fr(path: Path, keep_sources: set[str] | None) -> dict[str, dict]: + out: dict[str, dict] = {} + for feat in stream_features(path): + p = feat.get("properties") or {} + if (p.get("country") or "fr") != "fr": + continue + if keep_sources is not None and p.get("geom_source") not in keep_sources: + continue + out[p["slug"]] = { + "props": p, + "geom": _valid(shp_transform(_to_3035, shape(feat["geometry"]))), + } + return out + + +def _water_for_bbox(bounds4326: tuple[float, float, float, float]) -> list[dict]: + """BD TOPO water surfaces intersecting a WGS84 bbox, cached by bbox digest.""" + key = hashlib.sha1(",".join(f"{v:.4f}" for v in bounds4326).encode()).hexdigest()[:16] + cache = HYDRO_CACHE / f"{key}.json" + if cache.exists(): + return json.loads(cache.read_text())["features"] + minx, miny, maxx, maxy = bounds4326 + feats: list[dict] = [] + start = 0 + while True: + q = { + "SERVICE": "WFS", "VERSION": "2.0.0", "REQUEST": "GetFeature", + "TYPENAME": WFS_LAYER, "OUTPUTFORMAT": "application/json", + "COUNT": "1000", "STARTINDEX": str(start), "SRSNAME": "EPSG:4326", + "CQL_FILTER": f"BBOX(geometrie,{minx},{miny},{maxx},{maxy},'EPSG:4326')", + } + req = urllib.request.Request(WFS + urllib.parse.urlencode(q), headers=UA) + for attempt in range(4): + try: + with urllib.request.urlopen(req, timeout=300) as resp: + d = json.load(resp) + break + except Exception as exc: # network hiccup: back off and retry + print(f"[water] retry {attempt} {exc!r}", file=sys.stderr) + time.sleep(5 * (attempt + 1)) + else: + raise SystemExit(f"[water] gave up on bbox {bounds4326}") + page = d.get("features", []) + feats += page + if len(page) < 1000: + break + start += 1000 + HYDRO_CACHE.mkdir(parents=True, exist_ok=True) + cache.write_text(json.dumps({"type": "FeatureCollection", "features": feats})) + return feats + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--detail", type=Path, default=DETAIL_GEOJSON) + ap.add_argument("--overview", type=Path, default=OVERVIEW_GEOJSON) + ap.add_argument("--water", action="store_true", help="also measure BD TOPO water inside the added area (network, cached)") + ap.add_argument("--only", action="append", default=[], help="slug substring filter (repeatable)") + ap.add_argument("--top", type=int, default=15, help="rows per worst-first table") + ap.add_argument("--bridge-max", type=float, default=0.05, help="--strict fails above this bridge share of the footprint") + ap.add_argument("--ratio-warn", type=float, default=3.0) + ap.add_argument("--json", type=Path, help="write every per-record row here") + ap.add_argument("--strict", action="store_true") + args = ap.parse_args() + + for path in (args.detail, args.overview): + if not path.exists(): + sys.exit(f"missing {path} — run scripts/04_build_maps.py") + + t0 = time.perf_counter() + detail = _load_fr(args.detail, None) + overview = _load_fr(args.overview, {ENVELOPE_GEOM_SOURCE}) + print( + f"[load] {len(detail)} FR detail polygons, {len(overview)} footprints " + f"({time.perf_counter() - t0:.0f} s)", + file=sys.stderr, + ) + only = [s.lower() for s in args.only] + slugs = [s for s in overview if not only or any(o in s for o in only)] + + # Parity: every French parcel-level record has a footprint and vice versa. + parcel_level = {s for s, d in detail.items() if d["props"].get("geom_source") in ("parcellaire", "parcellaire-dgc")} + no_footprint = sorted(parcel_level - set(overview)) + orphan = sorted(set(overview) - set(detail)) + + # Bridging index: every FR parcel-level record's parcels. + idx_slugs = sorted(parcel_level) + tree = STRtree([detail[s]["geom"] for s in idx_slugs]) + tol = containment_tolerance_m() + bbox_slack = ENVELOPE_RADIUS_M + ENVELOPE_SIMPLIFY_M + 5.0 + + rows: list[dict] = [] + for i, slug in enumerate(slugs): + env = overview[slug]["geom"] + parcels = detail[slug]["geom"] if slug in detail else None + if parcels is None: + continue + p = detail[slug]["props"] + outside = _km2(parcels.difference(_valid(env.buffer(tol)))) + eminx, eminy, emaxx, emaxy = env.bounds + pminx, pminy, pmaxx, pmaxy = parcels.bounds + bbox_ok = ( + eminx >= pminx - bbox_slack and eminy >= pminy - bbox_slack + and emaxx <= pmaxx + bbox_slack and emaxy <= pmaxy + bbox_slack + ) + added = _valid(env.difference(parcels)) + added_km2 = _km2(added) + ratio = env.area / parcels.area if parcels.area > 0 else 1.0 + # Bridging onto other appellations' parcels. + bridges: list[tuple[str, float]] = [] + if added_km2 > 0: + for j in tree.query(added, predicate="intersects").tolist(): + other = idx_slugs[j] + if other == slug: + continue + op = detail[other]["props"] + if op.get("parent_slug") == slug or p.get("parent_slug") == other: + continue + og = detail[other]["geom"] + clipped = _clip(og, added.bounds) + if clipped.is_empty: + continue + # An umbrella whose parcels contain most of this record's is + # shared ground (regional over village), not a bridge. + shared = _inter_km2(_clip(og, parcels.bounds), parcels) + if parcels.area > 0 and shared / _km2(parcels) >= 0.9: + continue + b = _inter_km2(clipped, added) + if b > 0.005: + bridges.append((other, b)) + bridges.sort(key=lambda t: -t[1]) + bridge_km2 = sum(b for _, b in bridges) + row = { + "slug": slug, + "parts": p.get("parts"), + "parcels_km2": round(_km2(parcels), 3), + "footprint_km2": round(_km2(env), 3), + "ratio": round(ratio, 3), + "outside_km2": round(outside, 4), + "bbox_ok": bbox_ok, + "added_km2": round(added_km2, 3), + "bridge_km2": round(bridge_km2, 3), + "bridge_share": round(bridge_km2 / _km2(env), 4) if env.area > 0 else 0.0, + "bridges": [(o, round(b, 3)) for o, b in bridges[:5]], + } + if args.water and added_km2 > 0: + env4326 = shape(json.loads(shapely.to_geojson(shp_transform( + Transformer.from_crs("EPSG:3035", "EPSG:4326", always_xy=True).transform, env)))) + water_feats = _water_for_bbox(env4326.bounds) + by_nature: dict[str, float] = {} + for wf in water_feats: + try: + wg = shp_transform(_to_3035, shape(wf["geometry"])) + except Exception: + continue + if not wg.intersects(added): + continue + nature = (wf.get("properties") or {}).get("nature") or "?" + by_nature[nature] = by_nature.get(nature, 0.0) + _inter_km2(_valid(wg), added) + row["water_km2"] = round(sum(by_nature.values()), 3) + row["water_by_nature"] = {k: round(v, 3) for k, v in sorted(by_nature.items(), key=lambda kv: -kv[1]) if v > 0.001} + rows.append(row) + if (i + 1) % 100 == 0: + print(f"[audit] {i + 1}/{len(slugs)} ({time.perf_counter() - t0:.0f} s)", file=sys.stderr) + + # ---- report ---- + print(f"\n# Vineyard footprints — r = {ENVELOPE_RADIUS_M} m, {len(rows)} records audited\n") + print(f"parity: {len(no_footprint)} parcel-level record(s) without a footprint {no_footprint[:10]}; " + f"{len(orphan)} footprint(s) without a detail record {orphan[:10]}") + bad_contain = [r for r in rows if r["outside_km2"] > 0.001] + bad_bbox = [r for r in rows if not r["bbox_ok"]] + print(f"containment: {len(bad_contain)} record(s) with parcels outside the footprint " + f"(tolerance {tol:.0f} m)" + (f": {[(r['slug'], r['outside_km2']) for r in bad_contain[:10]]}" if bad_contain else "")) + print(f"bbox: {len(bad_bbox)} record(s) leave their parcels' bbox + {bbox_slack:.0f} m" + + (f": {[r['slug'] for r in bad_bbox[:10]]}" if bad_bbox else "")) + ratios = sorted(r["ratio"] for r in rows) + if ratios: + print(f"inflation (footprint / parcels): median {ratios[len(ratios)//2]:.2f}× · " + f"p90 {ratios[int(len(ratios)*0.9)]:.2f}× · max {ratios[-1]:.2f}× · " + f"{sum(1 for x in ratios if x > args.ratio_warn)} above {args.ratio_warn}×") + + def table(title: str, key: str, fmt, n: int) -> None: + print(f"\n## {title}\n") + print("| slug | parts | parcels km² | footprint km² | ratio | " + fmt[0] + " |") + print("|---|---:|---:|---:|---:|---|") + for r in sorted(rows, key=lambda r: -(r.get(key) or 0))[:n]: + print(f"| {r['slug']} | {r['parts']} | {r['parcels_km2']:.1f} | {r['footprint_km2']:.1f} | {r['ratio']:.2f} | {fmt[1](r)} |") + + table("Largest inflation", "ratio", ("added km²", lambda r: f"{r['added_km2']:.1f}"), args.top) + table( + "Worst bridging onto a neighbour's parcels (share of the footprint)", + "bridge_share", + ("bridge km² · share · onto", lambda r: f"{r['bridge_km2']:.2f} · {100*r['bridge_share']:.1f} % · " + ", ".join(f"{o} {b:.2f}" for o, b in r["bridges"][:3])), + args.top, + ) + if args.water: + table( + "Most water in the added area (BD TOPO surface_hydrographique)", + "water_km2", + ("water km² · by nature", lambda r: f"{r.get('water_km2', 0):.2f} · " + ", ".join(f"{k} {v:.2f}" for k, v in (r.get("water_by_nature") or {}).items())), + args.top, + ) + wtotal = sum(r.get("water_km2", 0.0) for r in rows) + print(f"\nwater in added area, corpus total: {wtotal:.1f} km² over " + f"{sum(1 for r in rows if r.get('water_km2', 0) > 0.05)} record(s) with > 0.05 km²") + + if args.json: + args.json.parent.mkdir(parents=True, exist_ok=True) + args.json.write_text(json.dumps(rows, ensure_ascii=False, indent=1)) + print(f"\nrows → {args.json}") + + over_bridge = [r for r in rows if r["bridge_share"] > args.bridge_max] + failed = bool(no_footprint or orphan or bad_contain or bad_bbox or over_bridge) + if args.strict and failed: + print(f"\nSTRICT: {len(no_footprint)} missing, {len(orphan)} orphan, {len(bad_contain)} containment, " + f"{len(bad_bbox)} bbox, {len(over_bridge)} bridge > {args.bridge_max:.0%}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_content_block.py b/tests/test_content_block.py index 540b6fb..fe37802 100644 --- a/tests/test_content_block.py +++ b/tests/test_content_block.py @@ -29,6 +29,14 @@ "geom_approx_aires": "Approx area — commune envelope.", "geom_approx_cadastre": "From lieu-dit «{lieu_dit}» (commune {commune}, {source}).", "geom_approx_cadastre_source_label": "cadastre.data.gouv.fr", + "geom_approx_pdo_plan": "Approx area — reconstructed from the plan.", + "geom_approx_aires_union": "Approx area — union of the {n} commune(s), water included.", + "geom_lod_footprint_static": "At overview zooms drawn as a footprint generalised at {radius} m.", + "geom_src_geoportal": "Official production zone from the geoportal ({region}).", + "geom_src_mapa": "Official MAPA production zone.", + "geom_src_betard": "Approx area — Bétard 2022 polygon, municipality resolution.", + "geom_src_pdo_union": "Approx area — union of the member PDO polygons.", + "geom_src_admin_union": "Approx area — union of the named administrative units.", "stub_message": "No {doc} found yet.", "stub_help_label": "help us find it", "panel_styles_h": "Styles", @@ -101,12 +109,17 @@ "pt": "🇵🇹", "es": "🇪🇸", "gr": "🇬🇷"} -def _ctx(locale: str = "fr") -> RenderCtx: +_LOD = {"radius_m": 250, "overview_max_zoom": 12, "countries": ["fr"], + "sources": ["parcellaire", "parcellaire-dgc"]} + + +def _ctx(locale: str = "fr", lod: dict | None = None) -> RenderCtx: return RenderCtx( locale=locale, labels=_LABELS, region_labels={"BORDEAUX": "Bordeaux"}, country_labels=_COUNTRY_LABELS, country_flag_emoji=_COUNTRY_FLAG, grapes_info=_GRAPES_INFO, styles_info=_STYLES_INFO, style_labels=_STYLE_LABELS, github_new_issue_url="https://github.com/x/y/issues/new", + lod=lod or {}, ) @@ -378,3 +391,67 @@ def test_fr_source_marker_only_off_locale() -> None: rec = {"name": "M", "kind": "AOC", "country": "fr", "summary": "Texte."} assert "(French)" not in _render(rec, locale="fr") # native locale: no marker assert "(French)" in _render(rec, locale="en") # off-locale: marker shown + + +# ---- geometry provenance lines (the approx-line chain, mirrored in app.js) ---- + +def _geom_rec(**over) -> dict: + rec = {"name": "X", "country": "fr", "kind": "AOC", "region": "BORDEAUX", + "styles": ["red"], "grapes_principal": ["merlot"]} + rec.update(over) + return rec + + +def test_commune_union_line_names_count_and_water() -> None: + html = _render(_geom_rec(geom_source="aires-csv", communes_matched=7)) + assert "union of the 7 commune(s), water included" in html + html = _render(_geom_rec(geom_source="dgc-village-override", communes_matched=1)) + assert "union of the 1 commune(s)" in html + html = _render(_geom_rec(geom_source="communes", communes_matched=3)) + assert "union of the 3 commune(s)" in html + + +def test_parcellaire_has_no_approx_line_without_lod() -> None: + html = _render(_geom_rec(geom_source="parcellaire")) + assert "approx-line" not in html + + +def test_footprint_line_only_for_parcel_level_french_records_when_lod_set() -> None: + ctx = _ctx(lod=_LOD) + html = render_content_block(_geom_rec(geom_source="parcellaire"), "x", ctx) + assert '
' in html + assert "generalised at 250 m" in html + html = render_content_block(_geom_rec(geom_source="parcellaire-dgc"), "x", ctx) + assert "lod-line" in html + # Not for a commune union, not for another country, not without config. + assert "lod-line" not in render_content_block(_geom_rec(geom_source="aires-csv"), "x", ctx) + assert "lod-line" not in render_content_block( + _geom_rec(geom_source="parcellaire", country="es"), "x", ctx + ) + assert "lod-line" not in _render(_geom_rec(geom_source="parcellaire")) + + +def test_zone_source_lines_for_other_countries() -> None: + html = _render(_geom_rec(country="it", geom_source="geoportal-zone:piemonte")) + assert "geoportal (Piemonte)" in html + assert "<" not in html.split('approx-line">')[1].split("
")[0] + html = _render(_geom_rec(country="it", geom_source="geoportal-zone:lazio+umbria")) + assert "geoportal (Lazio + Umbria)" in html + html = _render(_geom_rec(country="ch", geom_source="geoportal-canton:ge")) + assert "geoportal (GE)" in html + assert "Official MAPA production zone" in _render(_geom_rec(country="es", geom_source="mapa-zone")) + assert "Bétard 2022 polygon" in _render(_geom_rec(country="gr", geom_source="figshare-pdo")) + assert "Bétard 2022 polygon" in _render(_geom_rec(country="sk", geom_source="figshare-pdo-alias")) + assert "member PDO polygons" in _render(_geom_rec(country="si", geom_source="region-pdo-union")) + for gs in ("gisco-commune-list", "caop-concelho-union", "swissboundaries-canton-union", + "ons-country", "nuts2-province", "geometry-research-municipios"): + assert "named administrative units" in _render(_geom_rec(country="pt", geom_source=gs)), gs + # Parcel-precise or unknown sources say nothing (sigpac-hybrid-pliego is + # what stage 04 writes for the SIGPAC-resolved Priorat / Montsant records). + assert "approx-line" not in _render(_geom_rec(country="es", geom_source="sigpac-hybrid-pliego")) + assert "approx-line" not in _render(_geom_rec(country="lu", geom_source="ivv-commune-vineyard")) + + +def test_pdo_plan_line() -> None: + html = _render(_geom_rec(country="gb", geom_source="pdo-plan-parcel-hull-approx")) + assert "reconstructed from the plan" in html diff --git a/tests/test_vineyard_envelope.py b/tests/test_vineyard_envelope.py new file mode 100644 index 0000000..b372af0 --- /dev/null +++ b/tests/test_vineyard_envelope.py @@ -0,0 +1,147 @@ +"""The generalised vineyard footprint (scripts/_lib/vineyard_envelope.py). + +Pins the properties the map relies on: parts closer than 2r merge and parts +farther apart stay separate; the footprint contains the input and never +leaves its r-dilation; the component decomposition gives the same answer as +the monolithic closing; the disk cache is keyed on the input geometry and +round-trips; the shape metrics distinguish a fragmented record from a compact +one of the same area. +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest +import shapely +from shapely.geometry import MultiPolygon, box +from shapely.ops import transform as shp_transform + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) + +from _lib import vineyard_envelope as ve # noqa: E402 + + +def _to4326(g): + return shp_transform(ve._TO_4326, g) + + +def _to3035(g): + return shp_transform(ve._TO_3035, g) + + +# Two 400 m squares in EPSG:3035 (somewhere in Burgundy), `gap` metres apart. +def _two_squares(gap: float) -> MultiPolygon: + x0, y0 = 3_842_000.0, 2_756_000.0 + a = box(x0, y0, x0 + 400, y0 + 400) + b = box(x0 + 400 + gap, y0, x0 + 800 + gap, y0 + 400) + return MultiPolygon([a, b]) + + +def test_parts_within_2r_merge_and_farther_stay_apart() -> None: + near = ve.envelope_3035(_two_squares(300), 250) + far = ve.envelope_3035(_two_squares(700), 250) + assert near.geom_type == "Polygon" + assert far.geom_type == "MultiPolygon" and len(far.geoms) == 2 + + +def test_diagonal_neighbours_within_2r_share_a_component() -> None: + # Offset (280, 280) m: centre-to-corner distance 396 m < 2r = 500 m, but a + # rounded r-buffer of the bounding box would miss the corner (r·cos 45°). + x0, y0 = 3_842_000.0, 2_756_000.0 + a = box(x0, y0, x0 + 400, y0 + 400) + b = box(x0 + 400 + 280, y0 + 400 + 280, x0 + 800 + 280, y0 + 800 + 280) + groups = ve._components([a, b], 250) + assert len(groups) == 1 + # Whether the neck survives the erosion is the closing's business; the + # decomposition must give the same answer as the monolithic closing. + env = ve.envelope_3035(MultiPolygon([a, b]), 250) + mono = shapely.make_valid( + MultiPolygon([a, b]).buffer(250, quad_segs=3).buffer(-250, quad_segs=3) + .simplify(ve.ENVELOPE_SIMPLIFY_M, preserve_topology=True) + ) + assert env.symmetric_difference(mono).area / mono.area < 1e-3 + + +def test_footprint_contains_input_and_stays_inside_dilation() -> None: + g = _two_squares(300) + env = ve.envelope_3035(g, 250) + tol = ve.containment_tolerance_m(250) + assert tol < 40 + assert env.buffer(tol).contains(g) + assert g.buffer(250 + ve.ENVELOPE_SIMPLIFY_M + 1).contains(env) + # The bridged gap is the only area gained: 300 m × 400 m at most. + assert env.area - g.area <= 300 * 400 + 1e-6 + + +def test_component_decomposition_matches_monolithic_closing() -> None: + # Three clusters far apart, each with a bridgeable pair inside. + clusters = [] + for k in range(3): + for p in _two_squares(300).geoms: + clusters.append(shapely.affinity.translate(p, xoff=k * 5000)) + g = MultiPolygon(clusters) + groups = ve._components(list(g.geoms), 250) + assert sorted(len(x) for x in groups) == [2, 2, 2] + decomposed = ve.envelope_3035(g, 250) + monolithic = shapely.make_valid( + g.buffer(250, quad_segs=ve.ENVELOPE_QUAD_SEGS) + .buffer(-250, quad_segs=ve.ENVELOPE_QUAD_SEGS) + .simplify(ve.ENVELOPE_SIMPLIFY_M, preserve_topology=True) + ) + assert abs(decomposed.area - monolithic.area) / monolithic.area < 1e-3 + assert decomposed.symmetric_difference(monolithic).area / monolithic.area < 1e-2 + + +def test_cache_round_trip_is_keyed_on_input(tmp_path, monkeypatch) -> None: + monkeypatch.setattr(ve, "CACHE_DIR", tmp_path) + g = _to4326(_two_squares(300)) + stats: dict = {} + first = ve.envelope(g, stats=stats) + assert stats == pytest.approx({"misses": 1, "secs": stats["secs"]}) + second = ve.envelope(g, stats=stats) + assert stats["hits"] == 1 + assert first.equals_exact(second, 1e-12) + files = list(tmp_path.rglob("*.wkb")) + assert len(files) == 1 + # A different input → a different key, never the cached answer. + other = _to4326(_two_squares(700)) + ve.envelope(other, stats=stats) + assert stats["misses"] == 2 + assert len(list(tmp_path.rglob("*.wkb"))) == 2 + assert ve.cache_key(g) != ve.cache_key(other) + + +def test_envelope_never_empty_and_round_trips_projection(tmp_path, monkeypatch) -> None: + monkeypatch.setattr(ve, "CACHE_DIR", tmp_path) + g = _to4326(_two_squares(300)) + env = ve.envelope(g) + assert not env.is_empty + back = _to3035(env) + assert back.buffer(ve.containment_tolerance_m()).contains(_two_squares(300)) + + +def test_shape_metrics_separate_fragmented_from_compact() -> None: + compact = _to4326(box(3_842_000, 2_756_000, 3_842_000 + 3000, 2_756_000 + 3000)) + # Same total area, 36 separate 500 m squares. + cells = [ + box(3_842_000 + i * 1000, 2_756_000 + j * 1000, + 3_842_000 + i * 1000 + 500, 2_756_000 + j * 1000 + 500) + for i in range(6) for j in range(6) + ] + fragmented = _to4326(MultiPolygon(cells)) + p1, f1 = ve.shape_metrics(compact) + p2, f2 = ve.shape_metrics(fragmented) + assert (p1, p2) == (1, 36) + # Perimeter/area: 4·3000/9e6 vs 36·4·500/9e6 — six times the ink. + assert f1 == pytest.approx(4 * 3000 / 9e6, rel=0.03) + assert f2 == pytest.approx(36 * 4 * 500 / 9e6, rel=0.03) + + +def test_is_envelope_source() -> None: + assert ve.is_envelope_source("fr", "parcellaire") + assert ve.is_envelope_source("fr", "parcellaire-dgc") + assert not ve.is_envelope_source("fr", "aires-csv") + assert not ve.is_envelope_source("es", "parcellaire") + assert not ve.is_envelope_source("fr", "parent-appellation") From 265315a7e80f50261424c05bcc17bb8d5569203e Mon Sep 17 00:00:00 2001 From: Boris De Vloed Date: Wed, 23 Sep 2026 10:10:06 +0200 Subject: [PATCH 3/5] =?UTF-8?q?feat(search):=20one=20search=20box=20?= =?UTF-8?q?=E2=80=94=20drop=20the=20grape=20facet,=20"main=20grape=20only"?= =?UTF-8?q?=20becomes=20a=20chip=20toggle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sidebar's Grapes section (a second "Search grapes…" typeahead with its own chip tray, plus a permanent "main grape only" checkbox) is gone: the omnisearch is the only search box. A grape picked there appears as an active-filter chip as before, now followed by a chip-shaped "Main grape only" checkbox that narrows the filter to the principal roster; the toggle exists only while a grape chip does, is no longer persisted in localStorage, and clears with the last grape chip. Removes buildGrapeChipFilter and its helpers from app.js, the facet markup and its CSS from the template, and the `search_grape_placeholder` msgid. Also: audit_vineyard_envelopes reports bridging as the union of the neighbours' parcels inside the added area (overlapping neighbours were summed twice, giving shares above 100 %) and treats an appellation that covers half of the record's parcels as shared ground rather than a bridge. Co-Authored-By: Claude Fable 5.1 --- docs/analytics.md | 2 +- locale/en/LC_MESSAGES/messages.po | 463 ++++++++++++++-------------- locale/es/LC_MESSAGES/messages.po | 463 ++++++++++++++-------------- locale/fr/LC_MESSAGES/messages.po | 463 ++++++++++++++-------------- locale/messages.pot | 460 ++++++++++++++------------- locale/nl/LC_MESSAGES/messages.po | 463 ++++++++++++++-------------- scripts/_lib/assets/app.js | 226 ++------------ scripts/_lib/map_template.py | 53 +--- scripts/audit_vineyard_envelopes.py | 16 +- 9 files changed, 1199 insertions(+), 1410 deletions(-) diff --git a/docs/analytics.md b/docs/analytics.md index 227ef2e..88af724 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -58,7 +58,7 @@ option is inert, and a build with no `localhost` entry loads nothing there. | `Filters Reset` | `locale` | the reset button | | `Kind Toggled` | `kind` (`igp` / `spirits`), `enabled`, `via` (`reveal-hint`, optional), `locale` | the IGP / spirits switches | | `View Mode Switched` | `mode` (`simple` / `advanced`), `locale` | the mode toggle | -| `Grape Scope Toggled` | `scope` (`main` / `all`), `locale` | principal-only vs all grapes | +| `Grape Scope Toggled` | `scope` (`main` / `all`), `locale` | the "main grape only" checkbox that appears as a chip next to the grape chips once a grape is picked in the search box (2026-09-23; before that, a permanent checkbox in the Grapes facet). Not persisted: it ends with the last grape chip. | | `Omnisearch Used` | `result_count`, `had_match`, `groups` (a/g/r/s), `query_len`, `locale` | 1 s after typing stops in the omnisearch | | `Omnisearch Result Picked` | `type` (`grape` / `region` / `style` / `classification`), `locale` | a non-appellation suggestion is picked (appellation picks fire `Appellation Opened`) | | `Search Used` | `result_count`, `had_match`, `query_len`, `locale` | the legacy sidebar search (superseded by the omnisearch in June 2026) | diff --git a/locale/en/LC_MESSAGES/messages.po b/locale/en/LC_MESSAGES/messages.po index e36d7f7..a1ac85f 100644 --- a/locale/en/LC_MESSAGES/messages.po +++ b/locale/en/LC_MESSAGES/messages.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-23 08:12+0200\n" +"POT-Creation-Date: 2026-09-23 09:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -59,286 +59,282 @@ msgstr "appellation name…" msgid "Recherche d'appellation…" msgstr "Search appellations…" -#: scripts/_lib/map_template.py:56 -msgid "Recherche de cépage…" -msgstr "Search grapes…" - -#: scripts/_lib/map_template.py:58 +#: scripts/_lib/map_template.py:57 msgid "Rechercher une appellation, un cépage, une région…" msgstr "Search an appellation, grape or region…" -#: scripts/_lib/map_template.py:59 +#: scripts/_lib/map_template.py:58 msgid "Cépage principal uniquement" msgstr "Main grape only" -#: scripts/_lib/map_template.py:60 +#: scripts/_lib/map_template.py:59 #, python-brace-format msgid "Aucun résultat pour « {q} »" msgstr "No results for “{q}”" -#: scripts/_lib/map_template.py:61 +#: scripts/_lib/map_template.py:60 msgid "Options" msgstr "Options" -#: scripts/_lib/map_template.py:62 +#: scripts/_lib/map_template.py:61 msgid "Filtres actifs" msgstr "Active filters" -#: scripts/_lib/map_template.py:63 +#: scripts/_lib/map_template.py:62 msgid "Tout sélectionner" msgstr "Select all" -#: scripts/_lib/map_template.py:65 +#: scripts/_lib/map_template.py:64 #, python-brace-format msgid "{p} appellations · {s} dénominations géographiques complémentaires" msgstr "{p} appellations · {s} complementary geographic designations" -#: scripts/_lib/map_template.py:67 +#: scripts/_lib/map_template.py:66 #, python-brace-format msgid "{p} appellations" msgstr "{p} appellations" -#: scripts/_lib/map_template.py:68 +#: scripts/_lib/map_template.py:67 #, python-brace-format msgid "Ouvrir la fiche de {name}" msgstr "Open the {name} details" -#: scripts/_lib/map_template.py:69 +#: scripts/_lib/map_template.py:68 msgid "Ouvrir la fiche" msgstr "Open details" -#: scripts/_lib/map_template.py:70 +#: scripts/_lib/map_template.py:69 msgid "Inclure les spiritueux" msgstr "Include spirits" -#: scripts/_lib/map_template.py:71 +#: scripts/_lib/map_template.py:70 msgid "Style de vin" msgstr "Wine style" -#: scripts/_lib/map_template.py:72 +#: scripts/_lib/map_template.py:71 msgid "Classement" msgstr "Classification" -#: scripts/_lib/map_template.py:73 +#: scripts/_lib/map_template.py:72 msgid "Cépages principaux" msgstr "Principal grape varieties" -#: scripts/_lib/map_template.py:74 +#: scripts/_lib/map_template.py:73 msgid "Cépages accessoires" msgstr "Accessory grape varieties" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 -#: scripts/_lib/map_template.py:355 +#: scripts/_lib/map_template.py:74 scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:354 msgid "Cépages" msgstr "Grape varieties" -#: scripts/_lib/map_template.py:76 +#: scripts/_lib/map_template.py:75 msgid "Région" msgstr "Region" -#: scripts/_lib/map_template.py:77 +#: scripts/_lib/map_template.py:76 msgid "Appellation" msgstr "Appellation" -#: scripts/_lib/map_template.py:78 +#: scripts/_lib/map_template.py:77 msgid "Type" msgstr "Type" -#: scripts/_lib/map_template.py:79 +#: scripts/_lib/map_template.py:78 msgid "Origine protégée (AOP, AOC, DOC, DO…)" msgstr "Protected origin (PDO, AOC, DOC, DO…)" -#: scripts/_lib/map_template.py:80 +#: scripts/_lib/map_template.py:79 msgid "Indication géographique (IGP, IGT, Landwein…)" msgstr "Geographical indication (PGI, IGT, Landwein…)" -#: scripts/_lib/map_template.py:81 +#: scripts/_lib/map_template.py:80 msgid "AOP" msgstr "PDO" -#: scripts/_lib/map_template.py:82 +#: scripts/_lib/map_template.py:81 msgid "IGP" msgstr "PGI" -#: scripts/_lib/map_template.py:83 +#: scripts/_lib/map_template.py:82 msgid "IG spiritueux" msgstr "spirit-drink GI" -#: scripts/_lib/map_template.py:84 +#: scripts/_lib/map_template.py:83 msgid "Type d'appellation" msgstr "Appellation type" -#: scripts/_lib/map_template.py:85 +#: scripts/_lib/map_template.py:84 msgid "AOP (régime britannique)" msgstr "PDO (UK scheme)" -#: scripts/_lib/map_template.py:86 +#: scripts/_lib/map_template.py:85 msgid "IGP (régime britannique)" msgstr "PGI (UK scheme)" -#: scripts/_lib/map_template.py:87 +#: scripts/_lib/map_template.py:86 msgid "AOC (Suisse)" msgstr "AOC (Switzerland)" -#: scripts/_lib/map_template.py:88 +#: scripts/_lib/map_template.py:87 msgid "Source" msgstr "Source" -#: scripts/_lib/map_template.py:89 +#: scripts/_lib/map_template.py:88 msgid "Vue" msgstr "View" -#: scripts/_lib/map_template.py:90 +#: scripts/_lib/map_template.py:89 msgid "Simple" msgstr "Simple" -#: scripts/_lib/map_template.py:91 +#: scripts/_lib/map_template.py:90 msgid "Avancée" msgstr "Advanced" -#: scripts/_lib/map_template.py:92 +#: scripts/_lib/map_template.py:91 msgid "Thème" msgstr "Theme" -#: scripts/_lib/map_template.py:93 +#: scripts/_lib/map_template.py:92 msgid "Clair" msgstr "Light" -#: scripts/_lib/map_template.py:94 +#: scripts/_lib/map_template.py:93 msgid "Sombre" msgstr "Dark" -#: scripts/_lib/map_template.py:95 +#: scripts/_lib/map_template.py:94 msgid "Système" msgstr "System" -#: scripts/_lib/map_template.py:96 +#: scripts/_lib/map_template.py:95 msgid "Afficher les IGP" msgstr "Show PGIs" -#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:96 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:361 msgid "rouge" msgstr "red" -#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:362 msgid "blanc" msgstr "white" -#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:363 msgid "rosé" msgstr "rosé" -#: scripts/_lib/map_template.py:100 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:364 msgid "mousseux" msgstr "sparkling" -#: scripts/_lib/map_template.py:101 +#: scripts/_lib/map_template.py:100 msgid "moelleux / liquoreux" msgstr "sweet / dessert" -#: scripts/_lib/map_template.py:102 scripts/_lib/style_taxonomy.py:345 +#: scripts/_lib/map_template.py:101 scripts/_lib/style_taxonomy.py:345 msgid "oxydatif" msgstr "oxidative" -#: scripts/_lib/map_template.py:103 +#: scripts/_lib/map_template.py:102 msgid "autre" msgstr "other" -#: scripts/_lib/map_template.py:104 +#: scripts/_lib/map_template.py:103 msgid "Réinitialiser" msgstr "Reset" -#: scripts/_lib/map_template.py:105 +#: scripts/_lib/map_template.py:104 #, python-brace-format msgid "{n} appellations" msgstr "{n} appellations" -#: scripts/_lib/map_template.py:106 +#: scripts/_lib/map_template.py:105 #, python-brace-format msgid "{n} / {total} appellations" msgstr "{n} / {total} appellations" -#: scripts/_lib/map_template.py:107 +#: scripts/_lib/map_template.py:106 #, python-brace-format msgid "{n} masquées dans les IGP · afficher" msgstr "{n} hidden in PGIs · show" -#: scripts/_lib/map_template.py:108 +#: scripts/_lib/map_template.py:107 msgid "Fermer" msgstr "Close" -#: scripts/_lib/map_template.py:109 +#: scripts/_lib/map_template.py:108 msgid "Détails de l'appellation" msgstr "Appellation details" -#: scripts/_lib/map_template.py:110 +#: scripts/_lib/map_template.py:109 #, python-brace-format msgid "Retirer le filtre {label}" msgstr "Remove filter {label}" -#: scripts/_lib/map_template.py:111 +#: scripts/_lib/map_template.py:110 msgid "Filtres et options de la carte" msgstr "Filters and map options" -#: scripts/_lib/map_template.py:112 +#: scripts/_lib/map_template.py:111 msgid "Langue" msgstr "Language" -#: scripts/_lib/map_template.py:113 +#: scripts/_lib/map_template.py:112 msgid "Carte des appellations viticoles" msgstr "Map of wine appellations" -#: scripts/_lib/map_template.py:114 +#: scripts/_lib/map_template.py:113 msgid "Aller à la carte" msgstr "Skip to the map" -#: scripts/_lib/map_template.py:115 +#: scripts/_lib/map_template.py:114 msgid "Styles" msgstr "Styles" -#: scripts/_lib/map_template.py:116 +#: scripts/_lib/map_template.py:115 msgid "Variétés d'intérêt" msgstr "Varieties of interest" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 +#: scripts/_lib/map_template.py:116 scripts/_lib/map_template.py:357 msgid "Sources" msgstr "Sources" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:355 msgid "Terroir" msgstr "Terroir" -#: scripts/_lib/map_template.py:119 +#: scripts/_lib/map_template.py:118 #, python-brace-format msgid "Dűlők (lieux-dits) : {n}" msgstr "Dűlők (named vineyards): {n}" -#: scripts/_lib/map_template.py:120 +#: scripts/_lib/map_template.py:119 #, python-brace-format msgid "Menzioni geografiche aggiuntive (crus) : {n}" msgstr "Additional geographical mentions (crus): {n}" -#: scripts/_lib/map_template.py:121 +#: scripts/_lib/map_template.py:120 msgid "Facteurs naturels" msgstr "Natural factors" -#: scripts/_lib/map_template.py:122 +#: scripts/_lib/map_template.py:121 msgid "Facteurs humains" msgstr "Human factors" -#: scripts/_lib/map_template.py:123 +#: scripts/_lib/map_template.py:122 msgid "Caractéristiques du produit" msgstr "Product characteristics" -#: scripts/_lib/map_template.py:124 +#: scripts/_lib/map_template.py:123 msgid "Lien terroir / vin" msgstr "Terroir / wine link" -#: scripts/_lib/map_template.py:126 +#: scripts/_lib/map_template.py:125 #, python-brace-format msgid "" "Faits dégagés du Lien au terroir par interprétation automatique — voir la" @@ -347,26 +343,26 @@ msgstr "" "Facts drawn from the cahier's terroir-link section (Lien au terroir) by " "automatic interpretation — see the {source}." -#: scripts/_lib/map_template.py:128 +#: scripts/_lib/map_template.py:127 msgid "source" msgstr "source" -#: scripts/_lib/map_template.py:129 +#: scripts/_lib/map_template.py:128 msgid "via Wikipedia · CC BY-SA 4.0" msgstr "via Wikipedia · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:131 +#: scripts/_lib/map_template.py:130 #, python-brace-format msgid "Citation textuelle du Lien au terroir — voir la {source}." msgstr "" "Verbatim quote from the cahier's terroir-link section (Lien au terroir) —" " see the {source}." -#: scripts/_lib/map_template.py:133 +#: scripts/_lib/map_template.py:132 msgid "à vérifier — texte source court" msgstr "to verify — short source text" -#: scripts/_lib/map_template.py:135 +#: scripts/_lib/map_template.py:134 #, python-brace-format msgid "" "Ces repères décrivent l'appellation englobante {parent} — pas " @@ -375,29 +371,29 @@ msgstr "" "These highlights describe the wider {parent} appellation — not this " "denomination specifically." -#: scripts/_lib/map_template.py:138 +#: scripts/_lib/map_template.py:137 msgid "sans région" msgstr "no region" -#: scripts/_lib/map_template.py:139 +#: scripts/_lib/map_template.py:138 #, python-brace-format msgid "{n} commune(s) INAO" msgstr "{n} INAO commune(s)" -#: scripts/_lib/map_template.py:140 +#: scripts/_lib/map_template.py:139 #, python-brace-format msgid "{n} commune(s)" msgstr "{n} commune(s)" -#: scripts/_lib/map_template.py:141 +#: scripts/_lib/map_template.py:140 msgid "aire approchée" msgstr "approximate area" -#: scripts/_lib/map_template.py:142 +#: scripts/_lib/map_template.py:141 msgid "aire approchée (à l'échelle communale)" msgstr "approximate area (commune level)" -#: scripts/_lib/map_template.py:144 +#: scripts/_lib/map_template.py:143 #, python-brace-format msgid "" "Aire approchée — pas de données parcellaires précises pour cette " @@ -406,7 +402,7 @@ msgstr "" "Approximate area — no parcel-precise data published for this " "denomination; polygon inherited from {umbrella}." -#: scripts/_lib/map_template.py:148 +#: scripts/_lib/map_template.py:147 msgid "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de l'appellation parente." @@ -414,7 +410,7 @@ msgstr "" "Approximate area — no parcel-precise data published for this " "denomination; polygon inherited from the parent appellation." -#: scripts/_lib/map_template.py:152 +#: scripts/_lib/map_template.py:151 msgid "" "Aire approchée — pas de données parcellaires disponibles ; affichée comme" " l'emprise de la commune où se situe la dénomination." @@ -422,7 +418,7 @@ msgstr "" "Approximate area — no parcel data available; shown as the outline of the " "commune containing the denomination." -#: scripts/_lib/map_template.py:156 +#: scripts/_lib/map_template.py:155 #, python-brace-format msgid "" "Aire issue du lieu-dit cadastral « {lieu_dit} » (commune de {commune}, " @@ -431,11 +427,11 @@ msgstr "" "Boundary derived from the cadastral lieu-dit “{lieu_dit}” (commune of " "{commune}, {source})." -#: scripts/_lib/map_template.py:159 +#: scripts/_lib/map_template.py:158 msgid "cadastre.data.gouv.fr" msgstr "cadastre.data.gouv.fr" -#: scripts/_lib/map_template.py:161 +#: scripts/_lib/map_template.py:160 msgid "" "Aire approchée — reconstituée à partir des références parcellaires du " "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " @@ -445,7 +441,7 @@ msgstr "" "plan of the demarcated area annexed to the product specification; this is" " not an official boundary." -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:169 #, python-brace-format msgid "" "Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " @@ -456,7 +452,7 @@ msgstr "" "(INAO), water bodies and non-vineyard land included; no parcel " "delimitation exists for this denomination." -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:174 #, python-brace-format msgid "" "Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " @@ -467,7 +463,7 @@ msgstr "" "the INAO parcel delimitation — not the delimitation itself; the parcels " "appear as you zoom in." -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:179 #, python-brace-format msgid "" "Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " @@ -479,14 +475,14 @@ msgstr "" "of its vineyard generalised at {radius} m from the INAO parcel " "delimitation; the parcels themselves appear as you zoom in." -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:185 #, python-brace-format msgid "Aire : zone de production officielle publiée par le géoportail ({region})." msgstr "" "Area: the official production zone published by the regional geoportal " "({region})." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:189 msgid "" "Aire : zone de production officielle du MAPA (Zonas de Calidad " "Diferenciada : Vinos)." @@ -494,7 +490,7 @@ msgstr "" "Area: the official MAPA production zone (Zonas de Calidad Diferenciada: " "Vinos)." -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:193 msgid "" "Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " "la résolution des communes ; peut inclure des terres non viticoles." @@ -502,7 +498,7 @@ msgstr "" "Approximate area — the Bétard 2022 dataset polygon (EU PDOs), at " "municipality resolution; may include non-vineyard land." -#: scripts/_lib/map_template.py:199 +#: scripts/_lib/map_template.py:198 msgid "" "Aire approchée — réunion des polygones des appellations membres (Bétard " "2022), à la résolution des communes." @@ -510,7 +506,7 @@ msgstr "" "Approximate area — the union of the member appellations' polygons (Bétard" " 2022), at municipality resolution." -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:202 msgid "" "Aire approchée — réunion des unités administratives nommées dans le " "document (limites Eurostat GISCO ou cadastre national), terres non " @@ -520,116 +516,116 @@ msgstr "" "document (Eurostat GISCO or national cadastre boundaries), non-vineyard " "land included." -#: scripts/_lib/map_template.py:207 +#: scripts/_lib/map_template.py:206 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} appellations at this point" -#: scripts/_lib/map_template.py:208 +#: scripts/_lib/map_template.py:207 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Click again to cycle through the others" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:208 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Product specification (BO Agri, PDF)" -#: scripts/_lib/map_template.py:210 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "Product specification (EU GI register, PDF)" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:210 msgid "homologué" msgstr "approved" -#: scripts/_lib/map_template.py:212 +#: scripts/_lib/map_template.py:211 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:213 +#: scripts/_lib/map_template.py:212 msgid "Texte officiel INAO (show_texte)" msgstr "Official INAO text (show_texte)" -#: scripts/_lib/map_template.py:214 +#: scripts/_lib/map_template.py:213 msgid "Fiche produit INAO" msgstr "INAO product entry" -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:214 msgid "Site officiel de l'interprofession" msgstr "Official trade body site" -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:215 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Specification (EUR-Lex, single document)" -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:216 msgid "Pliego de condiciones (national, PDF)" msgstr "National pliego de condiciones (PDF)" -#: scripts/_lib/map_template.py:218 +#: scripts/_lib/map_template.py:217 msgid "variétés ajoutées" msgstr "varieties added" -#: scripts/_lib/map_template.py:219 +#: scripts/_lib/map_template.py:218 msgid "Cahier des charges national (PDF)" msgstr "National product specification (PDF)" -#: scripts/_lib/map_template.py:220 +#: scripts/_lib/map_template.py:219 msgid "Spécification du produit (IGP, PDF)" msgstr "Product specification (PGI, PDF)" -#: scripts/_lib/map_template.py:221 +#: scripts/_lib/map_template.py:220 msgid "Registre régional des cépages (PDF)" msgstr "Regional grape register (PDF)" -#: scripts/_lib/map_template.py:222 +#: scripts/_lib/map_template.py:221 msgid "Registre eAmbrosia (UE)" msgstr "eAmbrosia register (EU)" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:222 msgid "Numéro de dossier" msgstr "File number" -#: scripts/_lib/map_template.py:224 +#: scripts/_lib/map_template.py:223 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Cantonal wine regulation" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:224 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Swiss AOC register (FOAG/BLW)" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:225 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Product specification (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:226 msgid "Registre des IG du Royaume-Uni" msgstr "UK GI register" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:227 msgid "Légende couleurs" msgstr "Colour legend" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:228 msgid "Bassin viticole" msgstr "Wine basin" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:229 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Smaller area = denser fill." -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:231 msgid "principal — variété de la cuvée" msgstr "principal — main variety" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:232 msgid "accessoire — assemblage limité" msgstr "accessory — limited blending" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:233 msgid "intérêt — observation/conservation" msgstr "of interest — observation / conservation" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:235 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" @@ -639,7 +635,7 @@ msgstr "" "accessory varieties — every authorised casta is listed together in the " "caderno de especificações." -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:239 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " @@ -649,20 +645,20 @@ msgstr "" "in line with the disciplinare of the Bianchello del Metauro DOP; the VIVC" " catalogue records it as a synonym of Trebbiano Toscano." -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:244 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "Open Wine Map has not yet located a {doc} for this appellation." -#: scripts/_lib/map_template.py:247 +#: scripts/_lib/map_template.py:246 msgid "aidez-nous à le trouver" msgstr "help us find it" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:250 msgid "Annulée" msgstr "Cancelled" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:252 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -673,141 +669,141 @@ msgstr "" "labels and teaching material may still mention it; it is kept here for " "reference." -#: scripts/_lib/map_template.py:257 +#: scripts/_lib/map_template.py:256 #, python-brace-format msgid "Acte national : {act}." msgstr "National act: {act}." -#: scripts/_lib/map_template.py:258 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "It is succeeded by {successor}." -#: scripts/_lib/map_template.py:259 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Registration cancelled on {date}" -#: scripts/_lib/map_template.py:265 +#: scripts/_lib/map_template.py:264 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "Delimited by {regulator} in its {doc}, which authorises {grapes}." -#: scripts/_lib/map_template.py:267 +#: scripts/_lib/map_template.py:266 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "Delimited by {regulator} in its {doc}{extra}." -#: scripts/_lib/map_template.py:268 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "{names} and {n} other grape varieties" -#: scripts/_lib/map_template.py:269 +#: scripts/_lib/map_template.py:268 msgid "le registre eAmbrosia de l'UE" msgstr "the EU eAmbrosia register" -#: scripts/_lib/map_template.py:270 +#: scripts/_lib/map_template.py:269 #, python-brace-format msgid "le canton de {canton}" msgstr "the canton of {canton}" -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:270 msgid "(français)" msgstr "(source: French)" -#: scripts/_lib/map_template.py:272 +#: scripts/_lib/map_template.py:271 msgid "Texte source en français" msgstr "Source text in French" -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:272 msgid "(español)" msgstr "(source: Spanish)" -#: scripts/_lib/map_template.py:274 +#: scripts/_lib/map_template.py:273 msgid "Texte source en espagnol" msgstr "Source text in Spanish" -#: scripts/_lib/map_template.py:275 +#: scripts/_lib/map_template.py:274 msgid "(português)" msgstr "(source: Portuguese)" -#: scripts/_lib/map_template.py:276 +#: scripts/_lib/map_template.py:275 msgid "Texte source en portugais" msgstr "Source text in Portuguese" -#: scripts/_lib/map_template.py:277 +#: scripts/_lib/map_template.py:276 msgid "Filtres" msgstr "Filters" -#: scripts/_lib/map_template.py:278 +#: scripts/_lib/map_template.py:277 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Translated from {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:279 +#: scripts/_lib/map_template.py:278 msgid "Wikipédia en anglais" msgstr "English Wikipedia" -#: scripts/_lib/map_template.py:280 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en français" msgstr "French Wikipedia" -#: scripts/_lib/map_template.py:281 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en espagnol" msgstr "Spanish Wikipedia" -#: scripts/_lib/map_template.py:282 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en néerlandais" msgstr "Dutch Wikipedia" -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en portugais" msgstr "Portuguese Wikipedia" -#: scripts/_lib/map_template.py:284 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en croate" msgstr "Croatian Wikipedia" -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:284 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "Vitis International Variety Catalogue (Julius Kühn-Institut)" -#: scripts/_lib/map_template.py:286 +#: scripts/_lib/map_template.py:285 #, python-brace-format msgid "VIVC #{id}" msgstr "VIVC #{id}" -#: scripts/_lib/map_template.py:287 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Machine translated from {source}" -#: scripts/_lib/map_template.py:288 +#: scripts/_lib/map_template.py:287 msgid "le cahier des charges" msgstr "the cahier des charges" -#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 +#: scripts/_lib/map_template.py:288 scripts/_lib/map_template.py:290 msgid "le pliego de condiciones" msgstr "the specification" -#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le caderno de especificações" msgstr "the caderno de especificações" -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:292 msgid "Dénomination géographique complémentaire de" msgstr "Sub-region of" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:293 msgid "À propos" msgstr "About" -#: scripts/_lib/map_template.py:295 +#: scripts/_lib/map_template.py:294 msgid "À propos d'Open Wine Map" msgstr "About Open Wine Map" -#: scripts/_lib/map_template.py:297 +#: scripts/_lib/map_template.py:296 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -819,7 +815,7 @@ msgstr "" " Swiss federal repertoire of cantonal AOCs and the UK register of " "geographical indications." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:303 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -839,12 +835,12 @@ msgstr "" "human translator, with a small machine-translated remainder. Each item " "carries its own attribution line in the panel." -#: scripts/_lib/map_template.py:313 +#: scripts/_lib/map_template.py:312 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Made with ♡ by {devloed}." -#: scripts/_lib/map_template.py:315 +#: scripts/_lib/map_template.py:314 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -868,7 +864,7 @@ msgstr "" "Röckel et al.). Every Wikipedia excerpt is flagged where it appears. Full" " source list and licences in the {readme}." -#: scripts/_lib/map_template.py:326 +#: scripts/_lib/map_template.py:325 #, python-brace-format msgid "" "Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" @@ -884,12 +880,12 @@ msgstr "" "zoom {zoom} the parcels themselves are drawn. Every other appellation " "keeps the same outline at every zoom." -#: scripts/_lib/map_template.py:333 +#: scripts/_lib/map_template.py:332 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Issues and pull requests welcome on {github}." -#: scripts/_lib/map_template.py:335 +#: scripts/_lib/map_template.py:334 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -905,20 +901,20 @@ msgstr "" "identifiers, no IP address stored, no cross-site or cross-device tracking" " — which is why there is no consent banner to click through." -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:341 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Follow updates on {bluesky}." -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:342 msgid "e-mail" msgstr "email" -#: scripts/_lib/map_template.py:344 +#: scripts/_lib/map_template.py:343 msgid "E-mail copié dans le presse-papiers" msgstr "Email copied to clipboard" -#: scripts/_lib/map_template.py:346 +#: scripts/_lib/map_template.py:345 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -927,55 +923,55 @@ msgstr "" "Auto-generated map — errors are possible. Flag them from the appellation " "card, or by {email}." -#: scripts/_lib/map_template.py:352 +#: scripts/_lib/map_template.py:351 msgid "Signaler une erreur" msgstr "Report a mistake" -#: scripts/_lib/map_template.py:353 +#: scripts/_lib/map_template.py:352 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "One tap tells us what to check — no account needed." -#: scripts/_lib/map_template.py:354 +#: scripts/_lib/map_template.py:353 msgid "Aire / contour" msgstr "Boundary" -#: scripts/_lib/map_template.py:357 +#: scripts/_lib/map_template.py:356 msgid "Nom / type" msgstr "Name / type" -#: scripts/_lib/map_template.py:359 +#: scripts/_lib/map_template.py:358 msgid "Autre" msgstr "Other" -#: scripts/_lib/map_template.py:360 +#: scripts/_lib/map_template.py:359 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Thanks, noted: {aspect}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:361 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Add details if you like (optional — no personal details)" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:363 msgid "Envoyer" msgstr "Send" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:366 msgid "Géométrie" msgstr "Geometry" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:367 msgid "Envoyé, merci !" msgstr "Sent — thank you!" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:369 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." msgstr "{aspect} — your browser blocks our analytics; send an {email} instead." -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:373 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -988,15 +984,15 @@ msgstr "" "quality and extend coverage beyond the EU, Switzerland and the United " "Kingdom." -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:378 msgid "Toutes les appellations" msgstr "All appellations" -#: scripts/_lib/map_template.py:380 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "All wine appellations — Open Wine Map" -#: scripts/_lib/map_template.py:382 +#: scripts/_lib/map_template.py:381 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -1007,192 +1003,192 @@ msgstr "" " PDOs and PGIs with their traditional terms (AOC, DOCG, DOQ…), Swiss AOCs" " and UK GIs." -#: scripts/_lib/map_template.py:387 +#: scripts/_lib/map_template.py:386 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " "{map_link}." msgstr "The {n} appellations below are grouped by country. Back to the {map_link}." -#: scripts/_lib/map_template.py:390 +#: scripts/_lib/map_template.py:389 msgid "carte interactive" msgstr "interactive map" -#: scripts/_lib/map_template.py:391 +#: scripts/_lib/map_template.py:390 msgid "Liste des appellations par pays" msgstr "List of appellations by country" -#: scripts/_lib/map_template.py:392 +#: scripts/_lib/map_template.py:391 msgid "Appellation parente" msgstr "Parent appellation" -#: scripts/_lib/map_template.py:393 +#: scripts/_lib/map_template.py:392 msgid "Dénominations rattachées" msgstr "Related denominations" -#: scripts/_lib/map_template.py:395 +#: scripts/_lib/map_template.py:394 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Browse the full list of appellations: {browse_link}." -#: scripts/_lib/map_template.py:397 +#: scripts/_lib/map_template.py:396 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Data updated on {date}." -#: scripts/_lib/map_template.py:412 +#: scripts/_lib/map_template.py:411 msgid "BOURGOGNE" msgstr "Burgundy" -#: scripts/_lib/map_template.py:413 +#: scripts/_lib/map_template.py:412 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:414 +#: scripts/_lib/map_template.py:413 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:415 +#: scripts/_lib/map_template.py:414 msgid "SAVOIE" msgstr "Savoie" -#: scripts/_lib/map_template.py:416 +#: scripts/_lib/map_template.py:415 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:417 +#: scripts/_lib/map_template.py:416 msgid "ALSACE ET EST" msgstr "Alsace and East" -#: scripts/_lib/map_template.py:418 +#: scripts/_lib/map_template.py:417 msgid "VAL DE LOIRE" msgstr "Loire Valley" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:418 msgid "SUD-OUEST" msgstr "South-West" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:419 msgid "VALLEE DU RHÔNE" msgstr "Rhône Valley" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:420 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Roussillon" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:421 msgid "TOULOUSE-PYRENEES" msgstr "Toulouse-Pyrenees" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:422 msgid "PROVENCE-CORSE" msgstr "Provence-Corsica" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:423 msgid "CHAMPAGNE" msgstr "Champagne" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:424 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Cider eaux-de-vie" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:425 msgid "VIN DOUX NATURELS" msgstr "Natural sweet wines" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:426 msgid "COGNAC" msgstr "Cognac" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:427 msgid "ARMAGNAC" msgstr "Armagnac" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:428 msgid "RHUM" msgstr "Rum" -#: scripts/_lib/map_template.py:469 +#: scripts/_lib/map_template.py:468 msgid "France" msgstr "France" -#: scripts/_lib/map_template.py:470 +#: scripts/_lib/map_template.py:469 msgid "Espagne" msgstr "Spain" -#: scripts/_lib/map_template.py:471 +#: scripts/_lib/map_template.py:470 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:472 +#: scripts/_lib/map_template.py:471 msgid "Italie" msgstr "Italy" -#: scripts/_lib/map_template.py:473 +#: scripts/_lib/map_template.py:472 msgid "Autriche" msgstr "Austria" -#: scripts/_lib/map_template.py:474 +#: scripts/_lib/map_template.py:473 msgid "Slovénie" msgstr "Slovenia" -#: scripts/_lib/map_template.py:475 +#: scripts/_lib/map_template.py:474 msgid "Croatie" msgstr "Croatia" -#: scripts/_lib/map_template.py:476 +#: scripts/_lib/map_template.py:475 msgid "Hongrie" msgstr "Hungary" -#: scripts/_lib/map_template.py:477 +#: scripts/_lib/map_template.py:476 msgid "Roumanie" msgstr "Romania" -#: scripts/_lib/map_template.py:478 +#: scripts/_lib/map_template.py:477 msgid "Bulgarie" msgstr "Bulgaria" -#: scripts/_lib/map_template.py:479 +#: scripts/_lib/map_template.py:478 msgid "Grèce" msgstr "Greece" -#: scripts/_lib/map_template.py:480 +#: scripts/_lib/map_template.py:479 msgid "Allemagne" msgstr "Germany" -#: scripts/_lib/map_template.py:481 +#: scripts/_lib/map_template.py:480 msgid "Slovaquie" msgstr "Slovakia" -#: scripts/_lib/map_template.py:482 +#: scripts/_lib/map_template.py:481 msgid "Suisse" msgstr "Switzerland" -#: scripts/_lib/map_template.py:483 +#: scripts/_lib/map_template.py:482 msgid "Tchéquie" msgstr "Czechia" -#: scripts/_lib/map_template.py:484 +#: scripts/_lib/map_template.py:483 msgid "Luxembourg" msgstr "Luxembourg" -#: scripts/_lib/map_template.py:485 +#: scripts/_lib/map_template.py:484 msgid "Belgique" msgstr "Belgium" -#: scripts/_lib/map_template.py:486 +#: scripts/_lib/map_template.py:485 msgid "Pays-Bas" msgstr "Netherlands" -#: scripts/_lib/map_template.py:487 +#: scripts/_lib/map_template.py:486 msgid "Malte" msgstr "Malta" -#: scripts/_lib/map_template.py:488 +#: scripts/_lib/map_template.py:487 msgid "Chypre" msgstr "Cyprus" -#: scripts/_lib/map_template.py:489 +#: scripts/_lib/map_template.py:488 msgid "Royaume-Uni" msgstr "United Kingdom" @@ -1620,3 +1616,6 @@ msgstr "palo cortado" #~ msgid "Une erreur sur cette fiche ?" #~ msgstr "Something off on this card?" +#~ msgid "Recherche de cépage…" +#~ msgstr "Search grapes…" + diff --git a/locale/es/LC_MESSAGES/messages.po b/locale/es/LC_MESSAGES/messages.po index 95cea34..9a35de0 100644 --- a/locale/es/LC_MESSAGES/messages.po +++ b/locale/es/LC_MESSAGES/messages.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-23 08:12+0200\n" +"POT-Creation-Date: 2026-09-23 09:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: es\n" @@ -59,286 +59,282 @@ msgstr "nombre de denominación…" msgid "Recherche d'appellation…" msgstr "Buscar denominación…" -#: scripts/_lib/map_template.py:56 -msgid "Recherche de cépage…" -msgstr "Buscar variedad…" - -#: scripts/_lib/map_template.py:58 +#: scripts/_lib/map_template.py:57 msgid "Rechercher une appellation, un cépage, une région…" msgstr "Buscar una denominación, una uva o una región…" -#: scripts/_lib/map_template.py:59 +#: scripts/_lib/map_template.py:58 msgid "Cépage principal uniquement" msgstr "Solo uva principal" -#: scripts/_lib/map_template.py:60 +#: scripts/_lib/map_template.py:59 #, python-brace-format msgid "Aucun résultat pour « {q} »" msgstr "Sin resultados para «{q}»" -#: scripts/_lib/map_template.py:61 +#: scripts/_lib/map_template.py:60 msgid "Options" msgstr "Opciones" -#: scripts/_lib/map_template.py:62 +#: scripts/_lib/map_template.py:61 msgid "Filtres actifs" msgstr "Filtros activos" -#: scripts/_lib/map_template.py:63 +#: scripts/_lib/map_template.py:62 msgid "Tout sélectionner" msgstr "Seleccionar todo" -#: scripts/_lib/map_template.py:65 +#: scripts/_lib/map_template.py:64 #, python-brace-format msgid "{p} appellations · {s} dénominations géographiques complémentaires" msgstr "{p} denominaciones · {s} denominaciones geográficas complementarias" -#: scripts/_lib/map_template.py:67 +#: scripts/_lib/map_template.py:66 #, python-brace-format msgid "{p} appellations" msgstr "{p} denominaciones" -#: scripts/_lib/map_template.py:68 +#: scripts/_lib/map_template.py:67 #, python-brace-format msgid "Ouvrir la fiche de {name}" msgstr "Abrir la ficha de {name}" -#: scripts/_lib/map_template.py:69 +#: scripts/_lib/map_template.py:68 msgid "Ouvrir la fiche" msgstr "Abrir la ficha" -#: scripts/_lib/map_template.py:70 +#: scripts/_lib/map_template.py:69 msgid "Inclure les spiritueux" msgstr "Incluir destilados" -#: scripts/_lib/map_template.py:71 +#: scripts/_lib/map_template.py:70 msgid "Style de vin" msgstr "Estilo de vino" -#: scripts/_lib/map_template.py:72 +#: scripts/_lib/map_template.py:71 msgid "Classement" msgstr "Clasificación" -#: scripts/_lib/map_template.py:73 +#: scripts/_lib/map_template.py:72 msgid "Cépages principaux" msgstr "Variedades principales" -#: scripts/_lib/map_template.py:74 +#: scripts/_lib/map_template.py:73 msgid "Cépages accessoires" msgstr "Variedades complementarias" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 -#: scripts/_lib/map_template.py:355 +#: scripts/_lib/map_template.py:74 scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:354 msgid "Cépages" msgstr "Variedades de uva" -#: scripts/_lib/map_template.py:76 +#: scripts/_lib/map_template.py:75 msgid "Région" msgstr "Región" -#: scripts/_lib/map_template.py:77 +#: scripts/_lib/map_template.py:76 msgid "Appellation" msgstr "Denominación" -#: scripts/_lib/map_template.py:78 +#: scripts/_lib/map_template.py:77 msgid "Type" msgstr "Tipo" -#: scripts/_lib/map_template.py:79 +#: scripts/_lib/map_template.py:78 msgid "Origine protégée (AOP, AOC, DOC, DO…)" msgstr "Origen protegido (DOP, AOC, DOC, DO…)" -#: scripts/_lib/map_template.py:80 +#: scripts/_lib/map_template.py:79 msgid "Indication géographique (IGP, IGT, Landwein…)" msgstr "Indicación geográfica (IGP, IGT, Landwein…)" -#: scripts/_lib/map_template.py:81 +#: scripts/_lib/map_template.py:80 msgid "AOP" msgstr "DOP" -#: scripts/_lib/map_template.py:82 +#: scripts/_lib/map_template.py:81 msgid "IGP" msgstr "IGP" -#: scripts/_lib/map_template.py:83 +#: scripts/_lib/map_template.py:82 msgid "IG spiritueux" msgstr "IG de bebida espirituosa" -#: scripts/_lib/map_template.py:84 +#: scripts/_lib/map_template.py:83 msgid "Type d'appellation" msgstr "Tipo de denominación" -#: scripts/_lib/map_template.py:85 +#: scripts/_lib/map_template.py:84 msgid "AOP (régime britannique)" msgstr "DOP (régimen británico)" -#: scripts/_lib/map_template.py:86 +#: scripts/_lib/map_template.py:85 msgid "IGP (régime britannique)" msgstr "IGP (régimen británico)" -#: scripts/_lib/map_template.py:87 +#: scripts/_lib/map_template.py:86 msgid "AOC (Suisse)" msgstr "AOC (Suiza)" -#: scripts/_lib/map_template.py:88 +#: scripts/_lib/map_template.py:87 msgid "Source" msgstr "Fuente" -#: scripts/_lib/map_template.py:89 +#: scripts/_lib/map_template.py:88 msgid "Vue" msgstr "Vista" -#: scripts/_lib/map_template.py:90 +#: scripts/_lib/map_template.py:89 msgid "Simple" msgstr "Simple" -#: scripts/_lib/map_template.py:91 +#: scripts/_lib/map_template.py:90 msgid "Avancée" msgstr "Avanzada" -#: scripts/_lib/map_template.py:92 +#: scripts/_lib/map_template.py:91 msgid "Thème" msgstr "Tema" -#: scripts/_lib/map_template.py:93 +#: scripts/_lib/map_template.py:92 msgid "Clair" msgstr "Claro" -#: scripts/_lib/map_template.py:94 +#: scripts/_lib/map_template.py:93 msgid "Sombre" msgstr "Oscuro" -#: scripts/_lib/map_template.py:95 +#: scripts/_lib/map_template.py:94 msgid "Système" msgstr "Sistema" -#: scripts/_lib/map_template.py:96 +#: scripts/_lib/map_template.py:95 msgid "Afficher les IGP" msgstr "Mostrar las IGP" -#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:96 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:361 msgid "rouge" msgstr "tinto" -#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:362 msgid "blanc" msgstr "blanco" -#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:363 msgid "rosé" msgstr "rosado" -#: scripts/_lib/map_template.py:100 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:364 msgid "mousseux" msgstr "espumoso" -#: scripts/_lib/map_template.py:101 +#: scripts/_lib/map_template.py:100 msgid "moelleux / liquoreux" msgstr "dulce / licoroso" -#: scripts/_lib/map_template.py:102 scripts/_lib/style_taxonomy.py:345 +#: scripts/_lib/map_template.py:101 scripts/_lib/style_taxonomy.py:345 msgid "oxydatif" msgstr "oxidativo" -#: scripts/_lib/map_template.py:103 +#: scripts/_lib/map_template.py:102 msgid "autre" msgstr "otro" -#: scripts/_lib/map_template.py:104 +#: scripts/_lib/map_template.py:103 msgid "Réinitialiser" msgstr "Reiniciar" -#: scripts/_lib/map_template.py:105 +#: scripts/_lib/map_template.py:104 #, python-brace-format msgid "{n} appellations" msgstr "{n} denominaciones" -#: scripts/_lib/map_template.py:106 +#: scripts/_lib/map_template.py:105 #, python-brace-format msgid "{n} / {total} appellations" msgstr "{n} / {total} denominaciones" -#: scripts/_lib/map_template.py:107 +#: scripts/_lib/map_template.py:106 #, python-brace-format msgid "{n} masquées dans les IGP · afficher" msgstr "{n} ocultas en las IGP · mostrar" -#: scripts/_lib/map_template.py:108 +#: scripts/_lib/map_template.py:107 msgid "Fermer" msgstr "Cerrar" -#: scripts/_lib/map_template.py:109 +#: scripts/_lib/map_template.py:108 msgid "Détails de l'appellation" msgstr "Detalles de la denominación" -#: scripts/_lib/map_template.py:110 +#: scripts/_lib/map_template.py:109 #, python-brace-format msgid "Retirer le filtre {label}" msgstr "Quitar filtro {label}" -#: scripts/_lib/map_template.py:111 +#: scripts/_lib/map_template.py:110 msgid "Filtres et options de la carte" msgstr "Filtros y opciones del mapa" -#: scripts/_lib/map_template.py:112 +#: scripts/_lib/map_template.py:111 msgid "Langue" msgstr "Idioma" -#: scripts/_lib/map_template.py:113 +#: scripts/_lib/map_template.py:112 msgid "Carte des appellations viticoles" msgstr "Mapa de denominaciones vitivinícolas" -#: scripts/_lib/map_template.py:114 +#: scripts/_lib/map_template.py:113 msgid "Aller à la carte" msgstr "Ir al mapa" -#: scripts/_lib/map_template.py:115 +#: scripts/_lib/map_template.py:114 msgid "Styles" msgstr "Estilos" -#: scripts/_lib/map_template.py:116 +#: scripts/_lib/map_template.py:115 msgid "Variétés d'intérêt" msgstr "Variedades de interés" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 +#: scripts/_lib/map_template.py:116 scripts/_lib/map_template.py:357 msgid "Sources" msgstr "Fuentes" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:355 msgid "Terroir" msgstr "Terroir" -#: scripts/_lib/map_template.py:119 +#: scripts/_lib/map_template.py:118 #, python-brace-format msgid "Dűlők (lieux-dits) : {n}" msgstr "Dűlők (pagos): {n}" -#: scripts/_lib/map_template.py:120 +#: scripts/_lib/map_template.py:119 #, python-brace-format msgid "Menzioni geografiche aggiuntive (crus) : {n}" msgstr "Menciones geográficas adicionales (crus): {n}" -#: scripts/_lib/map_template.py:121 +#: scripts/_lib/map_template.py:120 msgid "Facteurs naturels" msgstr "Factores naturales" -#: scripts/_lib/map_template.py:122 +#: scripts/_lib/map_template.py:121 msgid "Facteurs humains" msgstr "Factores humanos" -#: scripts/_lib/map_template.py:123 +#: scripts/_lib/map_template.py:122 msgid "Caractéristiques du produit" msgstr "Características del producto" -#: scripts/_lib/map_template.py:124 +#: scripts/_lib/map_template.py:123 msgid "Lien terroir / vin" msgstr "Vínculo terroir / vino" -#: scripts/_lib/map_template.py:126 +#: scripts/_lib/map_template.py:125 #, python-brace-format msgid "" "Faits dégagés du Lien au terroir par interprétation automatique — voir la" @@ -347,26 +343,26 @@ msgstr "" "Hechos extraídos de la sección de vínculo con el terruño (Lien au " "terroir) por interpretación automática — véase el {source}." -#: scripts/_lib/map_template.py:128 +#: scripts/_lib/map_template.py:127 msgid "source" msgstr "fuente" -#: scripts/_lib/map_template.py:129 +#: scripts/_lib/map_template.py:128 msgid "via Wikipedia · CC BY-SA 4.0" msgstr "vía Wikipedia · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:131 +#: scripts/_lib/map_template.py:130 #, python-brace-format msgid "Citation textuelle du Lien au terroir — voir la {source}." msgstr "" "Cita textual de la sección de vínculo con el terruño (Lien au terroir) — " "véase el {source}." -#: scripts/_lib/map_template.py:133 +#: scripts/_lib/map_template.py:132 msgid "à vérifier — texte source court" msgstr "por verificar — texto fuente breve" -#: scripts/_lib/map_template.py:135 +#: scripts/_lib/map_template.py:134 #, python-brace-format msgid "" "Ces repères décrivent l'appellation englobante {parent} — pas " @@ -375,29 +371,29 @@ msgstr "" "Estas notas describen la denominación matriz {parent} — no " "específicamente esta denominación." -#: scripts/_lib/map_template.py:138 +#: scripts/_lib/map_template.py:137 msgid "sans région" msgstr "sin región" -#: scripts/_lib/map_template.py:139 +#: scripts/_lib/map_template.py:138 #, python-brace-format msgid "{n} commune(s) INAO" msgstr "{n} comuna(s) INAO" -#: scripts/_lib/map_template.py:140 +#: scripts/_lib/map_template.py:139 #, python-brace-format msgid "{n} commune(s)" msgstr "{n} comuna(s)" -#: scripts/_lib/map_template.py:141 +#: scripts/_lib/map_template.py:140 msgid "aire approchée" msgstr "área aproximada" -#: scripts/_lib/map_template.py:142 +#: scripts/_lib/map_template.py:141 msgid "aire approchée (à l'échelle communale)" msgstr "área aproximada (nivel municipal)" -#: scripts/_lib/map_template.py:144 +#: scripts/_lib/map_template.py:143 #, python-brace-format msgid "" "Aire approchée — pas de données parcellaires précises pour cette " @@ -406,7 +402,7 @@ msgstr "" "Área aproximada — no hay datos parcelarios precisos para esta " "denominación; polígono heredado de {umbrella}." -#: scripts/_lib/map_template.py:148 +#: scripts/_lib/map_template.py:147 msgid "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de l'appellation parente." @@ -414,7 +410,7 @@ msgstr "" "Área aproximada — no hay datos parcelarios precisos para esta " "denominación; polígono heredado de la denominación de origen principal." -#: scripts/_lib/map_template.py:152 +#: scripts/_lib/map_template.py:151 msgid "" "Aire approchée — pas de données parcellaires disponibles ; affichée comme" " l'emprise de la commune où se situe la dénomination." @@ -422,7 +418,7 @@ msgstr "" "Área aproximada — sin datos parcelarios disponibles; se muestra como el " "contorno del municipio que contiene la denominación." -#: scripts/_lib/map_template.py:156 +#: scripts/_lib/map_template.py:155 #, python-brace-format msgid "" "Aire issue du lieu-dit cadastral « {lieu_dit} » (commune de {commune}, " @@ -431,11 +427,11 @@ msgstr "" "Superficie derivada del paraje catastral «{lieu_dit}» (municipio de " "{commune}, {source})." -#: scripts/_lib/map_template.py:159 +#: scripts/_lib/map_template.py:158 msgid "cadastre.data.gouv.fr" msgstr "cadastre.data.gouv.fr" -#: scripts/_lib/map_template.py:161 +#: scripts/_lib/map_template.py:160 msgid "" "Aire approchée — reconstituée à partir des références parcellaires du " "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " @@ -445,7 +441,7 @@ msgstr "" "del plano de la zona delimitada anexo al pliego de condiciones; no es un " "límite oficial." -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:169 #, python-brace-format msgid "" "Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " @@ -456,7 +452,7 @@ msgstr "" "(INAO), incluidas las masas de agua y las tierras no vitícolas; no existe" " delimitación parcelaria para esta denominación." -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:174 #, python-brace-format msgid "" "Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " @@ -467,7 +463,7 @@ msgstr "" "la delimitación parcelaria del INAO; no es la delimitación en sí. Las " "parcelas aparecen al acercar el zoom." -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:179 #, python-brace-format msgid "" "Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " @@ -480,14 +476,14 @@ msgstr "" "delimitación parcelaria del INAO; las propias parcelas aparecen al " "acercar el zoom." -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:185 #, python-brace-format msgid "Aire : zone de production officielle publiée par le géoportail ({region})." msgstr "" "Área: zona de producción oficial publicada por el geoportal regional " "({region})." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:189 msgid "" "Aire : zone de production officielle du MAPA (Zonas de Calidad " "Diferenciada : Vinos)." @@ -495,7 +491,7 @@ msgstr "" "Área: zona de producción oficial del MAPA (Zonas de Calidad Diferenciada:" " Vinos)." -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:193 msgid "" "Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " "la résolution des communes ; peut inclure des terres non viticoles." @@ -503,7 +499,7 @@ msgstr "" "Área aproximada: polígono del conjunto de datos Bétard 2022 (DOP de la " "UE), con resolución municipal; puede incluir tierras no vitícolas." -#: scripts/_lib/map_template.py:199 +#: scripts/_lib/map_template.py:198 msgid "" "Aire approchée — réunion des polygones des appellations membres (Bétard " "2022), à la résolution des communes." @@ -511,7 +507,7 @@ msgstr "" "Área aproximada: unión de los polígonos de las denominaciones miembro " "(Bétard 2022), con resolución municipal." -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:202 msgid "" "Aire approchée — réunion des unités administratives nommées dans le " "document (limites Eurostat GISCO ou cadastre national), terres non " @@ -521,116 +517,116 @@ msgstr "" "documento (límites de Eurostat GISCO o del catastro nacional), incluidas " "las tierras no vitícolas." -#: scripts/_lib/map_template.py:207 +#: scripts/_lib/map_template.py:206 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} denominaciones en este punto" -#: scripts/_lib/map_template.py:208 +#: scripts/_lib/map_template.py:207 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Vuelve a hacer clic para recorrer las demás" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:208 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Pliego de condiciones (BO Agri, PDF)" -#: scripts/_lib/map_template.py:210 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "Pliego de condiciones (registro de IG de la UE, PDF)" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:210 msgid "homologué" msgstr "homologado" -#: scripts/_lib/map_template.py:212 +#: scripts/_lib/map_template.py:211 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:213 +#: scripts/_lib/map_template.py:212 msgid "Texte officiel INAO (show_texte)" msgstr "Texto oficial INAO (show_texte)" -#: scripts/_lib/map_template.py:214 +#: scripts/_lib/map_template.py:213 msgid "Fiche produit INAO" msgstr "Ficha de producto INAO" -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:214 msgid "Site officiel de l'interprofession" msgstr "Sitio oficial de la interprofesión" -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:215 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Pliego de condiciones (EUR-Lex, documento único)" -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:216 msgid "Pliego de condiciones (national, PDF)" msgstr "Pliego de condiciones nacional (PDF)" -#: scripts/_lib/map_template.py:218 +#: scripts/_lib/map_template.py:217 msgid "variétés ajoutées" msgstr "variedades añadidas" -#: scripts/_lib/map_template.py:219 +#: scripts/_lib/map_template.py:218 msgid "Cahier des charges national (PDF)" msgstr "Pliego de condiciones nacional (PDF)" -#: scripts/_lib/map_template.py:220 +#: scripts/_lib/map_template.py:219 msgid "Spécification du produit (IGP, PDF)" msgstr "Pliego de condiciones (IGP, PDF)" -#: scripts/_lib/map_template.py:221 +#: scripts/_lib/map_template.py:220 msgid "Registre régional des cépages (PDF)" msgstr "Registro regional de variedades (PDF)" -#: scripts/_lib/map_template.py:222 +#: scripts/_lib/map_template.py:221 msgid "Registre eAmbrosia (UE)" msgstr "Registro eAmbrosia (UE)" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:222 msgid "Numéro de dossier" msgstr "Número de expediente" -#: scripts/_lib/map_template.py:224 +#: scripts/_lib/map_template.py:223 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Reglamento cantonal de la viña y el vino" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:224 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Registro suizo de AOC (OFAG/BLW)" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:225 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Pliego de condiciones (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:226 msgid "Registre des IG du Royaume-Uni" msgstr "Registro de IG del Reino Unido" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:227 msgid "Légende couleurs" msgstr "Leyenda de colores" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:228 msgid "Bassin viticole" msgstr "Cuenca vitícola" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:229 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Cuanto menor es el área, más densa es la tonalidad." -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:231 msgid "principal — variété de la cuvée" msgstr "principal — variedad de la mezcla" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:232 msgid "accessoire — assemblage limité" msgstr "accesoria — mezcla limitada" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:233 msgid "intérêt — observation/conservation" msgstr "de interés — observación/conservación" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:235 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" @@ -640,7 +636,7 @@ msgstr "" "accesorias — todas las castas autorizadas se enumeran juntas en el " "caderno de especificações." -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:239 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " @@ -650,20 +646,20 @@ msgstr "" "conforme al disciplinare de la DOP Bianchello del Metauro; el catálogo " "VIVC lo registra como sinónimo de Trebbiano Toscano." -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:244 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "Open Wine Map todavía no ha localizado un {doc} para esta denominación." -#: scripts/_lib/map_template.py:247 +#: scripts/_lib/map_template.py:246 msgid "aidez-nous à le trouver" msgstr "ayúdanos a encontrarlo" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:250 msgid "Annulée" msgstr "Cancelada" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:252 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -674,141 +670,141 @@ msgstr "" "etiquetas y materiales didácticos antiguos pueden seguir mencionándola; " "se conserva aquí como referencia." -#: scripts/_lib/map_template.py:257 +#: scripts/_lib/map_template.py:256 #, python-brace-format msgid "Acte national : {act}." msgstr "Acto nacional: {act}." -#: scripts/_lib/map_template.py:258 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "Le sucede {successor}." -#: scripts/_lib/map_template.py:259 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Registro cancelado el {date}" -#: scripts/_lib/map_template.py:265 +#: scripts/_lib/map_template.py:264 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "Delimitada por {regulator} en su {doc}, que autoriza {grapes}." -#: scripts/_lib/map_template.py:267 +#: scripts/_lib/map_template.py:266 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "Delimitada por {regulator} en su {doc}{extra}." -#: scripts/_lib/map_template.py:268 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "{names} y otras {n} variedades de uva" -#: scripts/_lib/map_template.py:269 +#: scripts/_lib/map_template.py:268 msgid "le registre eAmbrosia de l'UE" msgstr "el registro eAmbrosia de la UE" -#: scripts/_lib/map_template.py:270 +#: scripts/_lib/map_template.py:269 #, python-brace-format msgid "le canton de {canton}" msgstr "el cantón de {canton}" -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:270 msgid "(français)" msgstr "(fuente: francés)" -#: scripts/_lib/map_template.py:272 +#: scripts/_lib/map_template.py:271 msgid "Texte source en français" msgstr "Texto fuente en francés" -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:272 msgid "(español)" msgstr "(fuente: español)" -#: scripts/_lib/map_template.py:274 +#: scripts/_lib/map_template.py:273 msgid "Texte source en espagnol" msgstr "Texto fuente en español" -#: scripts/_lib/map_template.py:275 +#: scripts/_lib/map_template.py:274 msgid "(português)" msgstr "(fuente: portugués)" -#: scripts/_lib/map_template.py:276 +#: scripts/_lib/map_template.py:275 msgid "Texte source en portugais" msgstr "Texto fuente en portugués" -#: scripts/_lib/map_template.py:277 +#: scripts/_lib/map_template.py:276 msgid "Filtres" msgstr "Filtros" -#: scripts/_lib/map_template.py:278 +#: scripts/_lib/map_template.py:277 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Traducido de {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:279 +#: scripts/_lib/map_template.py:278 msgid "Wikipédia en anglais" msgstr "Wikipedia en inglés" -#: scripts/_lib/map_template.py:280 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en français" msgstr "Wikipedia en francés" -#: scripts/_lib/map_template.py:281 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en espagnol" msgstr "Wikipedia en español" -#: scripts/_lib/map_template.py:282 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en néerlandais" msgstr "Wikipedia en neerlandés" -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en portugais" msgstr "Wikipedia en portugués" -#: scripts/_lib/map_template.py:284 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en croate" msgstr "Wikipedia en croata" -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:284 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "Vitis International Variety Catalogue (Julius Kühn-Institut)" -#: scripts/_lib/map_template.py:286 +#: scripts/_lib/map_template.py:285 #, python-brace-format msgid "VIVC #{id}" msgstr "VIVC #{id}" -#: scripts/_lib/map_template.py:287 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Traducción automática desde {source}" -#: scripts/_lib/map_template.py:288 +#: scripts/_lib/map_template.py:287 msgid "le cahier des charges" msgstr "el pliego de condiciones" -#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 +#: scripts/_lib/map_template.py:288 scripts/_lib/map_template.py:290 msgid "le pliego de condiciones" msgstr "el pliego de condiciones" -#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le caderno de especificações" msgstr "el caderno de especificações" -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:292 msgid "Dénomination géographique complémentaire de" msgstr "Subzona de" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:293 msgid "À propos" msgstr "Acerca de" -#: scripts/_lib/map_template.py:295 +#: scripts/_lib/map_template.py:294 msgid "À propos d'Open Wine Map" msgstr "Acerca de Open Wine Map" -#: scripts/_lib/map_template.py:297 +#: scripts/_lib/map_template.py:296 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -821,7 +817,7 @@ msgstr "" "suizo de AOC cantonales y el registro británico de indicaciones " "geográficas." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:303 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -842,12 +838,12 @@ msgstr "" "salvo un pequeño resto traducido automáticamente. Cada elemento lleva su " "propia línea de atribución en el panel." -#: scripts/_lib/map_template.py:313 +#: scripts/_lib/map_template.py:312 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Hecho con ♡ por {devloed}." -#: scripts/_lib/map_template.py:315 +#: scripts/_lib/map_template.py:314 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -872,7 +868,7 @@ msgstr "" "extracto de Wikipedia se indica allí donde aparece. Detalles y licencias " "en el {readme}." -#: scripts/_lib/map_template.py:326 +#: scripts/_lib/map_template.py:325 #, python-brace-format msgid "" "Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" @@ -889,12 +885,12 @@ msgstr "" "Las demás denominaciones conservan el mismo contorno en todos los niveles" " de zoom." -#: scripts/_lib/map_template.py:333 +#: scripts/_lib/map_template.py:332 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Sugerencias y pull requests bienvenidos en {github}." -#: scripts/_lib/map_template.py:335 +#: scripts/_lib/map_template.py:334 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -911,20 +907,20 @@ msgstr "" "seguimiento entre sitios ni entre dispositivos — por eso no hay banner de" " consentimiento." -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:341 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Sigue las novedades en {bluesky}." -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:342 msgid "e-mail" msgstr "correo" -#: scripts/_lib/map_template.py:344 +#: scripts/_lib/map_template.py:343 msgid "E-mail copié dans le presse-papiers" msgstr "Correo copiado al portapapeles" -#: scripts/_lib/map_template.py:346 +#: scripts/_lib/map_template.py:345 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -933,48 +929,48 @@ msgstr "" "Mapa generado automáticamente — puede contener errores. Señálalos desde " "la ficha de la denominación, o por {email}." -#: scripts/_lib/map_template.py:352 +#: scripts/_lib/map_template.py:351 msgid "Signaler une erreur" msgstr "Señalar un error" -#: scripts/_lib/map_template.py:353 +#: scripts/_lib/map_template.py:352 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "Un toque nos dice qué revisar — sin necesidad de cuenta." -#: scripts/_lib/map_template.py:354 +#: scripts/_lib/map_template.py:353 msgid "Aire / contour" msgstr "Zona / límites" -#: scripts/_lib/map_template.py:357 +#: scripts/_lib/map_template.py:356 msgid "Nom / type" msgstr "Nombre / tipo" -#: scripts/_lib/map_template.py:359 +#: scripts/_lib/map_template.py:358 msgid "Autre" msgstr "Otro" -#: scripts/_lib/map_template.py:360 +#: scripts/_lib/map_template.py:359 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Gracias, anotado: {aspect}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:361 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Añade detalles si quieres (opcional, sin datos personales)" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:363 msgid "Envoyer" msgstr "Enviar" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:366 msgid "Géométrie" msgstr "Geometría" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:367 msgid "Envoyé, merci !" msgstr "Enviado, ¡gracias!" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:369 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " @@ -983,7 +979,7 @@ msgstr "" "{aspect} — tu navegador bloquea nuestra herramienta de medición; envía un" " {email} en su lugar." -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:373 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -996,15 +992,15 @@ msgstr "" "iteraciones afinarán la calidad de los datos y ampliarán la cobertura más" " allá de la UE, Suiza y el Reino Unido." -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:378 msgid "Toutes les appellations" msgstr "Todas las denominaciones" -#: scripts/_lib/map_template.py:380 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "Todas las denominaciones vitivinícolas — Open Wine Map" -#: scripts/_lib/map_template.py:382 +#: scripts/_lib/map_template.py:381 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -1015,7 +1011,7 @@ msgstr "" " Map, por país: DOP e IGP de la UE con sus términos tradicionales (DOCa, " "DOQ, DOCG…), AOC suizas e IG británicas." -#: scripts/_lib/map_template.py:387 +#: scripts/_lib/map_template.py:386 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " @@ -1024,185 +1020,185 @@ msgstr "" "Las {n} denominaciones siguientes están agrupadas por país. Volver al " "{map_link}." -#: scripts/_lib/map_template.py:390 +#: scripts/_lib/map_template.py:389 msgid "carte interactive" msgstr "mapa interactivo" -#: scripts/_lib/map_template.py:391 +#: scripts/_lib/map_template.py:390 msgid "Liste des appellations par pays" msgstr "Lista de denominaciones por país" -#: scripts/_lib/map_template.py:392 +#: scripts/_lib/map_template.py:391 msgid "Appellation parente" msgstr "Denominación matriz" -#: scripts/_lib/map_template.py:393 +#: scripts/_lib/map_template.py:392 msgid "Dénominations rattachées" msgstr "Denominaciones vinculadas" -#: scripts/_lib/map_template.py:395 +#: scripts/_lib/map_template.py:394 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Explore la lista completa de denominaciones: {browse_link}." -#: scripts/_lib/map_template.py:397 +#: scripts/_lib/map_template.py:396 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Datos actualizados el {date}." -#: scripts/_lib/map_template.py:412 +#: scripts/_lib/map_template.py:411 msgid "BOURGOGNE" msgstr "Borgoña" -#: scripts/_lib/map_template.py:413 +#: scripts/_lib/map_template.py:412 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:414 +#: scripts/_lib/map_template.py:413 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:415 +#: scripts/_lib/map_template.py:414 msgid "SAVOIE" msgstr "Saboya" -#: scripts/_lib/map_template.py:416 +#: scripts/_lib/map_template.py:415 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:417 +#: scripts/_lib/map_template.py:416 msgid "ALSACE ET EST" msgstr "Alsacia y Este" -#: scripts/_lib/map_template.py:418 +#: scripts/_lib/map_template.py:417 msgid "VAL DE LOIRE" msgstr "Valle del Loira" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:418 msgid "SUD-OUEST" msgstr "Suroeste" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:419 msgid "VALLEE DU RHÔNE" msgstr "Valle del Ródano" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:420 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Rosellón" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:421 msgid "TOULOUSE-PYRENEES" msgstr "Tolosa-Pirineos" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:422 msgid "PROVENCE-CORSE" msgstr "Provenza-Córcega" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:423 msgid "CHAMPAGNE" msgstr "Champaña" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:424 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Aguardientes de sidra" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:425 msgid "VIN DOUX NATURELS" msgstr "Vinos dulces naturales" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:426 msgid "COGNAC" msgstr "Coñac" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:427 msgid "ARMAGNAC" msgstr "Armañac" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:428 msgid "RHUM" msgstr "Ron" -#: scripts/_lib/map_template.py:469 +#: scripts/_lib/map_template.py:468 msgid "France" msgstr "Francia" -#: scripts/_lib/map_template.py:470 +#: scripts/_lib/map_template.py:469 msgid "Espagne" msgstr "España" -#: scripts/_lib/map_template.py:471 +#: scripts/_lib/map_template.py:470 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:472 +#: scripts/_lib/map_template.py:471 msgid "Italie" msgstr "Italia" -#: scripts/_lib/map_template.py:473 +#: scripts/_lib/map_template.py:472 msgid "Autriche" msgstr "Austria" -#: scripts/_lib/map_template.py:474 +#: scripts/_lib/map_template.py:473 msgid "Slovénie" msgstr "Eslovenia" -#: scripts/_lib/map_template.py:475 +#: scripts/_lib/map_template.py:474 msgid "Croatie" msgstr "Croacia" -#: scripts/_lib/map_template.py:476 +#: scripts/_lib/map_template.py:475 msgid "Hongrie" msgstr "Hungría" -#: scripts/_lib/map_template.py:477 +#: scripts/_lib/map_template.py:476 msgid "Roumanie" msgstr "Rumanía" -#: scripts/_lib/map_template.py:478 +#: scripts/_lib/map_template.py:477 msgid "Bulgarie" msgstr "Bulgaria" -#: scripts/_lib/map_template.py:479 +#: scripts/_lib/map_template.py:478 msgid "Grèce" msgstr "Grecia" -#: scripts/_lib/map_template.py:480 +#: scripts/_lib/map_template.py:479 msgid "Allemagne" msgstr "Alemania" -#: scripts/_lib/map_template.py:481 +#: scripts/_lib/map_template.py:480 msgid "Slovaquie" msgstr "Eslovaquia" -#: scripts/_lib/map_template.py:482 +#: scripts/_lib/map_template.py:481 msgid "Suisse" msgstr "Suiza" -#: scripts/_lib/map_template.py:483 +#: scripts/_lib/map_template.py:482 msgid "Tchéquie" msgstr "Chequia" -#: scripts/_lib/map_template.py:484 +#: scripts/_lib/map_template.py:483 msgid "Luxembourg" msgstr "Luxemburgo" -#: scripts/_lib/map_template.py:485 +#: scripts/_lib/map_template.py:484 msgid "Belgique" msgstr "Bélgica" -#: scripts/_lib/map_template.py:486 +#: scripts/_lib/map_template.py:485 msgid "Pays-Bas" msgstr "Países Bajos" -#: scripts/_lib/map_template.py:487 +#: scripts/_lib/map_template.py:486 msgid "Malte" msgstr "Malta" -#: scripts/_lib/map_template.py:488 +#: scripts/_lib/map_template.py:487 msgid "Chypre" msgstr "Chipre" -#: scripts/_lib/map_template.py:489 +#: scripts/_lib/map_template.py:488 msgid "Royaume-Uni" msgstr "Reino Unido" @@ -1640,3 +1636,6 @@ msgstr "palo cortado" #~ msgid "Une erreur sur cette fiche ?" #~ msgstr "¿Algo no cuadra en esta ficha?" +#~ msgid "Recherche de cépage…" +#~ msgstr "Buscar variedad…" + diff --git a/locale/fr/LC_MESSAGES/messages.po b/locale/fr/LC_MESSAGES/messages.po index 9043015..a1037cd 100644 --- a/locale/fr/LC_MESSAGES/messages.po +++ b/locale/fr/LC_MESSAGES/messages.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-23 08:12+0200\n" +"POT-Creation-Date: 2026-09-23 09:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: fr\n" @@ -58,286 +58,282 @@ msgstr "nom d'appellation…" msgid "Recherche d'appellation…" msgstr "Recherche d'appellation…" -#: scripts/_lib/map_template.py:56 -msgid "Recherche de cépage…" -msgstr "Recherche de cépage…" - -#: scripts/_lib/map_template.py:58 +#: scripts/_lib/map_template.py:57 msgid "Rechercher une appellation, un cépage, une région…" msgstr "Rechercher une appellation, un cépage, une région…" -#: scripts/_lib/map_template.py:59 +#: scripts/_lib/map_template.py:58 msgid "Cépage principal uniquement" msgstr "Cépage principal uniquement" -#: scripts/_lib/map_template.py:60 +#: scripts/_lib/map_template.py:59 #, python-brace-format msgid "Aucun résultat pour « {q} »" msgstr "" -#: scripts/_lib/map_template.py:61 +#: scripts/_lib/map_template.py:60 msgid "Options" msgstr "Options" -#: scripts/_lib/map_template.py:62 +#: scripts/_lib/map_template.py:61 msgid "Filtres actifs" msgstr "Filtres actifs" -#: scripts/_lib/map_template.py:63 +#: scripts/_lib/map_template.py:62 msgid "Tout sélectionner" msgstr "Tout sélectionner" -#: scripts/_lib/map_template.py:65 +#: scripts/_lib/map_template.py:64 #, python-brace-format msgid "{p} appellations · {s} dénominations géographiques complémentaires" msgstr "{p} appellations · {s} dénominations géographiques complémentaires" -#: scripts/_lib/map_template.py:67 +#: scripts/_lib/map_template.py:66 #, python-brace-format msgid "{p} appellations" msgstr "{p} appellations" -#: scripts/_lib/map_template.py:68 +#: scripts/_lib/map_template.py:67 #, python-brace-format msgid "Ouvrir la fiche de {name}" msgstr "Ouvrir la fiche de {name}" -#: scripts/_lib/map_template.py:69 +#: scripts/_lib/map_template.py:68 msgid "Ouvrir la fiche" msgstr "Ouvrir la fiche" -#: scripts/_lib/map_template.py:70 +#: scripts/_lib/map_template.py:69 msgid "Inclure les spiritueux" msgstr "Inclure les spiritueux" -#: scripts/_lib/map_template.py:71 +#: scripts/_lib/map_template.py:70 msgid "Style de vin" msgstr "Style de vin" -#: scripts/_lib/map_template.py:72 +#: scripts/_lib/map_template.py:71 msgid "Classement" msgstr "Classement" -#: scripts/_lib/map_template.py:73 +#: scripts/_lib/map_template.py:72 msgid "Cépages principaux" msgstr "Cépages principaux" -#: scripts/_lib/map_template.py:74 +#: scripts/_lib/map_template.py:73 msgid "Cépages accessoires" msgstr "Cépages accessoires" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 -#: scripts/_lib/map_template.py:355 +#: scripts/_lib/map_template.py:74 scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:354 msgid "Cépages" msgstr "Cépages" -#: scripts/_lib/map_template.py:76 +#: scripts/_lib/map_template.py:75 msgid "Région" msgstr "Région" -#: scripts/_lib/map_template.py:77 +#: scripts/_lib/map_template.py:76 msgid "Appellation" msgstr "Appellation" -#: scripts/_lib/map_template.py:78 +#: scripts/_lib/map_template.py:77 msgid "Type" msgstr "Type" -#: scripts/_lib/map_template.py:79 +#: scripts/_lib/map_template.py:78 msgid "Origine protégée (AOP, AOC, DOC, DO…)" msgstr "Origine protégée (AOP, AOC, DOC, DO…)" -#: scripts/_lib/map_template.py:80 +#: scripts/_lib/map_template.py:79 msgid "Indication géographique (IGP, IGT, Landwein…)" msgstr "Indication géographique (IGP, IGT, Landwein…)" -#: scripts/_lib/map_template.py:81 +#: scripts/_lib/map_template.py:80 msgid "AOP" msgstr "AOP" -#: scripts/_lib/map_template.py:82 +#: scripts/_lib/map_template.py:81 msgid "IGP" msgstr "IGP" -#: scripts/_lib/map_template.py:83 +#: scripts/_lib/map_template.py:82 msgid "IG spiritueux" msgstr "IG spiritueux" -#: scripts/_lib/map_template.py:84 +#: scripts/_lib/map_template.py:83 msgid "Type d'appellation" msgstr "Type d'appellation" -#: scripts/_lib/map_template.py:85 +#: scripts/_lib/map_template.py:84 msgid "AOP (régime britannique)" msgstr "" -#: scripts/_lib/map_template.py:86 +#: scripts/_lib/map_template.py:85 msgid "IGP (régime britannique)" msgstr "" -#: scripts/_lib/map_template.py:87 +#: scripts/_lib/map_template.py:86 msgid "AOC (Suisse)" msgstr "" -#: scripts/_lib/map_template.py:88 +#: scripts/_lib/map_template.py:87 msgid "Source" msgstr "Source" -#: scripts/_lib/map_template.py:89 +#: scripts/_lib/map_template.py:88 msgid "Vue" msgstr "Vue" -#: scripts/_lib/map_template.py:90 +#: scripts/_lib/map_template.py:89 msgid "Simple" msgstr "Simple" -#: scripts/_lib/map_template.py:91 +#: scripts/_lib/map_template.py:90 msgid "Avancée" msgstr "Avancée" -#: scripts/_lib/map_template.py:92 +#: scripts/_lib/map_template.py:91 msgid "Thème" msgstr "Thème" -#: scripts/_lib/map_template.py:93 +#: scripts/_lib/map_template.py:92 msgid "Clair" msgstr "Clair" -#: scripts/_lib/map_template.py:94 +#: scripts/_lib/map_template.py:93 msgid "Sombre" msgstr "Sombre" -#: scripts/_lib/map_template.py:95 +#: scripts/_lib/map_template.py:94 msgid "Système" msgstr "Système" -#: scripts/_lib/map_template.py:96 +#: scripts/_lib/map_template.py:95 msgid "Afficher les IGP" msgstr "Afficher les IGP" -#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:96 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:361 msgid "rouge" msgstr "rouge" -#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:362 msgid "blanc" msgstr "blanc" -#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:363 msgid "rosé" msgstr "rosé" -#: scripts/_lib/map_template.py:100 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:364 msgid "mousseux" msgstr "mousseux" -#: scripts/_lib/map_template.py:101 +#: scripts/_lib/map_template.py:100 msgid "moelleux / liquoreux" msgstr "moelleux / liquoreux" -#: scripts/_lib/map_template.py:102 scripts/_lib/style_taxonomy.py:345 +#: scripts/_lib/map_template.py:101 scripts/_lib/style_taxonomy.py:345 msgid "oxydatif" msgstr "oxydatif" -#: scripts/_lib/map_template.py:103 +#: scripts/_lib/map_template.py:102 msgid "autre" msgstr "autre" -#: scripts/_lib/map_template.py:104 +#: scripts/_lib/map_template.py:103 msgid "Réinitialiser" msgstr "Réinitialiser" -#: scripts/_lib/map_template.py:105 +#: scripts/_lib/map_template.py:104 #, python-brace-format msgid "{n} appellations" msgstr "{n} appellations" -#: scripts/_lib/map_template.py:106 +#: scripts/_lib/map_template.py:105 #, python-brace-format msgid "{n} / {total} appellations" msgstr "{n} / {total} appellations" -#: scripts/_lib/map_template.py:107 +#: scripts/_lib/map_template.py:106 #, python-brace-format msgid "{n} masquées dans les IGP · afficher" msgstr "{n} masquées dans les IGP · afficher" -#: scripts/_lib/map_template.py:108 +#: scripts/_lib/map_template.py:107 msgid "Fermer" msgstr "Fermer" -#: scripts/_lib/map_template.py:109 +#: scripts/_lib/map_template.py:108 msgid "Détails de l'appellation" msgstr "Détails de l'appellation" -#: scripts/_lib/map_template.py:110 +#: scripts/_lib/map_template.py:109 #, python-brace-format msgid "Retirer le filtre {label}" msgstr "Retirer le filtre {label}" -#: scripts/_lib/map_template.py:111 +#: scripts/_lib/map_template.py:110 msgid "Filtres et options de la carte" msgstr "Filtres et options de la carte" -#: scripts/_lib/map_template.py:112 +#: scripts/_lib/map_template.py:111 msgid "Langue" msgstr "Langue" -#: scripts/_lib/map_template.py:113 +#: scripts/_lib/map_template.py:112 msgid "Carte des appellations viticoles" msgstr "Carte des appellations viticoles" -#: scripts/_lib/map_template.py:114 +#: scripts/_lib/map_template.py:113 msgid "Aller à la carte" msgstr "Aller à la carte" -#: scripts/_lib/map_template.py:115 +#: scripts/_lib/map_template.py:114 msgid "Styles" msgstr "Styles" -#: scripts/_lib/map_template.py:116 +#: scripts/_lib/map_template.py:115 msgid "Variétés d'intérêt" msgstr "Variétés d'intérêt" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 +#: scripts/_lib/map_template.py:116 scripts/_lib/map_template.py:357 msgid "Sources" msgstr "Sources" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:355 msgid "Terroir" msgstr "Terroir" -#: scripts/_lib/map_template.py:119 +#: scripts/_lib/map_template.py:118 #, python-brace-format msgid "Dűlők (lieux-dits) : {n}" msgstr "Dűlők (lieux-dits) : {n}" -#: scripts/_lib/map_template.py:120 +#: scripts/_lib/map_template.py:119 #, python-brace-format msgid "Menzioni geografiche aggiuntive (crus) : {n}" msgstr "Mentions géographiques complémentaires (crus) : {n}" -#: scripts/_lib/map_template.py:121 +#: scripts/_lib/map_template.py:120 msgid "Facteurs naturels" msgstr "Facteurs naturels" -#: scripts/_lib/map_template.py:122 +#: scripts/_lib/map_template.py:121 msgid "Facteurs humains" msgstr "Facteurs humains" -#: scripts/_lib/map_template.py:123 +#: scripts/_lib/map_template.py:122 msgid "Caractéristiques du produit" msgstr "Caractéristiques du produit" -#: scripts/_lib/map_template.py:124 +#: scripts/_lib/map_template.py:123 msgid "Lien terroir / vin" msgstr "Lien terroir / vin" -#: scripts/_lib/map_template.py:126 +#: scripts/_lib/map_template.py:125 #, python-brace-format msgid "" "Faits dégagés du Lien au terroir par interprétation automatique — voir la" @@ -346,53 +342,53 @@ msgstr "" "Faits dégagés du Lien au terroir par interprétation automatique — voir la" " {source}." -#: scripts/_lib/map_template.py:128 +#: scripts/_lib/map_template.py:127 msgid "source" msgstr "source" -#: scripts/_lib/map_template.py:129 +#: scripts/_lib/map_template.py:128 msgid "via Wikipedia · CC BY-SA 4.0" msgstr "via Wikipedia · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:131 +#: scripts/_lib/map_template.py:130 #, python-brace-format msgid "Citation textuelle du Lien au terroir — voir la {source}." msgstr "Citation textuelle du Lien au terroir — voir la {source}." -#: scripts/_lib/map_template.py:133 +#: scripts/_lib/map_template.py:132 msgid "à vérifier — texte source court" msgstr "" -#: scripts/_lib/map_template.py:135 +#: scripts/_lib/map_template.py:134 #, python-brace-format msgid "" "Ces repères décrivent l'appellation englobante {parent} — pas " "spécifiquement cette dénomination." msgstr "" -#: scripts/_lib/map_template.py:138 +#: scripts/_lib/map_template.py:137 msgid "sans région" msgstr "sans région" -#: scripts/_lib/map_template.py:139 +#: scripts/_lib/map_template.py:138 #, python-brace-format msgid "{n} commune(s) INAO" msgstr "{n} commune(s) INAO" -#: scripts/_lib/map_template.py:140 +#: scripts/_lib/map_template.py:139 #, python-brace-format msgid "{n} commune(s)" msgstr "{n} commune(s)" -#: scripts/_lib/map_template.py:141 +#: scripts/_lib/map_template.py:140 msgid "aire approchée" msgstr "aire approchée" -#: scripts/_lib/map_template.py:142 +#: scripts/_lib/map_template.py:141 msgid "aire approchée (à l'échelle communale)" msgstr "aire approchée (à l'échelle communale)" -#: scripts/_lib/map_template.py:144 +#: scripts/_lib/map_template.py:143 #, python-brace-format msgid "" "Aire approchée — pas de données parcellaires précises pour cette " @@ -401,7 +397,7 @@ msgstr "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de {umbrella}." -#: scripts/_lib/map_template.py:148 +#: scripts/_lib/map_template.py:147 msgid "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de l'appellation parente." @@ -409,7 +405,7 @@ msgstr "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de l'appellation parente." -#: scripts/_lib/map_template.py:152 +#: scripts/_lib/map_template.py:151 msgid "" "Aire approchée — pas de données parcellaires disponibles ; affichée comme" " l'emprise de la commune où se situe la dénomination." @@ -417,18 +413,18 @@ msgstr "" "Aire approchée — pas de données parcellaires disponibles ; affichée comme" " l'emprise de la commune où se situe la dénomination." -#: scripts/_lib/map_template.py:156 +#: scripts/_lib/map_template.py:155 #, python-brace-format msgid "" "Aire issue du lieu-dit cadastral « {lieu_dit} » (commune de {commune}, " "{source})." msgstr "" -#: scripts/_lib/map_template.py:159 +#: scripts/_lib/map_template.py:158 msgid "cadastre.data.gouv.fr" msgstr "" -#: scripts/_lib/map_template.py:161 +#: scripts/_lib/map_template.py:160 msgid "" "Aire approchée — reconstituée à partir des références parcellaires du " "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " @@ -438,7 +434,7 @@ msgstr "" "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " "limite officielle." -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:169 #, python-brace-format msgid "" "Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " @@ -446,7 +442,7 @@ msgid "" "parcellaire pour cette dénomination." msgstr "" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:174 #, python-brace-format msgid "" "Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " @@ -454,7 +450,7 @@ msgid "" "même ; les parcelles apparaissent en zoomant." msgstr "" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:179 #, python-brace-format msgid "" "Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " @@ -463,173 +459,173 @@ msgid "" " zoomant." msgstr "" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:185 #, python-brace-format msgid "Aire : zone de production officielle publiée par le géoportail ({region})." msgstr "" -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:189 msgid "" "Aire : zone de production officielle du MAPA (Zonas de Calidad " "Diferenciada : Vinos)." msgstr "" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:193 msgid "" "Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " "la résolution des communes ; peut inclure des terres non viticoles." msgstr "" -#: scripts/_lib/map_template.py:199 +#: scripts/_lib/map_template.py:198 msgid "" "Aire approchée — réunion des polygones des appellations membres (Bétard " "2022), à la résolution des communes." msgstr "" -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:202 msgid "" "Aire approchée — réunion des unités administratives nommées dans le " "document (limites Eurostat GISCO ou cadastre national), terres non " "viticoles comprises." msgstr "" -#: scripts/_lib/map_template.py:207 +#: scripts/_lib/map_template.py:206 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} appellations à ce point" -#: scripts/_lib/map_template.py:208 +#: scripts/_lib/map_template.py:207 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Cliquer à nouveau pour parcourir les autres" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:208 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Cahier des charges (BO Agri, PDF)" -#: scripts/_lib/map_template.py:210 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:210 msgid "homologué" msgstr "homologué" -#: scripts/_lib/map_template.py:212 +#: scripts/_lib/map_template.py:211 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:213 +#: scripts/_lib/map_template.py:212 msgid "Texte officiel INAO (show_texte)" msgstr "Texte officiel INAO (show_texte)" -#: scripts/_lib/map_template.py:214 +#: scripts/_lib/map_template.py:213 msgid "Fiche produit INAO" msgstr "Fiche produit INAO" -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:214 msgid "Site officiel de l'interprofession" msgstr "Site officiel de l'interprofession" -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:215 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Cahier des charges (EUR-Lex, document unique)" -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:216 msgid "Pliego de condiciones (national, PDF)" msgstr "Pliego de condiciones (national, PDF)" -#: scripts/_lib/map_template.py:218 +#: scripts/_lib/map_template.py:217 msgid "variétés ajoutées" msgstr "variétés ajoutées" -#: scripts/_lib/map_template.py:219 +#: scripts/_lib/map_template.py:218 msgid "Cahier des charges national (PDF)" msgstr "Cahier des charges national (PDF)" -#: scripts/_lib/map_template.py:220 +#: scripts/_lib/map_template.py:219 msgid "Spécification du produit (IGP, PDF)" msgstr "Spécification du produit (IGP, PDF)" -#: scripts/_lib/map_template.py:221 +#: scripts/_lib/map_template.py:220 msgid "Registre régional des cépages (PDF)" msgstr "Registre régional des cépages (PDF)" -#: scripts/_lib/map_template.py:222 +#: scripts/_lib/map_template.py:221 msgid "Registre eAmbrosia (UE)" msgstr "Registre eAmbrosia (UE)" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:222 msgid "Numéro de dossier" msgstr "Numéro de dossier" -#: scripts/_lib/map_template.py:224 +#: scripts/_lib/map_template.py:223 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Règlement cantonal sur la vigne et le vin" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:224 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Répertoire suisse des AOC (OFAG/BLW)" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:225 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Cahier des charges (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:226 msgid "Registre des IG du Royaume-Uni" msgstr "Registre des IG du Royaume-Uni" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:227 msgid "Légende couleurs" msgstr "Légende couleurs" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:228 msgid "Bassin viticole" msgstr "Bassin viticole" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:229 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Plus l'aire est petite, plus la teinte est dense." -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:231 msgid "principal — variété de la cuvée" msgstr "principal — variété de la cuvée" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:232 msgid "accessoire — assemblage limité" msgstr "accessoire — assemblage limité" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:233 msgid "intérêt — observation/conservation" msgstr "intérêt — observation/conservation" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:235 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" " dans le caderno de especificações." msgstr "" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:239 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " "catalogue VIVC le recense comme synonyme du Trebbiano Toscano." msgstr "" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:244 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "" -#: scripts/_lib/map_template.py:247 +#: scripts/_lib/map_template.py:246 msgid "aidez-nous à le trouver" msgstr "aidez-nous à le trouver" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:250 msgid "Annulée" msgstr "Annulée" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:252 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -640,142 +636,142 @@ msgstr "" "bouteilles, étiquettes et supports de formation plus anciens peuvent " "encore la mentionner ; elle est conservée ici à titre de référence." -#: scripts/_lib/map_template.py:257 +#: scripts/_lib/map_template.py:256 #, python-brace-format msgid "Acte national : {act}." msgstr "Acte national : {act}." -#: scripts/_lib/map_template.py:258 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "Elle est remplacée par {successor}." -#: scripts/_lib/map_template.py:259 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Enregistrement annulé le {date}" -#: scripts/_lib/map_template.py:265 +#: scripts/_lib/map_template.py:264 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "" -#: scripts/_lib/map_template.py:267 +#: scripts/_lib/map_template.py:266 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "" -#: scripts/_lib/map_template.py:268 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "" -#: scripts/_lib/map_template.py:269 +#: scripts/_lib/map_template.py:268 #, fuzzy msgid "le registre eAmbrosia de l'UE" msgstr "Registre eAmbrosia (UE)" -#: scripts/_lib/map_template.py:270 +#: scripts/_lib/map_template.py:269 #, python-brace-format msgid "le canton de {canton}" msgstr "" -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:270 msgid "(français)" msgstr "(français)" -#: scripts/_lib/map_template.py:272 +#: scripts/_lib/map_template.py:271 msgid "Texte source en français" msgstr "Texte source en français" -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:272 msgid "(español)" msgstr "(español)" -#: scripts/_lib/map_template.py:274 +#: scripts/_lib/map_template.py:273 msgid "Texte source en espagnol" msgstr "Texte source en espagnol" -#: scripts/_lib/map_template.py:275 +#: scripts/_lib/map_template.py:274 msgid "(português)" msgstr "" -#: scripts/_lib/map_template.py:276 +#: scripts/_lib/map_template.py:275 msgid "Texte source en portugais" msgstr "Texte source en portugais" -#: scripts/_lib/map_template.py:277 +#: scripts/_lib/map_template.py:276 msgid "Filtres" msgstr "Filtres" -#: scripts/_lib/map_template.py:278 +#: scripts/_lib/map_template.py:277 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Traduit de {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:279 +#: scripts/_lib/map_template.py:278 msgid "Wikipédia en anglais" msgstr "Wikipédia en anglais" -#: scripts/_lib/map_template.py:280 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en français" msgstr "Wikipédia en français" -#: scripts/_lib/map_template.py:281 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en espagnol" msgstr "Wikipédia en espagnol" -#: scripts/_lib/map_template.py:282 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en néerlandais" msgstr "Wikipédia en néerlandais" -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en portugais" msgstr "Wikipédia en portugais" -#: scripts/_lib/map_template.py:284 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en croate" msgstr "Wikipédia en croate" -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:284 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "" -#: scripts/_lib/map_template.py:286 +#: scripts/_lib/map_template.py:285 #, python-brace-format msgid "VIVC #{id}" msgstr "" -#: scripts/_lib/map_template.py:287 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Traduction automatique depuis {source}" -#: scripts/_lib/map_template.py:288 +#: scripts/_lib/map_template.py:287 msgid "le cahier des charges" msgstr "le cahier des charges" -#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 +#: scripts/_lib/map_template.py:288 scripts/_lib/map_template.py:290 msgid "le pliego de condiciones" msgstr "le pliego de condiciones" -#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le caderno de especificações" msgstr "" -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:292 msgid "Dénomination géographique complémentaire de" msgstr "Dénomination géographique complémentaire de" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:293 msgid "À propos" msgstr "À propos" -#: scripts/_lib/map_template.py:295 +#: scripts/_lib/map_template.py:294 msgid "À propos d'Open Wine Map" msgstr "À propos d'Open Wine Map" -#: scripts/_lib/map_template.py:297 +#: scripts/_lib/map_template.py:296 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -787,7 +783,7 @@ msgstr "" "et IGP, les régulateurs nationaux, le répertoire fédéral suisse des AOC " "cantonales et le registre britannique des indications géographiques." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:303 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -809,12 +805,12 @@ msgstr "" "automatiquement. Chaque élément porte sa propre ligne d'attribution dans " "le panneau." -#: scripts/_lib/map_template.py:313 +#: scripts/_lib/map_template.py:312 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Réalisé avec ♡ par {devloed}." -#: scripts/_lib/map_template.py:315 +#: scripts/_lib/map_template.py:314 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -839,7 +835,7 @@ msgstr "" "et al.). Tout extrait Wikipedia est signalé sur place. Détails et " "licences dans le {readme}." -#: scripts/_lib/map_template.py:326 +#: scripts/_lib/map_template.py:325 #, python-brace-format msgid "" "Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" @@ -850,12 +846,12 @@ msgid "" "tous les zooms." msgstr "" -#: scripts/_lib/map_template.py:333 +#: scripts/_lib/map_template.py:332 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Suggestions et pull requests bienvenues sur {github}." -#: scripts/_lib/map_template.py:335 +#: scripts/_lib/map_template.py:334 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -872,20 +868,20 @@ msgstr "" "adresse IP conservée, pas de suivi entre sites ni entre appareils — d'où " "l'absence de bandeau de consentement." -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:341 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Suivez les mises à jour sur {bluesky}." -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:342 msgid "e-mail" msgstr "e-mail" -#: scripts/_lib/map_template.py:344 +#: scripts/_lib/map_template.py:343 msgid "E-mail copié dans le presse-papiers" msgstr "E-mail copié dans le presse-papiers" -#: scripts/_lib/map_template.py:346 +#: scripts/_lib/map_template.py:345 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -894,48 +890,48 @@ msgstr "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " "depuis la fiche de l'appellation, ou par {email}." -#: scripts/_lib/map_template.py:352 +#: scripts/_lib/map_template.py:351 msgid "Signaler une erreur" msgstr "Signaler une erreur" -#: scripts/_lib/map_template.py:353 +#: scripts/_lib/map_template.py:352 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "Un clic nous dit quoi vérifier — sans compte." -#: scripts/_lib/map_template.py:354 +#: scripts/_lib/map_template.py:353 msgid "Aire / contour" msgstr "Aire / contour" -#: scripts/_lib/map_template.py:357 +#: scripts/_lib/map_template.py:356 msgid "Nom / type" msgstr "Nom / type" -#: scripts/_lib/map_template.py:359 +#: scripts/_lib/map_template.py:358 msgid "Autre" msgstr "Autre" -#: scripts/_lib/map_template.py:360 +#: scripts/_lib/map_template.py:359 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Merci, c'est noté : {aspect}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:361 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Précisez si vous voulez (facultatif, sans données personnelles)" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:363 msgid "Envoyer" msgstr "Envoyer" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:366 msgid "Géométrie" msgstr "Géométrie" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:367 msgid "Envoyé, merci !" msgstr "Envoyé, merci !" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:369 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " @@ -944,7 +940,7 @@ msgstr "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:373 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -957,15 +953,15 @@ msgstr "" "supplémentaires affineront la qualité des données et étendront la " "couverture au-delà de l'UE, de la Suisse et du Royaume-Uni." -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:378 msgid "Toutes les appellations" msgstr "Toutes les appellations" -#: scripts/_lib/map_template.py:380 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "Toutes les appellations viticoles — Open Wine Map" -#: scripts/_lib/map_template.py:382 +#: scripts/_lib/map_template.py:381 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -976,7 +972,7 @@ msgstr "" "classées par pays : AOP et IGP de l'UE avec leurs termes traditionnels " "(AOC, DOCG, DOQ…), AOC suisses et IG britanniques." -#: scripts/_lib/map_template.py:387 +#: scripts/_lib/map_template.py:386 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " @@ -985,185 +981,185 @@ msgstr "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " "{map_link}." -#: scripts/_lib/map_template.py:390 +#: scripts/_lib/map_template.py:389 msgid "carte interactive" msgstr "carte interactive" -#: scripts/_lib/map_template.py:391 +#: scripts/_lib/map_template.py:390 msgid "Liste des appellations par pays" msgstr "Liste des appellations par pays" -#: scripts/_lib/map_template.py:392 +#: scripts/_lib/map_template.py:391 msgid "Appellation parente" msgstr "Appellation parente" -#: scripts/_lib/map_template.py:393 +#: scripts/_lib/map_template.py:392 msgid "Dénominations rattachées" msgstr "Dénominations rattachées" -#: scripts/_lib/map_template.py:395 +#: scripts/_lib/map_template.py:394 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Parcourir la liste complète des appellations : {browse_link}." -#: scripts/_lib/map_template.py:397 +#: scripts/_lib/map_template.py:396 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Données mises à jour le {date}." -#: scripts/_lib/map_template.py:412 +#: scripts/_lib/map_template.py:411 msgid "BOURGOGNE" msgstr "Bourgogne" -#: scripts/_lib/map_template.py:413 +#: scripts/_lib/map_template.py:412 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:414 +#: scripts/_lib/map_template.py:413 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:415 +#: scripts/_lib/map_template.py:414 msgid "SAVOIE" msgstr "Savoie" -#: scripts/_lib/map_template.py:416 +#: scripts/_lib/map_template.py:415 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:417 +#: scripts/_lib/map_template.py:416 msgid "ALSACE ET EST" msgstr "Alsace et Est" -#: scripts/_lib/map_template.py:418 +#: scripts/_lib/map_template.py:417 msgid "VAL DE LOIRE" msgstr "Val de Loire" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:418 msgid "SUD-OUEST" msgstr "Sud-Ouest" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:419 msgid "VALLEE DU RHÔNE" msgstr "Vallée du Rhône" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:420 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Roussillon" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:421 msgid "TOULOUSE-PYRENEES" msgstr "Toulouse-Pyrénées" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:422 msgid "PROVENCE-CORSE" msgstr "Provence-Corse" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:423 msgid "CHAMPAGNE" msgstr "Champagne" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:424 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Eaux-de-vie de cidre" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:425 msgid "VIN DOUX NATURELS" msgstr "Vins doux naturels" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:426 msgid "COGNAC" msgstr "Cognac" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:427 msgid "ARMAGNAC" msgstr "Armagnac" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:428 msgid "RHUM" msgstr "Rhum" -#: scripts/_lib/map_template.py:469 +#: scripts/_lib/map_template.py:468 msgid "France" msgstr "France" -#: scripts/_lib/map_template.py:470 +#: scripts/_lib/map_template.py:469 msgid "Espagne" msgstr "Espagne" -#: scripts/_lib/map_template.py:471 +#: scripts/_lib/map_template.py:470 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:472 +#: scripts/_lib/map_template.py:471 msgid "Italie" msgstr "Italie" -#: scripts/_lib/map_template.py:473 +#: scripts/_lib/map_template.py:472 msgid "Autriche" msgstr "Autriche" -#: scripts/_lib/map_template.py:474 +#: scripts/_lib/map_template.py:473 msgid "Slovénie" msgstr "Slovénie" -#: scripts/_lib/map_template.py:475 +#: scripts/_lib/map_template.py:474 msgid "Croatie" msgstr "Croatie" -#: scripts/_lib/map_template.py:476 +#: scripts/_lib/map_template.py:475 msgid "Hongrie" msgstr "Hongrie" -#: scripts/_lib/map_template.py:477 +#: scripts/_lib/map_template.py:476 msgid "Roumanie" msgstr "Roumanie" -#: scripts/_lib/map_template.py:478 +#: scripts/_lib/map_template.py:477 msgid "Bulgarie" msgstr "Bulgarie" -#: scripts/_lib/map_template.py:479 +#: scripts/_lib/map_template.py:478 msgid "Grèce" msgstr "Grèce" -#: scripts/_lib/map_template.py:480 +#: scripts/_lib/map_template.py:479 msgid "Allemagne" msgstr "Allemagne" -#: scripts/_lib/map_template.py:481 +#: scripts/_lib/map_template.py:480 msgid "Slovaquie" msgstr "Slovaquie" -#: scripts/_lib/map_template.py:482 +#: scripts/_lib/map_template.py:481 msgid "Suisse" msgstr "Suisse" -#: scripts/_lib/map_template.py:483 +#: scripts/_lib/map_template.py:482 msgid "Tchéquie" msgstr "Tchéquie" -#: scripts/_lib/map_template.py:484 +#: scripts/_lib/map_template.py:483 msgid "Luxembourg" msgstr "Luxembourg" -#: scripts/_lib/map_template.py:485 +#: scripts/_lib/map_template.py:484 msgid "Belgique" msgstr "Belgique" -#: scripts/_lib/map_template.py:486 +#: scripts/_lib/map_template.py:485 msgid "Pays-Bas" msgstr "Pays-Bas" -#: scripts/_lib/map_template.py:487 +#: scripts/_lib/map_template.py:486 msgid "Malte" msgstr "Malte" -#: scripts/_lib/map_template.py:488 +#: scripts/_lib/map_template.py:487 msgid "Chypre" msgstr "Chypre" -#: scripts/_lib/map_template.py:489 +#: scripts/_lib/map_template.py:488 msgid "Royaume-Uni" msgstr "Royaume-Uni" @@ -1605,3 +1601,6 @@ msgstr "palo cortado" #~ msgid "Une erreur sur cette fiche ?" #~ msgstr "Une erreur sur cette fiche ?" +#~ msgid "Recherche de cépage…" +#~ msgstr "Recherche de cépage…" + diff --git a/locale/messages.pot b/locale/messages.pot index 43d340f..b3a0f43 100644 --- a/locale/messages.pot +++ b/locale/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-23 08:12+0200\n" +"POT-Creation-Date: 2026-09-23 09:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -59,376 +59,372 @@ msgstr "" msgid "Recherche d'appellation…" msgstr "" -#: scripts/_lib/map_template.py:56 -msgid "Recherche de cépage…" -msgstr "" - -#: scripts/_lib/map_template.py:58 +#: scripts/_lib/map_template.py:57 msgid "Rechercher une appellation, un cépage, une région…" msgstr "" -#: scripts/_lib/map_template.py:59 +#: scripts/_lib/map_template.py:58 msgid "Cépage principal uniquement" msgstr "" -#: scripts/_lib/map_template.py:60 +#: scripts/_lib/map_template.py:59 #, python-brace-format msgid "Aucun résultat pour « {q} »" msgstr "" -#: scripts/_lib/map_template.py:61 +#: scripts/_lib/map_template.py:60 msgid "Options" msgstr "" -#: scripts/_lib/map_template.py:62 +#: scripts/_lib/map_template.py:61 msgid "Filtres actifs" msgstr "" -#: scripts/_lib/map_template.py:63 +#: scripts/_lib/map_template.py:62 msgid "Tout sélectionner" msgstr "" -#: scripts/_lib/map_template.py:65 +#: scripts/_lib/map_template.py:64 #, python-brace-format msgid "{p} appellations · {s} dénominations géographiques complémentaires" msgstr "" -#: scripts/_lib/map_template.py:67 +#: scripts/_lib/map_template.py:66 #, python-brace-format msgid "{p} appellations" msgstr "" -#: scripts/_lib/map_template.py:68 +#: scripts/_lib/map_template.py:67 #, python-brace-format msgid "Ouvrir la fiche de {name}" msgstr "" -#: scripts/_lib/map_template.py:69 +#: scripts/_lib/map_template.py:68 msgid "Ouvrir la fiche" msgstr "" -#: scripts/_lib/map_template.py:70 +#: scripts/_lib/map_template.py:69 msgid "Inclure les spiritueux" msgstr "" -#: scripts/_lib/map_template.py:71 +#: scripts/_lib/map_template.py:70 msgid "Style de vin" msgstr "" -#: scripts/_lib/map_template.py:72 +#: scripts/_lib/map_template.py:71 msgid "Classement" msgstr "" -#: scripts/_lib/map_template.py:73 +#: scripts/_lib/map_template.py:72 msgid "Cépages principaux" msgstr "" -#: scripts/_lib/map_template.py:74 +#: scripts/_lib/map_template.py:73 msgid "Cépages accessoires" msgstr "" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 -#: scripts/_lib/map_template.py:355 +#: scripts/_lib/map_template.py:74 scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:354 msgid "Cépages" msgstr "" -#: scripts/_lib/map_template.py:76 +#: scripts/_lib/map_template.py:75 msgid "Région" msgstr "" -#: scripts/_lib/map_template.py:77 +#: scripts/_lib/map_template.py:76 msgid "Appellation" msgstr "" -#: scripts/_lib/map_template.py:78 +#: scripts/_lib/map_template.py:77 msgid "Type" msgstr "" -#: scripts/_lib/map_template.py:79 +#: scripts/_lib/map_template.py:78 msgid "Origine protégée (AOP, AOC, DOC, DO…)" msgstr "" -#: scripts/_lib/map_template.py:80 +#: scripts/_lib/map_template.py:79 msgid "Indication géographique (IGP, IGT, Landwein…)" msgstr "" -#: scripts/_lib/map_template.py:81 +#: scripts/_lib/map_template.py:80 msgid "AOP" msgstr "" -#: scripts/_lib/map_template.py:82 +#: scripts/_lib/map_template.py:81 msgid "IGP" msgstr "" -#: scripts/_lib/map_template.py:83 +#: scripts/_lib/map_template.py:82 msgid "IG spiritueux" msgstr "" -#: scripts/_lib/map_template.py:84 +#: scripts/_lib/map_template.py:83 msgid "Type d'appellation" msgstr "" -#: scripts/_lib/map_template.py:85 +#: scripts/_lib/map_template.py:84 msgid "AOP (régime britannique)" msgstr "" -#: scripts/_lib/map_template.py:86 +#: scripts/_lib/map_template.py:85 msgid "IGP (régime britannique)" msgstr "" -#: scripts/_lib/map_template.py:87 +#: scripts/_lib/map_template.py:86 msgid "AOC (Suisse)" msgstr "" -#: scripts/_lib/map_template.py:88 +#: scripts/_lib/map_template.py:87 msgid "Source" msgstr "" -#: scripts/_lib/map_template.py:89 +#: scripts/_lib/map_template.py:88 msgid "Vue" msgstr "" -#: scripts/_lib/map_template.py:90 +#: scripts/_lib/map_template.py:89 msgid "Simple" msgstr "" -#: scripts/_lib/map_template.py:91 +#: scripts/_lib/map_template.py:90 msgid "Avancée" msgstr "" -#: scripts/_lib/map_template.py:92 +#: scripts/_lib/map_template.py:91 msgid "Thème" msgstr "" -#: scripts/_lib/map_template.py:93 +#: scripts/_lib/map_template.py:92 msgid "Clair" msgstr "" -#: scripts/_lib/map_template.py:94 +#: scripts/_lib/map_template.py:93 msgid "Sombre" msgstr "" -#: scripts/_lib/map_template.py:95 +#: scripts/_lib/map_template.py:94 msgid "Système" msgstr "" -#: scripts/_lib/map_template.py:96 +#: scripts/_lib/map_template.py:95 msgid "Afficher les IGP" msgstr "" -#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:96 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:361 msgid "rouge" msgstr "" -#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:362 msgid "blanc" msgstr "" -#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:363 msgid "rosé" msgstr "" -#: scripts/_lib/map_template.py:100 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:364 msgid "mousseux" msgstr "" -#: scripts/_lib/map_template.py:101 +#: scripts/_lib/map_template.py:100 msgid "moelleux / liquoreux" msgstr "" -#: scripts/_lib/map_template.py:102 scripts/_lib/style_taxonomy.py:345 +#: scripts/_lib/map_template.py:101 scripts/_lib/style_taxonomy.py:345 msgid "oxydatif" msgstr "" -#: scripts/_lib/map_template.py:103 +#: scripts/_lib/map_template.py:102 msgid "autre" msgstr "" -#: scripts/_lib/map_template.py:104 +#: scripts/_lib/map_template.py:103 msgid "Réinitialiser" msgstr "" -#: scripts/_lib/map_template.py:105 +#: scripts/_lib/map_template.py:104 #, python-brace-format msgid "{n} appellations" msgstr "" -#: scripts/_lib/map_template.py:106 +#: scripts/_lib/map_template.py:105 #, python-brace-format msgid "{n} / {total} appellations" msgstr "" -#: scripts/_lib/map_template.py:107 +#: scripts/_lib/map_template.py:106 #, python-brace-format msgid "{n} masquées dans les IGP · afficher" msgstr "" -#: scripts/_lib/map_template.py:108 +#: scripts/_lib/map_template.py:107 msgid "Fermer" msgstr "" -#: scripts/_lib/map_template.py:109 +#: scripts/_lib/map_template.py:108 msgid "Détails de l'appellation" msgstr "" -#: scripts/_lib/map_template.py:110 +#: scripts/_lib/map_template.py:109 #, python-brace-format msgid "Retirer le filtre {label}" msgstr "" -#: scripts/_lib/map_template.py:111 +#: scripts/_lib/map_template.py:110 msgid "Filtres et options de la carte" msgstr "" -#: scripts/_lib/map_template.py:112 +#: scripts/_lib/map_template.py:111 msgid "Langue" msgstr "" -#: scripts/_lib/map_template.py:113 +#: scripts/_lib/map_template.py:112 msgid "Carte des appellations viticoles" msgstr "" -#: scripts/_lib/map_template.py:114 +#: scripts/_lib/map_template.py:113 msgid "Aller à la carte" msgstr "" -#: scripts/_lib/map_template.py:115 +#: scripts/_lib/map_template.py:114 msgid "Styles" msgstr "" -#: scripts/_lib/map_template.py:116 +#: scripts/_lib/map_template.py:115 msgid "Variétés d'intérêt" msgstr "" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 +#: scripts/_lib/map_template.py:116 scripts/_lib/map_template.py:357 msgid "Sources" msgstr "" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:355 msgid "Terroir" msgstr "" -#: scripts/_lib/map_template.py:119 +#: scripts/_lib/map_template.py:118 #, python-brace-format msgid "Dűlők (lieux-dits) : {n}" msgstr "" -#: scripts/_lib/map_template.py:120 +#: scripts/_lib/map_template.py:119 #, python-brace-format msgid "Menzioni geografiche aggiuntive (crus) : {n}" msgstr "" -#: scripts/_lib/map_template.py:121 +#: scripts/_lib/map_template.py:120 msgid "Facteurs naturels" msgstr "" -#: scripts/_lib/map_template.py:122 +#: scripts/_lib/map_template.py:121 msgid "Facteurs humains" msgstr "" -#: scripts/_lib/map_template.py:123 +#: scripts/_lib/map_template.py:122 msgid "Caractéristiques du produit" msgstr "" -#: scripts/_lib/map_template.py:124 +#: scripts/_lib/map_template.py:123 msgid "Lien terroir / vin" msgstr "" -#: scripts/_lib/map_template.py:126 +#: scripts/_lib/map_template.py:125 #, python-brace-format msgid "" "Faits dégagés du Lien au terroir par interprétation automatique — voir la" " {source}." msgstr "" -#: scripts/_lib/map_template.py:128 +#: scripts/_lib/map_template.py:127 msgid "source" msgstr "" -#: scripts/_lib/map_template.py:129 +#: scripts/_lib/map_template.py:128 msgid "via Wikipedia · CC BY-SA 4.0" msgstr "" -#: scripts/_lib/map_template.py:131 +#: scripts/_lib/map_template.py:130 #, python-brace-format msgid "Citation textuelle du Lien au terroir — voir la {source}." msgstr "" -#: scripts/_lib/map_template.py:133 +#: scripts/_lib/map_template.py:132 msgid "à vérifier — texte source court" msgstr "" -#: scripts/_lib/map_template.py:135 +#: scripts/_lib/map_template.py:134 #, python-brace-format msgid "" "Ces repères décrivent l'appellation englobante {parent} — pas " "spécifiquement cette dénomination." msgstr "" -#: scripts/_lib/map_template.py:138 +#: scripts/_lib/map_template.py:137 msgid "sans région" msgstr "" -#: scripts/_lib/map_template.py:139 +#: scripts/_lib/map_template.py:138 #, python-brace-format msgid "{n} commune(s) INAO" msgstr "" -#: scripts/_lib/map_template.py:140 +#: scripts/_lib/map_template.py:139 #, python-brace-format msgid "{n} commune(s)" msgstr "" -#: scripts/_lib/map_template.py:141 +#: scripts/_lib/map_template.py:140 msgid "aire approchée" msgstr "" -#: scripts/_lib/map_template.py:142 +#: scripts/_lib/map_template.py:141 msgid "aire approchée (à l'échelle communale)" msgstr "" -#: scripts/_lib/map_template.py:144 +#: scripts/_lib/map_template.py:143 #, python-brace-format msgid "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de {umbrella}." msgstr "" -#: scripts/_lib/map_template.py:148 +#: scripts/_lib/map_template.py:147 msgid "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de l'appellation parente." msgstr "" -#: scripts/_lib/map_template.py:152 +#: scripts/_lib/map_template.py:151 msgid "" "Aire approchée — pas de données parcellaires disponibles ; affichée comme" " l'emprise de la commune où se situe la dénomination." msgstr "" -#: scripts/_lib/map_template.py:156 +#: scripts/_lib/map_template.py:155 #, python-brace-format msgid "" "Aire issue du lieu-dit cadastral « {lieu_dit} » (commune de {commune}, " "{source})." msgstr "" -#: scripts/_lib/map_template.py:159 +#: scripts/_lib/map_template.py:158 msgid "cadastre.data.gouv.fr" msgstr "" -#: scripts/_lib/map_template.py:161 +#: scripts/_lib/map_template.py:160 msgid "" "Aire approchée — reconstituée à partir des références parcellaires du " "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " "limite officielle." msgstr "" -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:169 #, python-brace-format msgid "" "Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " @@ -436,7 +432,7 @@ msgid "" "parcellaire pour cette dénomination." msgstr "" -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:174 #, python-brace-format msgid "" "Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " @@ -444,7 +440,7 @@ msgid "" "même ; les parcelles apparaissent en zoomant." msgstr "" -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:179 #, python-brace-format msgid "" "Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " @@ -453,173 +449,173 @@ msgid "" " zoomant." msgstr "" -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:185 #, python-brace-format msgid "Aire : zone de production officielle publiée par le géoportail ({region})." msgstr "" -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:189 msgid "" "Aire : zone de production officielle du MAPA (Zonas de Calidad " "Diferenciada : Vinos)." msgstr "" -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:193 msgid "" "Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " "la résolution des communes ; peut inclure des terres non viticoles." msgstr "" -#: scripts/_lib/map_template.py:199 +#: scripts/_lib/map_template.py:198 msgid "" "Aire approchée — réunion des polygones des appellations membres (Bétard " "2022), à la résolution des communes." msgstr "" -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:202 msgid "" "Aire approchée — réunion des unités administratives nommées dans le " "document (limites Eurostat GISCO ou cadastre national), terres non " "viticoles comprises." msgstr "" -#: scripts/_lib/map_template.py:207 +#: scripts/_lib/map_template.py:206 #, python-brace-format msgid "{n} appellations à ce point" msgstr "" -#: scripts/_lib/map_template.py:208 +#: scripts/_lib/map_template.py:207 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:208 msgid "Cahier des charges (BO Agri, PDF)" msgstr "" -#: scripts/_lib/map_template.py:210 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:210 msgid "homologué" msgstr "" -#: scripts/_lib/map_template.py:212 +#: scripts/_lib/map_template.py:211 msgid "JORF" msgstr "" -#: scripts/_lib/map_template.py:213 +#: scripts/_lib/map_template.py:212 msgid "Texte officiel INAO (show_texte)" msgstr "" -#: scripts/_lib/map_template.py:214 +#: scripts/_lib/map_template.py:213 msgid "Fiche produit INAO" msgstr "" -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:214 msgid "Site officiel de l'interprofession" msgstr "" -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:215 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "" -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:216 msgid "Pliego de condiciones (national, PDF)" msgstr "" -#: scripts/_lib/map_template.py:218 +#: scripts/_lib/map_template.py:217 msgid "variétés ajoutées" msgstr "" -#: scripts/_lib/map_template.py:219 +#: scripts/_lib/map_template.py:218 msgid "Cahier des charges national (PDF)" msgstr "" -#: scripts/_lib/map_template.py:220 +#: scripts/_lib/map_template.py:219 msgid "Spécification du produit (IGP, PDF)" msgstr "" -#: scripts/_lib/map_template.py:221 +#: scripts/_lib/map_template.py:220 msgid "Registre régional des cépages (PDF)" msgstr "" -#: scripts/_lib/map_template.py:222 +#: scripts/_lib/map_template.py:221 msgid "Registre eAmbrosia (UE)" msgstr "" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:222 msgid "Numéro de dossier" msgstr "" -#: scripts/_lib/map_template.py:224 +#: scripts/_lib/map_template.py:223 msgid "Règlement cantonal sur la vigne et le vin" msgstr "" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:224 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:225 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:226 msgid "Registre des IG du Royaume-Uni" msgstr "" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:227 msgid "Légende couleurs" msgstr "" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:228 msgid "Bassin viticole" msgstr "" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:229 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "" -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:231 msgid "principal — variété de la cuvée" msgstr "" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:232 msgid "accessoire — assemblage limité" msgstr "" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:233 msgid "intérêt — observation/conservation" msgstr "" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:235 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" " dans le caderno de especificações." msgstr "" -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:239 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " "catalogue VIVC le recense comme synonyme du Trebbiano Toscano." msgstr "" -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:244 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "" -#: scripts/_lib/map_template.py:247 +#: scripts/_lib/map_template.py:246 msgid "aidez-nous à le trouver" msgstr "" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:250 msgid "Annulée" msgstr "" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:252 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -627,141 +623,141 @@ msgid "" "encore la mentionner ; elle est conservée ici à titre de référence." msgstr "" -#: scripts/_lib/map_template.py:257 +#: scripts/_lib/map_template.py:256 #, python-brace-format msgid "Acte national : {act}." msgstr "" -#: scripts/_lib/map_template.py:258 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "" -#: scripts/_lib/map_template.py:259 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "" -#: scripts/_lib/map_template.py:265 +#: scripts/_lib/map_template.py:264 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "" -#: scripts/_lib/map_template.py:267 +#: scripts/_lib/map_template.py:266 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "" -#: scripts/_lib/map_template.py:268 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "" -#: scripts/_lib/map_template.py:269 +#: scripts/_lib/map_template.py:268 msgid "le registre eAmbrosia de l'UE" msgstr "" -#: scripts/_lib/map_template.py:270 +#: scripts/_lib/map_template.py:269 #, python-brace-format msgid "le canton de {canton}" msgstr "" -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:270 msgid "(français)" msgstr "" -#: scripts/_lib/map_template.py:272 +#: scripts/_lib/map_template.py:271 msgid "Texte source en français" msgstr "" -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:272 msgid "(español)" msgstr "" -#: scripts/_lib/map_template.py:274 +#: scripts/_lib/map_template.py:273 msgid "Texte source en espagnol" msgstr "" -#: scripts/_lib/map_template.py:275 +#: scripts/_lib/map_template.py:274 msgid "(português)" msgstr "" -#: scripts/_lib/map_template.py:276 +#: scripts/_lib/map_template.py:275 msgid "Texte source en portugais" msgstr "" -#: scripts/_lib/map_template.py:277 +#: scripts/_lib/map_template.py:276 msgid "Filtres" msgstr "" -#: scripts/_lib/map_template.py:278 +#: scripts/_lib/map_template.py:277 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "" -#: scripts/_lib/map_template.py:279 +#: scripts/_lib/map_template.py:278 msgid "Wikipédia en anglais" msgstr "" -#: scripts/_lib/map_template.py:280 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en français" msgstr "" -#: scripts/_lib/map_template.py:281 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en espagnol" msgstr "" -#: scripts/_lib/map_template.py:282 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en néerlandais" msgstr "" -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en portugais" msgstr "" -#: scripts/_lib/map_template.py:284 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en croate" msgstr "" -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:284 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "" -#: scripts/_lib/map_template.py:286 +#: scripts/_lib/map_template.py:285 #, python-brace-format msgid "VIVC #{id}" msgstr "" -#: scripts/_lib/map_template.py:287 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "" -#: scripts/_lib/map_template.py:288 +#: scripts/_lib/map_template.py:287 msgid "le cahier des charges" msgstr "" -#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 +#: scripts/_lib/map_template.py:288 scripts/_lib/map_template.py:290 msgid "le pliego de condiciones" msgstr "" -#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le caderno de especificações" msgstr "" -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:292 msgid "Dénomination géographique complémentaire de" msgstr "" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:293 msgid "À propos" msgstr "" -#: scripts/_lib/map_template.py:295 +#: scripts/_lib/map_template.py:294 msgid "À propos d'Open Wine Map" msgstr "" -#: scripts/_lib/map_template.py:297 +#: scripts/_lib/map_template.py:296 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -769,7 +765,7 @@ msgid "" "cantonales et le registre britannique des indications géographiques." msgstr "" -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:303 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -782,12 +778,12 @@ msgid "" "le panneau." msgstr "" -#: scripts/_lib/map_template.py:313 +#: scripts/_lib/map_template.py:312 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "" -#: scripts/_lib/map_template.py:315 +#: scripts/_lib/map_template.py:314 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -802,7 +798,7 @@ msgid "" "licences dans le {readme}." msgstr "" -#: scripts/_lib/map_template.py:326 +#: scripts/_lib/map_template.py:325 #, python-brace-format msgid "" "Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" @@ -813,12 +809,12 @@ msgid "" "tous les zooms." msgstr "" -#: scripts/_lib/map_template.py:333 +#: scripts/_lib/map_template.py:332 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "" -#: scripts/_lib/map_template.py:335 +#: scripts/_lib/map_template.py:334 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -829,75 +825,75 @@ msgid "" "l'absence de bandeau de consentement." msgstr "" -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:341 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "" -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:342 msgid "e-mail" msgstr "" -#: scripts/_lib/map_template.py:344 +#: scripts/_lib/map_template.py:343 msgid "E-mail copié dans le presse-papiers" msgstr "" -#: scripts/_lib/map_template.py:346 +#: scripts/_lib/map_template.py:345 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " "depuis la fiche de l'appellation, ou par {email}." msgstr "" -#: scripts/_lib/map_template.py:352 +#: scripts/_lib/map_template.py:351 msgid "Signaler une erreur" msgstr "" -#: scripts/_lib/map_template.py:353 +#: scripts/_lib/map_template.py:352 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "" -#: scripts/_lib/map_template.py:354 +#: scripts/_lib/map_template.py:353 msgid "Aire / contour" msgstr "" -#: scripts/_lib/map_template.py:357 +#: scripts/_lib/map_template.py:356 msgid "Nom / type" msgstr "" -#: scripts/_lib/map_template.py:359 +#: scripts/_lib/map_template.py:358 msgid "Autre" msgstr "" -#: scripts/_lib/map_template.py:360 +#: scripts/_lib/map_template.py:359 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "" -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:361 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:363 msgid "Envoyer" msgstr "" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:366 msgid "Géométrie" msgstr "" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:367 msgid "Envoyé, merci !" msgstr "" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:369 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." msgstr "" -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:373 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -906,15 +902,15 @@ msgid "" "couverture au-delà de l'UE, de la Suisse et du Royaume-Uni." msgstr "" -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:378 msgid "Toutes les appellations" msgstr "" -#: scripts/_lib/map_template.py:380 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "" -#: scripts/_lib/map_template.py:382 +#: scripts/_lib/map_template.py:381 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -922,192 +918,192 @@ msgid "" "(AOC, DOCG, DOQ…), AOC suisses et IG britanniques." msgstr "" -#: scripts/_lib/map_template.py:387 +#: scripts/_lib/map_template.py:386 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " "{map_link}." msgstr "" -#: scripts/_lib/map_template.py:390 +#: scripts/_lib/map_template.py:389 msgid "carte interactive" msgstr "" -#: scripts/_lib/map_template.py:391 +#: scripts/_lib/map_template.py:390 msgid "Liste des appellations par pays" msgstr "" -#: scripts/_lib/map_template.py:392 +#: scripts/_lib/map_template.py:391 msgid "Appellation parente" msgstr "" -#: scripts/_lib/map_template.py:393 +#: scripts/_lib/map_template.py:392 msgid "Dénominations rattachées" msgstr "" -#: scripts/_lib/map_template.py:395 +#: scripts/_lib/map_template.py:394 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "" -#: scripts/_lib/map_template.py:397 +#: scripts/_lib/map_template.py:396 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "" -#: scripts/_lib/map_template.py:412 +#: scripts/_lib/map_template.py:411 msgid "BOURGOGNE" msgstr "" -#: scripts/_lib/map_template.py:413 +#: scripts/_lib/map_template.py:412 msgid "BEAUJOLAIS" msgstr "" -#: scripts/_lib/map_template.py:414 +#: scripts/_lib/map_template.py:413 msgid "JURA" msgstr "" -#: scripts/_lib/map_template.py:415 +#: scripts/_lib/map_template.py:414 msgid "SAVOIE" msgstr "" -#: scripts/_lib/map_template.py:416 +#: scripts/_lib/map_template.py:415 msgid "BUGEY" msgstr "" -#: scripts/_lib/map_template.py:417 +#: scripts/_lib/map_template.py:416 msgid "ALSACE ET EST" msgstr "" -#: scripts/_lib/map_template.py:418 +#: scripts/_lib/map_template.py:417 msgid "VAL DE LOIRE" msgstr "" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:418 msgid "SUD-OUEST" msgstr "" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:419 msgid "VALLEE DU RHÔNE" msgstr "" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:420 msgid "LANGUEDOC-ROUSSILLON" msgstr "" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:421 msgid "TOULOUSE-PYRENEES" msgstr "" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:422 msgid "PROVENCE-CORSE" msgstr "" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:423 msgid "CHAMPAGNE" msgstr "" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:424 msgid "EAUX-DE-VIE DE CIDRE" msgstr "" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:425 msgid "VIN DOUX NATURELS" msgstr "" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:426 msgid "COGNAC" msgstr "" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:427 msgid "ARMAGNAC" msgstr "" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:428 msgid "RHUM" msgstr "" -#: scripts/_lib/map_template.py:469 +#: scripts/_lib/map_template.py:468 msgid "France" msgstr "" -#: scripts/_lib/map_template.py:470 +#: scripts/_lib/map_template.py:469 msgid "Espagne" msgstr "" -#: scripts/_lib/map_template.py:471 +#: scripts/_lib/map_template.py:470 msgid "Portugal" msgstr "" -#: scripts/_lib/map_template.py:472 +#: scripts/_lib/map_template.py:471 msgid "Italie" msgstr "" -#: scripts/_lib/map_template.py:473 +#: scripts/_lib/map_template.py:472 msgid "Autriche" msgstr "" -#: scripts/_lib/map_template.py:474 +#: scripts/_lib/map_template.py:473 msgid "Slovénie" msgstr "" -#: scripts/_lib/map_template.py:475 +#: scripts/_lib/map_template.py:474 msgid "Croatie" msgstr "" -#: scripts/_lib/map_template.py:476 +#: scripts/_lib/map_template.py:475 msgid "Hongrie" msgstr "" -#: scripts/_lib/map_template.py:477 +#: scripts/_lib/map_template.py:476 msgid "Roumanie" msgstr "" -#: scripts/_lib/map_template.py:478 +#: scripts/_lib/map_template.py:477 msgid "Bulgarie" msgstr "" -#: scripts/_lib/map_template.py:479 +#: scripts/_lib/map_template.py:478 msgid "Grèce" msgstr "" -#: scripts/_lib/map_template.py:480 +#: scripts/_lib/map_template.py:479 msgid "Allemagne" msgstr "" -#: scripts/_lib/map_template.py:481 +#: scripts/_lib/map_template.py:480 msgid "Slovaquie" msgstr "" -#: scripts/_lib/map_template.py:482 +#: scripts/_lib/map_template.py:481 msgid "Suisse" msgstr "" -#: scripts/_lib/map_template.py:483 +#: scripts/_lib/map_template.py:482 msgid "Tchéquie" msgstr "" -#: scripts/_lib/map_template.py:484 +#: scripts/_lib/map_template.py:483 msgid "Luxembourg" msgstr "" -#: scripts/_lib/map_template.py:485 +#: scripts/_lib/map_template.py:484 msgid "Belgique" msgstr "" -#: scripts/_lib/map_template.py:486 +#: scripts/_lib/map_template.py:485 msgid "Pays-Bas" msgstr "" -#: scripts/_lib/map_template.py:487 +#: scripts/_lib/map_template.py:486 msgid "Malte" msgstr "" -#: scripts/_lib/map_template.py:488 +#: scripts/_lib/map_template.py:487 msgid "Chypre" msgstr "" -#: scripts/_lib/map_template.py:489 +#: scripts/_lib/map_template.py:488 msgid "Royaume-Uni" msgstr "" diff --git a/locale/nl/LC_MESSAGES/messages.po b/locale/nl/LC_MESSAGES/messages.po index 7614c1b..4eed4af 100644 --- a/locale/nl/LC_MESSAGES/messages.po +++ b/locale/nl/LC_MESSAGES/messages.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: open-wine-map\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-09-23 08:12+0200\n" +"POT-Creation-Date: 2026-09-23 09:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: nl\n" @@ -59,286 +59,282 @@ msgstr "naam van appellatie…" msgid "Recherche d'appellation…" msgstr "Appellatie zoeken…" -#: scripts/_lib/map_template.py:56 -msgid "Recherche de cépage…" -msgstr "Druif zoeken…" - -#: scripts/_lib/map_template.py:58 +#: scripts/_lib/map_template.py:57 msgid "Rechercher une appellation, un cépage, une région…" msgstr "Zoek een appellatie, druif of streek…" -#: scripts/_lib/map_template.py:59 +#: scripts/_lib/map_template.py:58 msgid "Cépage principal uniquement" msgstr "Alleen hoofddruif" -#: scripts/_lib/map_template.py:60 +#: scripts/_lib/map_template.py:59 #, python-brace-format msgid "Aucun résultat pour « {q} »" msgstr "Geen resultaten voor ‘{q}’" -#: scripts/_lib/map_template.py:61 +#: scripts/_lib/map_template.py:60 msgid "Options" msgstr "Opties" -#: scripts/_lib/map_template.py:62 +#: scripts/_lib/map_template.py:61 msgid "Filtres actifs" msgstr "Actieve filters" -#: scripts/_lib/map_template.py:63 +#: scripts/_lib/map_template.py:62 msgid "Tout sélectionner" msgstr "Alles selecteren" -#: scripts/_lib/map_template.py:65 +#: scripts/_lib/map_template.py:64 #, python-brace-format msgid "{p} appellations · {s} dénominations géographiques complémentaires" msgstr "{p} appellaties · {s} aanvullende geografische benamingen" -#: scripts/_lib/map_template.py:67 +#: scripts/_lib/map_template.py:66 #, python-brace-format msgid "{p} appellations" msgstr "{p} appellaties" -#: scripts/_lib/map_template.py:68 +#: scripts/_lib/map_template.py:67 #, python-brace-format msgid "Ouvrir la fiche de {name}" msgstr "Open de fiche van {name}" -#: scripts/_lib/map_template.py:69 +#: scripts/_lib/map_template.py:68 msgid "Ouvrir la fiche" msgstr "Open fiche" -#: scripts/_lib/map_template.py:70 +#: scripts/_lib/map_template.py:69 msgid "Inclure les spiritueux" msgstr "Gedistilleerd tonen" -#: scripts/_lib/map_template.py:71 +#: scripts/_lib/map_template.py:70 msgid "Style de vin" msgstr "Wijnstijl" -#: scripts/_lib/map_template.py:72 +#: scripts/_lib/map_template.py:71 msgid "Classement" msgstr "Classificatie" -#: scripts/_lib/map_template.py:73 +#: scripts/_lib/map_template.py:72 msgid "Cépages principaux" msgstr "Hoofddruivenrassen" -#: scripts/_lib/map_template.py:74 +#: scripts/_lib/map_template.py:73 msgid "Cépages accessoires" msgstr "Aanvullende druivenrassen" -#: scripts/_lib/map_template.py:75 scripts/_lib/map_template.py:231 -#: scripts/_lib/map_template.py:355 +#: scripts/_lib/map_template.py:74 scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:354 msgid "Cépages" msgstr "Druivenrassen" -#: scripts/_lib/map_template.py:76 +#: scripts/_lib/map_template.py:75 msgid "Région" msgstr "Regio" -#: scripts/_lib/map_template.py:77 +#: scripts/_lib/map_template.py:76 msgid "Appellation" msgstr "Appellatie" -#: scripts/_lib/map_template.py:78 +#: scripts/_lib/map_template.py:77 msgid "Type" msgstr "Type" -#: scripts/_lib/map_template.py:79 +#: scripts/_lib/map_template.py:78 msgid "Origine protégée (AOP, AOC, DOC, DO…)" msgstr "Beschermde oorsprong (BOB, AOC, DOC, DO…)" -#: scripts/_lib/map_template.py:80 +#: scripts/_lib/map_template.py:79 msgid "Indication géographique (IGP, IGT, Landwein…)" msgstr "Geografische aanduiding (BGA, IGT, Landwein…)" -#: scripts/_lib/map_template.py:81 +#: scripts/_lib/map_template.py:80 msgid "AOP" msgstr "BOB" -#: scripts/_lib/map_template.py:82 +#: scripts/_lib/map_template.py:81 msgid "IGP" msgstr "BGA" -#: scripts/_lib/map_template.py:83 +#: scripts/_lib/map_template.py:82 msgid "IG spiritueux" msgstr "GA gedistilleerde drank" -#: scripts/_lib/map_template.py:84 +#: scripts/_lib/map_template.py:83 msgid "Type d'appellation" msgstr "Type appellatie" -#: scripts/_lib/map_template.py:85 +#: scripts/_lib/map_template.py:84 msgid "AOP (régime britannique)" msgstr "BOB (Britse regeling)" -#: scripts/_lib/map_template.py:86 +#: scripts/_lib/map_template.py:85 msgid "IGP (régime britannique)" msgstr "BGA (Britse regeling)" -#: scripts/_lib/map_template.py:87 +#: scripts/_lib/map_template.py:86 msgid "AOC (Suisse)" msgstr "AOC (Zwitserland)" -#: scripts/_lib/map_template.py:88 +#: scripts/_lib/map_template.py:87 msgid "Source" msgstr "Bron" -#: scripts/_lib/map_template.py:89 +#: scripts/_lib/map_template.py:88 msgid "Vue" msgstr "Weergave" -#: scripts/_lib/map_template.py:90 +#: scripts/_lib/map_template.py:89 msgid "Simple" msgstr "Eenvoudig" -#: scripts/_lib/map_template.py:91 +#: scripts/_lib/map_template.py:90 msgid "Avancée" msgstr "Geavanceerd" -#: scripts/_lib/map_template.py:92 +#: scripts/_lib/map_template.py:91 msgid "Thème" msgstr "Thema" -#: scripts/_lib/map_template.py:93 +#: scripts/_lib/map_template.py:92 msgid "Clair" msgstr "Licht" -#: scripts/_lib/map_template.py:94 +#: scripts/_lib/map_template.py:93 msgid "Sombre" msgstr "Donker" -#: scripts/_lib/map_template.py:95 +#: scripts/_lib/map_template.py:94 msgid "Système" msgstr "Systeem" -#: scripts/_lib/map_template.py:96 +#: scripts/_lib/map_template.py:95 msgid "Afficher les IGP" msgstr "BGA's tonen" -#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:96 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:361 msgid "rouge" msgstr "rood" -#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:97 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:362 msgid "blanc" msgstr "wit" -#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:98 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:363 msgid "rosé" msgstr "rosé" -#: scripts/_lib/map_template.py:100 scripts/_lib/style_taxonomy.py:332 +#: scripts/_lib/map_template.py:99 scripts/_lib/style_taxonomy.py:332 #: scripts/_lib/style_taxonomy.py:364 msgid "mousseux" msgstr "mousserend" -#: scripts/_lib/map_template.py:101 +#: scripts/_lib/map_template.py:100 msgid "moelleux / liquoreux" msgstr "zoet / dessertwijn" -#: scripts/_lib/map_template.py:102 scripts/_lib/style_taxonomy.py:345 +#: scripts/_lib/map_template.py:101 scripts/_lib/style_taxonomy.py:345 msgid "oxydatif" msgstr "oxidatief" -#: scripts/_lib/map_template.py:103 +#: scripts/_lib/map_template.py:102 msgid "autre" msgstr "andere" -#: scripts/_lib/map_template.py:104 +#: scripts/_lib/map_template.py:103 msgid "Réinitialiser" msgstr "Resetten" -#: scripts/_lib/map_template.py:105 +#: scripts/_lib/map_template.py:104 #, python-brace-format msgid "{n} appellations" msgstr "{n} appellaties" -#: scripts/_lib/map_template.py:106 +#: scripts/_lib/map_template.py:105 #, python-brace-format msgid "{n} / {total} appellations" msgstr "{n} / {total} appellaties" -#: scripts/_lib/map_template.py:107 +#: scripts/_lib/map_template.py:106 #, python-brace-format msgid "{n} masquées dans les IGP · afficher" msgstr "{n} verborgen in BGA's · tonen" -#: scripts/_lib/map_template.py:108 +#: scripts/_lib/map_template.py:107 msgid "Fermer" msgstr "Sluiten" -#: scripts/_lib/map_template.py:109 +#: scripts/_lib/map_template.py:108 msgid "Détails de l'appellation" msgstr "Details van de appellatie" -#: scripts/_lib/map_template.py:110 +#: scripts/_lib/map_template.py:109 #, python-brace-format msgid "Retirer le filtre {label}" msgstr "Filter {label} verwijderen" -#: scripts/_lib/map_template.py:111 +#: scripts/_lib/map_template.py:110 msgid "Filtres et options de la carte" msgstr "Filters en kaartopties" -#: scripts/_lib/map_template.py:112 +#: scripts/_lib/map_template.py:111 msgid "Langue" msgstr "Taal" -#: scripts/_lib/map_template.py:113 +#: scripts/_lib/map_template.py:112 msgid "Carte des appellations viticoles" msgstr "Kaart van wijnappellaties" -#: scripts/_lib/map_template.py:114 +#: scripts/_lib/map_template.py:113 msgid "Aller à la carte" msgstr "Naar de kaart" -#: scripts/_lib/map_template.py:115 +#: scripts/_lib/map_template.py:114 msgid "Styles" msgstr "Stijlen" -#: scripts/_lib/map_template.py:116 +#: scripts/_lib/map_template.py:115 msgid "Variétés d'intérêt" msgstr "Variëteiten van belang" -#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:358 +#: scripts/_lib/map_template.py:116 scripts/_lib/map_template.py:357 msgid "Sources" msgstr "Bronnen" -#: scripts/_lib/map_template.py:118 scripts/_lib/map_template.py:356 +#: scripts/_lib/map_template.py:117 scripts/_lib/map_template.py:355 msgid "Terroir" msgstr "Terroir" -#: scripts/_lib/map_template.py:119 +#: scripts/_lib/map_template.py:118 #, python-brace-format msgid "Dűlők (lieux-dits) : {n}" msgstr "Dűlők (wijngaarden): {n}" -#: scripts/_lib/map_template.py:120 +#: scripts/_lib/map_template.py:119 #, python-brace-format msgid "Menzioni geografiche aggiuntive (crus) : {n}" msgstr "Aanvullende geografische aanduidingen (crus): {n}" -#: scripts/_lib/map_template.py:121 +#: scripts/_lib/map_template.py:120 msgid "Facteurs naturels" msgstr "Natuurlijke factoren" -#: scripts/_lib/map_template.py:122 +#: scripts/_lib/map_template.py:121 msgid "Facteurs humains" msgstr "Menselijke factoren" -#: scripts/_lib/map_template.py:123 +#: scripts/_lib/map_template.py:122 msgid "Caractéristiques du produit" msgstr "Producteigenschappen" -#: scripts/_lib/map_template.py:124 +#: scripts/_lib/map_template.py:123 msgid "Lien terroir / vin" msgstr "Verband terroir / wijn" -#: scripts/_lib/map_template.py:126 +#: scripts/_lib/map_template.py:125 #, python-brace-format msgid "" "Faits dégagés du Lien au terroir par interprétation automatique — voir la" @@ -347,26 +343,26 @@ msgstr "" "Feiten afgeleid uit de terroir-bandsectie (Lien au terroir) door " "automatische interpretatie — zie de {source}." -#: scripts/_lib/map_template.py:128 +#: scripts/_lib/map_template.py:127 msgid "source" msgstr "bron" -#: scripts/_lib/map_template.py:129 +#: scripts/_lib/map_template.py:128 msgid "via Wikipedia · CC BY-SA 4.0" msgstr "via Wikipedia · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:131 +#: scripts/_lib/map_template.py:130 #, python-brace-format msgid "Citation textuelle du Lien au terroir — voir la {source}." msgstr "" "Letterlijk citaat uit de terroir-bandsectie (Lien au terroir) — zie de " "{source}." -#: scripts/_lib/map_template.py:133 +#: scripts/_lib/map_template.py:132 msgid "à vérifier — texte source court" msgstr "te controleren — korte brontekst" -#: scripts/_lib/map_template.py:135 +#: scripts/_lib/map_template.py:134 #, python-brace-format msgid "" "Ces repères décrivent l'appellation englobante {parent} — pas " @@ -375,29 +371,29 @@ msgstr "" "Deze kenmerken beschrijven de overkoepelende appellatie {parent} — niet " "specifiek deze denominatie." -#: scripts/_lib/map_template.py:138 +#: scripts/_lib/map_template.py:137 msgid "sans région" msgstr "geen regio" -#: scripts/_lib/map_template.py:139 +#: scripts/_lib/map_template.py:138 #, python-brace-format msgid "{n} commune(s) INAO" msgstr "{n} INAO-gemeente(n)" -#: scripts/_lib/map_template.py:140 +#: scripts/_lib/map_template.py:139 #, python-brace-format msgid "{n} commune(s)" msgstr "{n} gemeente(n)" -#: scripts/_lib/map_template.py:141 +#: scripts/_lib/map_template.py:140 msgid "aire approchée" msgstr "benaderd gebied" -#: scripts/_lib/map_template.py:142 +#: scripts/_lib/map_template.py:141 msgid "aire approchée (à l'échelle communale)" msgstr "benaderd gebied (gemeenteniveau)" -#: scripts/_lib/map_template.py:144 +#: scripts/_lib/map_template.py:143 #, python-brace-format msgid "" "Aire approchée — pas de données parcellaires précises pour cette " @@ -406,7 +402,7 @@ msgstr "" "Benaderd gebied — geen perceelnauwkeurige data beschikbaar voor deze " "benaming; polygoon overgenomen van {umbrella}." -#: scripts/_lib/map_template.py:148 +#: scripts/_lib/map_template.py:147 msgid "" "Aire approchée — pas de données parcellaires précises pour cette " "dénomination ; polygone hérité de l'appellation parente." @@ -414,7 +410,7 @@ msgstr "" "Benaderd gebied — geen perceelnauwkeurige data beschikbaar voor deze " "benaming; polygoon overgenomen van de bovenliggende appellatie." -#: scripts/_lib/map_template.py:152 +#: scripts/_lib/map_template.py:151 msgid "" "Aire approchée — pas de données parcellaires disponibles ; affichée comme" " l'emprise de la commune où se situe la dénomination." @@ -422,7 +418,7 @@ msgstr "" "Benaderd gebied — geen perceelgegevens beschikbaar; weergegeven als de " "omtrek van de gemeente waarin de benaming ligt." -#: scripts/_lib/map_template.py:156 +#: scripts/_lib/map_template.py:155 #, python-brace-format msgid "" "Aire issue du lieu-dit cadastral « {lieu_dit} » (commune de {commune}, " @@ -431,11 +427,11 @@ msgstr "" "Gebied afgeleid van het kadastrale gehucht „{lieu_dit}” (gemeente " "{commune}, {source})." -#: scripts/_lib/map_template.py:159 +#: scripts/_lib/map_template.py:158 msgid "cadastre.data.gouv.fr" msgstr "cadastre.data.gouv.fr" -#: scripts/_lib/map_template.py:161 +#: scripts/_lib/map_template.py:160 msgid "" "Aire approchée — reconstituée à partir des références parcellaires du " "plan de l'aire délimitée annexé au cahier des charges ; ce n'est pas une " @@ -445,7 +441,7 @@ msgstr "" "het plan van het afgebakende gebied bij het productdossier; dit is geen " "officiële grens." -#: scripts/_lib/map_template.py:170 +#: scripts/_lib/map_template.py:169 #, python-brace-format msgid "" "Aire approchée — réunion des {n} commune(s) de l'aire délimitée (INAO), " @@ -456,7 +452,7 @@ msgstr "" "afgebakende gebied (INAO), inclusief waterpartijen en niet-wijnbouwgrond;" " voor deze benaming bestaat geen perceelsgewijze afbakening." -#: scripts/_lib/map_template.py:175 +#: scripts/_lib/map_template.py:174 #, python-brace-format msgid "" "Vue d'ensemble : silhouette du vignoble, généralisée à {radius} m à " @@ -467,7 +463,7 @@ msgstr "" "basis van de perceelsafbakening van het INAO — niet de afbakening zelf; " "de percelen verschijnen bij inzoomen." -#: scripts/_lib/map_template.py:180 +#: scripts/_lib/map_template.py:179 #, python-brace-format msgid "" "Sur la carte, aux zooms d'ensemble, cette appellation est dessinée comme " @@ -480,14 +476,14 @@ msgstr "" "perceelsafbakening van het INAO; de percelen zelf verschijnen bij " "inzoomen." -#: scripts/_lib/map_template.py:186 +#: scripts/_lib/map_template.py:185 #, python-brace-format msgid "Aire : zone de production officielle publiée par le géoportail ({region})." msgstr "" "Gebied: de officiële productiezone gepubliceerd door het regionale " "geoportaal ({region})." -#: scripts/_lib/map_template.py:190 +#: scripts/_lib/map_template.py:189 msgid "" "Aire : zone de production officielle du MAPA (Zonas de Calidad " "Diferenciada : Vinos)." @@ -495,7 +491,7 @@ msgstr "" "Gebied: de officiële productiezone van het MAPA (Zonas de Calidad " "Diferenciada: Vinos)." -#: scripts/_lib/map_template.py:194 +#: scripts/_lib/map_template.py:193 msgid "" "Aire approchée — polygone du jeu de données Bétard 2022 (AOP de l'UE), à " "la résolution des communes ; peut inclure des terres non viticoles." @@ -503,7 +499,7 @@ msgstr "" "Benaderd gebied — polygoon uit de dataset Bétard 2022 (EU-BOB's), op " "gemeenteniveau; kan niet-wijnbouwgrond omvatten." -#: scripts/_lib/map_template.py:199 +#: scripts/_lib/map_template.py:198 msgid "" "Aire approchée — réunion des polygones des appellations membres (Bétard " "2022), à la résolution des communes." @@ -511,7 +507,7 @@ msgstr "" "Benaderd gebied — de vereniging van de polygonen van de deelnemende " "appellations (Bétard 2022), op gemeenteniveau." -#: scripts/_lib/map_template.py:203 +#: scripts/_lib/map_template.py:202 msgid "" "Aire approchée — réunion des unités administratives nommées dans le " "document (limites Eurostat GISCO ou cadastre national), terres non " @@ -521,116 +517,116 @@ msgstr "" "bestuurlijke eenheden (grenzen van Eurostat GISCO of het nationale " "kadaster), inclusief niet-wijnbouwgrond." -#: scripts/_lib/map_template.py:207 +#: scripts/_lib/map_template.py:206 #, python-brace-format msgid "{n} appellations à ce point" msgstr "{n} appellaties op dit punt" -#: scripts/_lib/map_template.py:208 +#: scripts/_lib/map_template.py:207 msgid "Cliquer à nouveau pour parcourir les autres" msgstr "Klik opnieuw om door de andere te bladeren" -#: scripts/_lib/map_template.py:209 +#: scripts/_lib/map_template.py:208 msgid "Cahier des charges (BO Agri, PDF)" msgstr "Productdossier (BO Agri, PDF)" -#: scripts/_lib/map_template.py:210 +#: scripts/_lib/map_template.py:209 msgid "Cahier des charges (registre GI de l'UE, PDF)" msgstr "Productdossier (EU GI-register, PDF)" -#: scripts/_lib/map_template.py:211 +#: scripts/_lib/map_template.py:210 msgid "homologué" msgstr "goedgekeurd" -#: scripts/_lib/map_template.py:212 +#: scripts/_lib/map_template.py:211 msgid "JORF" msgstr "JORF" -#: scripts/_lib/map_template.py:213 +#: scripts/_lib/map_template.py:212 msgid "Texte officiel INAO (show_texte)" msgstr "Officiële INAO-tekst (show_texte)" -#: scripts/_lib/map_template.py:214 +#: scripts/_lib/map_template.py:213 msgid "Fiche produit INAO" msgstr "INAO-productfiche" -#: scripts/_lib/map_template.py:215 +#: scripts/_lib/map_template.py:214 msgid "Site officiel de l'interprofession" msgstr "Officiële site van de wijnorganisatie" -#: scripts/_lib/map_template.py:216 +#: scripts/_lib/map_template.py:215 msgid "Cahier des charges (EUR-Lex, document unique)" msgstr "Productspecificatie (EUR-Lex, enig document)" -#: scripts/_lib/map_template.py:217 +#: scripts/_lib/map_template.py:216 msgid "Pliego de condiciones (national, PDF)" msgstr "Nationaal pliego de condiciones (PDF)" -#: scripts/_lib/map_template.py:218 +#: scripts/_lib/map_template.py:217 msgid "variétés ajoutées" msgstr "rassen toegevoegd" -#: scripts/_lib/map_template.py:219 +#: scripts/_lib/map_template.py:218 msgid "Cahier des charges national (PDF)" msgstr "Nationaal productdossier (PDF)" -#: scripts/_lib/map_template.py:220 +#: scripts/_lib/map_template.py:219 msgid "Spécification du produit (IGP, PDF)" msgstr "Productdossier (BGA, PDF)" -#: scripts/_lib/map_template.py:221 +#: scripts/_lib/map_template.py:220 msgid "Registre régional des cépages (PDF)" msgstr "Regionaal druivenrasregister (PDF)" -#: scripts/_lib/map_template.py:222 +#: scripts/_lib/map_template.py:221 msgid "Registre eAmbrosia (UE)" msgstr "eAmbrosia-register (EU)" -#: scripts/_lib/map_template.py:223 +#: scripts/_lib/map_template.py:222 msgid "Numéro de dossier" msgstr "Dossiernummer" -#: scripts/_lib/map_template.py:224 +#: scripts/_lib/map_template.py:223 msgid "Règlement cantonal sur la vigne et le vin" msgstr "Kantonaal wijnreglement" -#: scripts/_lib/map_template.py:225 +#: scripts/_lib/map_template.py:224 msgid "Répertoire suisse des AOC (OFAG/BLW)" msgstr "Zwitsers AOC-register (OFAG/BLW)" -#: scripts/_lib/map_template.py:226 +#: scripts/_lib/map_template.py:225 msgid "Cahier des charges (GOV.UK, PDF)" msgstr "Productdossier (GOV.UK, PDF)" -#: scripts/_lib/map_template.py:227 +#: scripts/_lib/map_template.py:226 msgid "Registre des IG du Royaume-Uni" msgstr "Register van geografische aanduidingen (VK)" -#: scripts/_lib/map_template.py:228 +#: scripts/_lib/map_template.py:227 msgid "Légende couleurs" msgstr "Legende" -#: scripts/_lib/map_template.py:229 +#: scripts/_lib/map_template.py:228 msgid "Bassin viticole" msgstr "Wijnstreek" -#: scripts/_lib/map_template.py:230 +#: scripts/_lib/map_template.py:229 msgid "Plus l'aire est petite, plus la teinte est dense." msgstr "Hoe kleiner het gebied, hoe dichter de tint." -#: scripts/_lib/map_template.py:232 +#: scripts/_lib/map_template.py:231 msgid "principal — variété de la cuvée" msgstr "hoofdras — variëteit van de cuvée" -#: scripts/_lib/map_template.py:233 +#: scripts/_lib/map_template.py:232 msgid "accessoire — assemblage limité" msgstr "bijras — beperkte assemblage" -#: scripts/_lib/map_template.py:234 +#: scripts/_lib/map_template.py:233 msgid "intérêt — observation/conservation" msgstr "interesse — observatie/behoud" -#: scripts/_lib/map_template.py:236 +#: scripts/_lib/map_template.py:235 msgid "" "Le régulateur portugais (IVV) n'établit pas de distinction " "principal/accessoire — toutes les castas autorisées sont listées ensemble" @@ -640,7 +636,7 @@ msgstr "" "en bijrassen — alle toegelaten druivenrassen worden samen vermeld in de " "caderno de especificações." -#: scripts/_lib/map_template.py:240 +#: scripts/_lib/map_template.py:239 msgid "" "Bianchello (ou Biancame) est traité ici comme un cépage distinct, " "conformément au disciplinare de la DOP Bianchello del Metauro ; le " @@ -651,20 +647,20 @@ msgstr "" "Metauro; de VIVC-catalogus vermeldt het als synoniem van Trebbiano " "Toscano." -#: scripts/_lib/map_template.py:245 +#: scripts/_lib/map_template.py:244 #, python-brace-format msgid "Open Wine Map n'a pas encore trouvé de {doc} pour cette appellation." msgstr "Open Wine Map heeft nog geen {doc} gevonden voor deze benaming." -#: scripts/_lib/map_template.py:247 +#: scripts/_lib/map_template.py:246 msgid "aidez-nous à le trouver" msgstr "help ons het te vinden" -#: scripts/_lib/map_template.py:251 +#: scripts/_lib/map_template.py:250 msgid "Annulée" msgstr "Geannuleerd" -#: scripts/_lib/map_template.py:253 +#: scripts/_lib/map_template.py:252 #, python-brace-format msgid "" "Cette appellation a été annulée le {date} par {regulation}. Des " @@ -675,141 +671,141 @@ msgstr "" "flessen, etiketten en cursusmateriaal kunnen de naam nog vermelden; ze " "blijft hier ter referentie staan." -#: scripts/_lib/map_template.py:257 +#: scripts/_lib/map_template.py:256 #, python-brace-format msgid "Acte national : {act}." msgstr "Nationaal besluit: {act}." -#: scripts/_lib/map_template.py:258 +#: scripts/_lib/map_template.py:257 #, python-brace-format msgid "Elle est remplacée par {successor}." msgstr "Ze is opgevolgd door {successor}." -#: scripts/_lib/map_template.py:259 +#: scripts/_lib/map_template.py:258 #, python-brace-format msgid "Enregistrement annulé le {date}" msgstr "Registratie geannuleerd op {date}" -#: scripts/_lib/map_template.py:265 +#: scripts/_lib/map_template.py:264 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}, qui autorise {grapes}." msgstr "Afgebakend door {regulator} in zijn {doc}, dat {grapes} toestaat." -#: scripts/_lib/map_template.py:267 +#: scripts/_lib/map_template.py:266 #, python-brace-format msgid "Délimitée par {regulator} dans son {doc}{extra}." msgstr "Afgebakend door {regulator} in zijn {doc}{extra}." -#: scripts/_lib/map_template.py:268 +#: scripts/_lib/map_template.py:267 #, python-brace-format msgid "{names} et {n} autres cépages" msgstr "{names} en {n} andere druivenrassen" -#: scripts/_lib/map_template.py:269 +#: scripts/_lib/map_template.py:268 msgid "le registre eAmbrosia de l'UE" msgstr "het eAmbrosia-register van de EU" -#: scripts/_lib/map_template.py:270 +#: scripts/_lib/map_template.py:269 #, python-brace-format msgid "le canton de {canton}" msgstr "het kanton {canton}" -#: scripts/_lib/map_template.py:271 +#: scripts/_lib/map_template.py:270 msgid "(français)" msgstr "(bron: Frans)" -#: scripts/_lib/map_template.py:272 +#: scripts/_lib/map_template.py:271 msgid "Texte source en français" msgstr "Brontekst in het Frans" -#: scripts/_lib/map_template.py:273 +#: scripts/_lib/map_template.py:272 msgid "(español)" msgstr "(bron: Spaans)" -#: scripts/_lib/map_template.py:274 +#: scripts/_lib/map_template.py:273 msgid "Texte source en espagnol" msgstr "Brontekst in het Spaans" -#: scripts/_lib/map_template.py:275 +#: scripts/_lib/map_template.py:274 msgid "(português)" msgstr "(bron: Portugees)" -#: scripts/_lib/map_template.py:276 +#: scripts/_lib/map_template.py:275 msgid "Texte source en portugais" msgstr "Brontekst in het Portugees" -#: scripts/_lib/map_template.py:277 +#: scripts/_lib/map_template.py:276 msgid "Filtres" msgstr "Filters" -#: scripts/_lib/map_template.py:278 +#: scripts/_lib/map_template.py:277 #, python-brace-format msgid "Traduit de {wiki} · CC BY-SA 4.0" msgstr "Vertaald uit {wiki} · CC BY-SA 4.0" -#: scripts/_lib/map_template.py:279 +#: scripts/_lib/map_template.py:278 msgid "Wikipédia en anglais" msgstr "Engelstalige Wikipedia" -#: scripts/_lib/map_template.py:280 +#: scripts/_lib/map_template.py:279 msgid "Wikipédia en français" msgstr "Franstalige Wikipedia" -#: scripts/_lib/map_template.py:281 +#: scripts/_lib/map_template.py:280 msgid "Wikipédia en espagnol" msgstr "Spaanstalige Wikipedia" -#: scripts/_lib/map_template.py:282 +#: scripts/_lib/map_template.py:281 msgid "Wikipédia en néerlandais" msgstr "Nederlandstalige Wikipedia" -#: scripts/_lib/map_template.py:283 +#: scripts/_lib/map_template.py:282 msgid "Wikipédia en portugais" msgstr "Portugeestalige Wikipedia" -#: scripts/_lib/map_template.py:284 +#: scripts/_lib/map_template.py:283 msgid "Wikipédia en croate" msgstr "Kroatische Wikipedia" -#: scripts/_lib/map_template.py:285 +#: scripts/_lib/map_template.py:284 msgid "Vitis International Variety Catalogue (Julius Kühn-Institut)" msgstr "Vitis International Variety Catalogue (Julius Kühn-Institut)" -#: scripts/_lib/map_template.py:286 +#: scripts/_lib/map_template.py:285 #, python-brace-format msgid "VIVC #{id}" msgstr "VIVC #{id}" -#: scripts/_lib/map_template.py:287 +#: scripts/_lib/map_template.py:286 #, python-brace-format msgid "Traduction automatique depuis {source}" msgstr "Automatisch vertaald uit {source}" -#: scripts/_lib/map_template.py:288 +#: scripts/_lib/map_template.py:287 msgid "le cahier des charges" msgstr "het cahier des charges" -#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 +#: scripts/_lib/map_template.py:288 scripts/_lib/map_template.py:290 msgid "le pliego de condiciones" msgstr "de productspecificatie" -#: scripts/_lib/map_template.py:290 scripts/_lib/map_template.py:292 +#: scripts/_lib/map_template.py:289 scripts/_lib/map_template.py:291 msgid "le caderno de especificações" msgstr "de caderno de especificações" -#: scripts/_lib/map_template.py:293 +#: scripts/_lib/map_template.py:292 msgid "Dénomination géographique complémentaire de" msgstr "Deelgebied van" -#: scripts/_lib/map_template.py:294 +#: scripts/_lib/map_template.py:293 msgid "À propos" msgstr "Over" -#: scripts/_lib/map_template.py:295 +#: scripts/_lib/map_template.py:294 msgid "À propos d'Open Wine Map" msgstr "Over Open Wine Map" -#: scripts/_lib/map_template.py:297 +#: scripts/_lib/map_template.py:296 msgid "" "Carte de référence des appellations viticoles, générée automatiquement à " "partir des registres publics : le registre de l'Union européenne des AOP " @@ -821,7 +817,7 @@ msgstr "" " het Zwitserse federale repertorium van kantonnale AOC's en het Britse " "register van geografische aanduidingen." -#: scripts/_lib/map_template.py:304 +#: scripts/_lib/map_template.py:303 msgid "" "Une partie du texte est produite par des modèles de langage : les repères" " de terroir sont dégagés du texte du régulateur par Claude, vérifiés " @@ -842,12 +838,12 @@ msgstr "" "menselijke vertaler vertaald, op een klein machinaal vertaald restant na." " Elk onderdeel heeft zijn eigen bronvermelding in het paneel." -#: scripts/_lib/map_template.py:313 +#: scripts/_lib/map_template.py:312 #, python-brace-format msgid "Réalisé avec ♡ par {devloed}." msgstr "Met ♡ gemaakt door {devloed}." -#: scripts/_lib/map_template.py:315 +#: scripts/_lib/map_template.py:314 #, python-brace-format msgid "" "Sources : INAO ({inao}) pour les cahiers des charges et les aires " @@ -871,7 +867,7 @@ msgstr "" "druivennamen en rasnummers (citaat: Röckel et al.). Elk Wikipedia-" "fragment wordt ter plaatse vermeld. Details en licenties in de {readme}." -#: scripts/_lib/map_template.py:326 +#: scripts/_lib/map_template.py:325 #, python-brace-format msgid "" "Niveau de détail : en dessous du zoom {zoom}, les appellations françaises" @@ -887,12 +883,12 @@ msgstr "" "percelen); vanaf zoomniveau {zoom} worden de percelen zelf getekend. Alle" " andere appellations behouden dezelfde omtrek op elk zoomniveau." -#: scripts/_lib/map_template.py:333 +#: scripts/_lib/map_template.py:332 #, python-brace-format msgid "Suggestions et pull requests bienvenues sur {github}." msgstr "Issues en pull requests welkom op {github}." -#: scripts/_lib/map_template.py:335 +#: scripts/_lib/map_template.py:334 #, python-brace-format msgid "" "Vie privée : la fréquentation est mesurée avec une instance {plausible} " @@ -909,20 +905,20 @@ msgstr "" "adressen, geen tracking over sites of apparaten heen — daarom is er geen " "cookiebanner." -#: scripts/_lib/map_template.py:342 +#: scripts/_lib/map_template.py:341 #, python-brace-format msgid "Suivez les mises à jour sur {bluesky}." msgstr "Volg de updates op {bluesky}." -#: scripts/_lib/map_template.py:343 +#: scripts/_lib/map_template.py:342 msgid "e-mail" msgstr "e-mail" -#: scripts/_lib/map_template.py:344 +#: scripts/_lib/map_template.py:343 msgid "E-mail copié dans le presse-papiers" msgstr "E-mail gekopieerd naar klembord" -#: scripts/_lib/map_template.py:346 +#: scripts/_lib/map_template.py:345 #, python-brace-format msgid "" "Carte générée automatiquement — des erreurs sont possibles. Signalez-les " @@ -931,55 +927,55 @@ msgstr "" "Automatisch gegenereerde kaart — fouten zijn mogelijk. Meld ze op de " "fiche van de appellatie, of per {email}." -#: scripts/_lib/map_template.py:352 +#: scripts/_lib/map_template.py:351 msgid "Signaler une erreur" msgstr "Een fout melden" -#: scripts/_lib/map_template.py:353 +#: scripts/_lib/map_template.py:352 msgid "Un clic nous dit quoi vérifier — sans compte." msgstr "Eén tik vertelt ons wat we moeten nakijken — geen account nodig." -#: scripts/_lib/map_template.py:354 +#: scripts/_lib/map_template.py:353 msgid "Aire / contour" msgstr "Gebied / grens" -#: scripts/_lib/map_template.py:357 +#: scripts/_lib/map_template.py:356 msgid "Nom / type" msgstr "Naam / type" -#: scripts/_lib/map_template.py:359 +#: scripts/_lib/map_template.py:358 msgid "Autre" msgstr "Anders" -#: scripts/_lib/map_template.py:360 +#: scripts/_lib/map_template.py:359 #, python-brace-format msgid "Merci, c'est noté : {aspect}." msgstr "Bedankt, genoteerd: {aspect}." -#: scripts/_lib/map_template.py:362 +#: scripts/_lib/map_template.py:361 msgid "Précisez si vous voulez (facultatif, sans données personnelles)" msgstr "Licht toe als je wilt (optioneel, geen persoonsgegevens)" -#: scripts/_lib/map_template.py:364 +#: scripts/_lib/map_template.py:363 msgid "Envoyer" msgstr "Verzenden" -#: scripts/_lib/map_template.py:367 +#: scripts/_lib/map_template.py:366 msgid "Géométrie" msgstr "Geometrie" -#: scripts/_lib/map_template.py:368 +#: scripts/_lib/map_template.py:367 msgid "Envoyé, merci !" msgstr "Verzonden, bedankt!" -#: scripts/_lib/map_template.py:370 +#: scripts/_lib/map_template.py:369 #, python-brace-format msgid "" "{aspect} — votre navigateur bloque notre outil de mesure, envoyez plutôt " "un {email}." msgstr "{aspect} — je browser blokkeert onze meettool; stuur liever een {email}." -#: scripts/_lib/map_template.py:374 +#: scripts/_lib/map_template.py:373 #, python-brace-format msgid "" "{c} pays européens cartographiés ({n} appellations : {parents} " @@ -992,15 +988,15 @@ msgstr "" "verfijnen de datakwaliteit en breiden de dekking uit tot buiten de EU, " "Zwitserland en het Verenigd Koninkrijk." -#: scripts/_lib/map_template.py:379 +#: scripts/_lib/map_template.py:378 msgid "Toutes les appellations" msgstr "Alle appellaties" -#: scripts/_lib/map_template.py:380 +#: scripts/_lib/map_template.py:379 msgid "Toutes les appellations viticoles — Open Wine Map" msgstr "Alle wijnappellaties — Open Wine Map" -#: scripts/_lib/map_template.py:382 +#: scripts/_lib/map_template.py:381 #, python-brace-format msgid "" "Liste des {n} appellations viticoles cartographiées sur Open Wine Map, " @@ -1011,7 +1007,7 @@ msgstr "" "BGA's van de EU met hun traditionele aanduidingen (AOC, DOCG, DOQ…), " "Zwitserse AOC's en Britse GA's." -#: scripts/_lib/map_template.py:387 +#: scripts/_lib/map_template.py:386 #, python-brace-format msgid "" "Les {n} appellations ci-dessous sont classées par pays. Retour à la " @@ -1020,185 +1016,185 @@ msgstr "" "De {n} onderstaande appellaties zijn gegroepeerd per land. Terug naar de " "{map_link}." -#: scripts/_lib/map_template.py:390 +#: scripts/_lib/map_template.py:389 msgid "carte interactive" msgstr "interactieve kaart" -#: scripts/_lib/map_template.py:391 +#: scripts/_lib/map_template.py:390 msgid "Liste des appellations par pays" msgstr "Lijst van appellaties per land" -#: scripts/_lib/map_template.py:392 +#: scripts/_lib/map_template.py:391 msgid "Appellation parente" msgstr "Bovenliggende appellatie" -#: scripts/_lib/map_template.py:393 +#: scripts/_lib/map_template.py:392 msgid "Dénominations rattachées" msgstr "Gekoppelde benamingen" -#: scripts/_lib/map_template.py:395 +#: scripts/_lib/map_template.py:394 #, python-brace-format msgid "Parcourir la liste complète des appellations : {browse_link}." msgstr "Blader door de volledige lijst van appellaties: {browse_link}." -#: scripts/_lib/map_template.py:397 +#: scripts/_lib/map_template.py:396 #, python-brace-format msgid "Données mises à jour le {date}." msgstr "Gegevens bijgewerkt op {date}." -#: scripts/_lib/map_template.py:412 +#: scripts/_lib/map_template.py:411 msgid "BOURGOGNE" msgstr "Bourgogne" -#: scripts/_lib/map_template.py:413 +#: scripts/_lib/map_template.py:412 msgid "BEAUJOLAIS" msgstr "Beaujolais" -#: scripts/_lib/map_template.py:414 +#: scripts/_lib/map_template.py:413 msgid "JURA" msgstr "Jura" -#: scripts/_lib/map_template.py:415 +#: scripts/_lib/map_template.py:414 msgid "SAVOIE" msgstr "Savoie" -#: scripts/_lib/map_template.py:416 +#: scripts/_lib/map_template.py:415 msgid "BUGEY" msgstr "Bugey" -#: scripts/_lib/map_template.py:417 +#: scripts/_lib/map_template.py:416 msgid "ALSACE ET EST" msgstr "Elzas en Oost" -#: scripts/_lib/map_template.py:418 +#: scripts/_lib/map_template.py:417 msgid "VAL DE LOIRE" msgstr "Loirestreek" -#: scripts/_lib/map_template.py:419 +#: scripts/_lib/map_template.py:418 msgid "SUD-OUEST" msgstr "Zuidwest-Frankrijk" -#: scripts/_lib/map_template.py:420 +#: scripts/_lib/map_template.py:419 msgid "VALLEE DU RHÔNE" msgstr "Rhônestreek" -#: scripts/_lib/map_template.py:421 +#: scripts/_lib/map_template.py:420 msgid "LANGUEDOC-ROUSSILLON" msgstr "Languedoc-Roussillon" -#: scripts/_lib/map_template.py:422 +#: scripts/_lib/map_template.py:421 msgid "TOULOUSE-PYRENEES" msgstr "Toulouse-Pyreneeën" -#: scripts/_lib/map_template.py:423 +#: scripts/_lib/map_template.py:422 msgid "PROVENCE-CORSE" msgstr "Provence-Corsica" -#: scripts/_lib/map_template.py:424 +#: scripts/_lib/map_template.py:423 msgid "CHAMPAGNE" msgstr "Champagne" -#: scripts/_lib/map_template.py:425 +#: scripts/_lib/map_template.py:424 msgid "EAUX-DE-VIE DE CIDRE" msgstr "Cider-eaux-de-vie" -#: scripts/_lib/map_template.py:426 +#: scripts/_lib/map_template.py:425 msgid "VIN DOUX NATURELS" msgstr "Natuurlijk zoete wijnen" -#: scripts/_lib/map_template.py:427 +#: scripts/_lib/map_template.py:426 msgid "COGNAC" msgstr "Cognac" -#: scripts/_lib/map_template.py:428 +#: scripts/_lib/map_template.py:427 msgid "ARMAGNAC" msgstr "Armagnac" -#: scripts/_lib/map_template.py:429 +#: scripts/_lib/map_template.py:428 msgid "RHUM" msgstr "Rum" -#: scripts/_lib/map_template.py:469 +#: scripts/_lib/map_template.py:468 msgid "France" msgstr "Frankrijk" -#: scripts/_lib/map_template.py:470 +#: scripts/_lib/map_template.py:469 msgid "Espagne" msgstr "Spanje" -#: scripts/_lib/map_template.py:471 +#: scripts/_lib/map_template.py:470 msgid "Portugal" msgstr "Portugal" -#: scripts/_lib/map_template.py:472 +#: scripts/_lib/map_template.py:471 msgid "Italie" msgstr "Italië" -#: scripts/_lib/map_template.py:473 +#: scripts/_lib/map_template.py:472 msgid "Autriche" msgstr "Oostenrijk" -#: scripts/_lib/map_template.py:474 +#: scripts/_lib/map_template.py:473 msgid "Slovénie" msgstr "Slovenië" -#: scripts/_lib/map_template.py:475 +#: scripts/_lib/map_template.py:474 msgid "Croatie" msgstr "Kroatië" -#: scripts/_lib/map_template.py:476 +#: scripts/_lib/map_template.py:475 msgid "Hongrie" msgstr "Hongarije" -#: scripts/_lib/map_template.py:477 +#: scripts/_lib/map_template.py:476 msgid "Roumanie" msgstr "Roemenië" -#: scripts/_lib/map_template.py:478 +#: scripts/_lib/map_template.py:477 msgid "Bulgarie" msgstr "Bulgarije" -#: scripts/_lib/map_template.py:479 +#: scripts/_lib/map_template.py:478 msgid "Grèce" msgstr "Griekenland" -#: scripts/_lib/map_template.py:480 +#: scripts/_lib/map_template.py:479 msgid "Allemagne" msgstr "Duitsland" -#: scripts/_lib/map_template.py:481 +#: scripts/_lib/map_template.py:480 msgid "Slovaquie" msgstr "Slowakije" -#: scripts/_lib/map_template.py:482 +#: scripts/_lib/map_template.py:481 msgid "Suisse" msgstr "Zwitserland" -#: scripts/_lib/map_template.py:483 +#: scripts/_lib/map_template.py:482 msgid "Tchéquie" msgstr "Tsjechië" -#: scripts/_lib/map_template.py:484 +#: scripts/_lib/map_template.py:483 msgid "Luxembourg" msgstr "Luxemburg" -#: scripts/_lib/map_template.py:485 +#: scripts/_lib/map_template.py:484 msgid "Belgique" msgstr "België" -#: scripts/_lib/map_template.py:486 +#: scripts/_lib/map_template.py:485 msgid "Pays-Bas" msgstr "Nederland" -#: scripts/_lib/map_template.py:487 +#: scripts/_lib/map_template.py:486 msgid "Malte" msgstr "Malta" -#: scripts/_lib/map_template.py:488 +#: scripts/_lib/map_template.py:487 msgid "Chypre" msgstr "Cyprus" -#: scripts/_lib/map_template.py:489 +#: scripts/_lib/map_template.py:488 msgid "Royaume-Uni" msgstr "Verenigd Koninkrijk" @@ -1630,3 +1626,6 @@ msgstr "palo cortado" #~ msgid "Une erreur sur cette fiche ?" #~ msgstr "Klopt er iets niet op deze fiche?" +#~ msgid "Recherche de cépage…" +#~ msgstr "Druif zoeken…" + diff --git a/scripts/_lib/assets/app.js b/scripts/_lib/assets/app.js index 0878420..bfaf8d8 100644 --- a/scripts/_lib/assets/app.js +++ b/scripts/_lib/assets/app.js @@ -164,166 +164,6 @@ return out.slice(0, limit).map(o => ({ entry: o.entry, matched: o.matched, score: o.score })); } - function _findGrapeEntry(slug) { - for (const e of _GRAPE_INDEX_NORM) if (e.entry.slug === slug) return e.entry; - return null; - } - - function _grapeChipHtml(entry) { - const canon = entry.canonical && !canonicalEqualsCahier(entry.canonical, entry.label) - ? ` (${escapeHtml(entry.canonical)})` : ''; - return ( - `` + - `${escapeHtml(toTitleCase(entry.label))}${canon}` + - `` + - `` - ); - } - - function _grapeSuggestionHtml(entry, matched, role, active) { - // When the query matched on an alias (e.g. "ull de llebre" → Tempranillo - // via the GRAPE_ALIAS reverse-key "ull-de-llebre"), promote the - // matched alias to the primary slot so the suggestion reads in the - // user's terminology — "Ull de Llebre (Tempranillo)" — instead of - // burying the match in the canonical row label. - const primary = matched || entry.label; - const secondary = matched && matched.toLowerCase() !== entry.label.toLowerCase() - ? entry.label - : (entry.canonical && !canonicalEqualsCahier(entry.canonical, entry.label) ? entry.canonical : ''); - const secondaryHtml = secondary - ? ` ${escapeHtml(toTitleCase(secondary))}` : ''; - const countKey = role === 'principal' ? 'count_principal' - : role === 'accessory' ? 'count_accessory' : 'count'; - const cls = ['suggestion']; - if (active) cls.push('active'); - return ( - `
` + - `${escapeHtml(toTitleCase(primary))}${secondaryHtml}` + - `${entry[countKey]}` + - `
` - ); - } - - function buildGrapeChipFilter(container, role, filterSet) { - container.innerHTML = - `
` + - `
` + - `` + - `` + - `
`; - const tray = container.querySelector('.chip-tray'); - const input = container.querySelector('.grape-search'); - const drop = container.querySelector('.grape-suggestions'); - let activeIdx = 0; - let currentSuggestions = []; - - function renderChips() { - const chips = []; - for (const slug of filterSet) { - const e = _findGrapeEntry(slug); - if (e) chips.push(_grapeChipHtml(e)); - } - tray.innerHTML = chips.join(''); - } - - function renderSuggestions(q) { - currentSuggestions = rankGrapeSuggestions(q, role, 12) - .filter(s => !filterSet.has(s.entry.slug)); - activeIdx = 0; - if (!currentSuggestions.length) { - drop.innerHTML = ''; - drop.hidden = true; - input.setAttribute('aria-expanded', 'false'); - return; - } - drop.innerHTML = currentSuggestions - .map((s, i) => _grapeSuggestionHtml(s.entry, s.matched, role, i === activeIdx)).join(''); - drop.hidden = false; - input.setAttribute('aria-expanded', 'true'); - } - - function highlight(i) { - const items = drop.querySelectorAll('.suggestion'); - if (!items.length) return; - items.forEach((el, k) => el.classList.toggle('active', k === i)); - activeIdx = i; - const cur = items[i]; - if (cur) cur.scrollIntoView({ block: 'nearest' }); - } - - function pick(slug) { - filterSet.add(slug); - input.value = ''; - renderChips(); - renderSuggestions(''); - applyFilter(); - input.focus(); - } - - function remove(slug) { - filterSet.delete(slug); - renderChips(); - renderSuggestions(input.value); - applyFilter(); - } - - tray.addEventListener('click', (e) => { - const btn = e.target.closest('.chip-x'); - if (!btn) return; - const chip = btn.closest('.chip'); - if (chip) remove(chip.dataset.slug); - }); - - drop.addEventListener('mousedown', (e) => { - const s = e.target.closest('.suggestion'); - if (!s) return; - e.preventDefault(); // keep focus on input - pick(s.dataset.slug); - }); - - drop.addEventListener('mousemove', (e) => { - const s = e.target.closest('.suggestion'); - if (!s) return; - const items = Array.from(drop.querySelectorAll('.suggestion')); - highlight(items.indexOf(s)); - }); - - input.addEventListener('input', () => renderSuggestions(input.value)); - input.addEventListener('focus', () => renderSuggestions(input.value)); - input.addEventListener('blur', () => { - // Delay so the mousedown handler runs before we hide. - setTimeout(() => { drop.hidden = true; input.setAttribute('aria-expanded', 'false'); }, 120); - }); - input.addEventListener('keydown', (e) => { - if (e.key === 'ArrowDown') { - e.preventDefault(); - if (drop.hidden) renderSuggestions(input.value); - else highlight((activeIdx + 1) % currentSuggestions.length); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - if (!drop.hidden) highlight((activeIdx - 1 + currentSuggestions.length) % currentSuggestions.length); - } else if (e.key === 'Enter') { - if (!drop.hidden && currentSuggestions[activeIdx]) { - e.preventDefault(); - pick(currentSuggestions[activeIdx].entry.slug); - } - } else if (e.key === 'Escape') { - drop.hidden = true; - input.setAttribute('aria-expanded', 'false'); - } else if (e.key === 'Backspace' && !input.value && filterSet.size) { - // Remove the most-recently-added chip. - const last = [...filterSet].pop(); - remove(last); - } - }); - - renderChips(); - container._refresh = () => { renderChips(); if (!drop.hidden) renderSuggestions(input.value); }; - } - - function refreshAllGrapeChipFilters() { - document.querySelectorAll('.grape-chip-filter').forEach(c => c._refresh && c._refresh()); - } const STYLES_INFO = __OWM_styles_info_json__; const REGION_LABELS = __OWM_region_labels_json__; const COUNTRY_LABELS = __OWM_country_labels_json__; @@ -658,9 +498,11 @@ appellations: new Set(), // When true, the grape filter matches grapes_principal instead of grapes_all // (the "main grape only" toggle inside the Grapes facet). + // "Main grape only" narrows a grape filter to the principal roster. It is + // a refinement of the grape chips, shown as a toggle next to them, and + // ends with them — not a persisted preference. mainGrapeOnly: false, }; - try { filters.mainGrapeOnly = localStorage.getItem('main_grape_only') === '1'; } catch (e) {} function debounce(fn, ms) { let t = null; @@ -699,7 +541,10 @@ else if (kind === 'style') filters.styles.delete(key); else if (kind === 'classification') filters.classifications.delete(key); else if (kind === 'appellationType') filters.appellationType.delete(key); - else if (kind === 'grapeAll') filters.grapesAll.delete(key); + else if (kind === 'grapeAll') { + filters.grapesAll.delete(key); + if (!filters.grapesAll.size) filters.mainGrapeOnly = false; + } else if (kind === 'principal') filters.principal.delete(key); else if (kind === 'accessory') filters.accessory.delete(key); else if (kind === 'appellation') filters.appellations.delete(key); @@ -707,11 +552,6 @@ // both Hungarian and Slovak), so the country rides along on the chip. else if (kind === 'region') setSlugSelection(visibleSlugsInRegion(chip.dataset.country || '', key), false); else if (kind === 'country') setSlugSelection(visibleSlugsInCountry(key), false); - else if (kind === 'mainGrapeOnly') { - filters.mainGrapeOnly = false; - const m = document.getElementById('main-grape-only'); if (m) m.checked = false; - try { localStorage.setItem('main_grape_only', '0'); } catch (e) {} - } // Sync the underlying checkboxes for the cleared filter. document.querySelectorAll('#sidebar .facet input[type=checkbox]').forEach(inp => { const k = inp.dataset.key; @@ -724,10 +564,20 @@ refreshTreeTriStates(); applyFilter(); }); + // The "main grape only" toggle is rendered inside the chip tray next to the + // grape chips (renderActiveFilters), so it is re-created on every render; + // one delegated listener serves every instance. + document.getElementById('active-filters-chips').addEventListener('change', e => { + const inp = e.target; + if (!inp || inp.id !== 'main-grape-only') return; + filters.mainGrapeOnly = !!inp.checked; + track('Grape Scope Toggled', { scope: filters.mainGrapeOnly ? 'main' : 'all', locale: LANG }); + applyFilter({ fit: true }); + }); function refreshSidebarCheckedState() { - // Re-sync facet checkboxes (styles only — grapes are chip filters - // and re-render their chip tray via `refreshAllGrapeChipFilters`). + // Re-sync facet checkboxes (styles only — the grape filter lives in the + // chip tray and is rendered from `filters.grapesAll`). const sets = { 'facet-styles': filters.styles, 'facet-styles-simple': filters.stylesSimple, @@ -741,7 +591,6 @@ inp.checked = set.has(inp.dataset.key); }); } - refreshAllGrapeChipFilters(); } // Which polygon of a record is on screen depends on the zoom: the footprint @@ -936,13 +785,6 @@ buildTreeFacet('facet-classification', FACET_CLASS_TREE, filters.classifications, CLASS_LABELS, CLASS_DESCENDANTS, 'classification'); buildTreeFacet('facet-appellation-type', FACET_TERM_TREE, filters.appellationType, TERM_LABELS, TERM_DESCENDANTS, 'appellation-type'); buildFacet('facet-styles-simple', FACET_STYLES_SIMPLE, filters.stylesSimple, k => SIMPLE_STYLE_LABELS[k] || k); - document.querySelectorAll('.grape-chip-filter').forEach(container => { - const role = container.dataset.role || 'all'; - const set = role === 'principal' ? filters.principal - : role === 'accessory' ? filters.accessory - : filters.grapesAll; - buildGrapeChipFilter(container, role, set); - }); // Country → region → list of slugs, computed once. The tree re-renders on // spirits-toggle (entries appear/disappear), but the grouping itself is @@ -1364,8 +1206,6 @@ filters.principal.clear(); filters.accessory.clear(); filters.grapesAll.clear(); filters.appellations.clear(); filters.mainGrapeOnly = false; - try { localStorage.setItem('main_grape_only', '0'); } catch (e) {} - const _mgo = document.getElementById('main-grape-only'); if (_mgo) _mgo.checked = false; const _omni = document.getElementById('omni'); if (_omni) { _omni.value = ''; @@ -1471,7 +1311,9 @@ for (const k of filters.classifications) chips.push({ kind: 'classification', key: k, label: CLASS_LABELS[k] || k }); for (const k of filters.appellationType) chips.push({ kind: 'appellationType', key: k, label: TERM_LABELS[k] || k }); for (const k of filters.grapesAll) chips.push({ kind: 'grapeAll', key: k, label: grapeName(k) }); - if (filters.mainGrapeOnly) chips.push({ kind: 'mainGrapeOnly', key: '1', label: LABELS.main_grape_only_label }); + // The scope toggle rides with the grape chips: a grape picked in the + // search box gets a "main grape only" checkbox right after it. + if (filters.grapesAll.size) chips.push({ kind: 'mainGrapeToggle', key: '1', label: LABELS.main_grape_only_label }); // Collapse a fully-selected subtree into one chip — a whole country first, // else each of its whole regions. The flag prefix disambiguates region // names shared across a border, which the tree does by nesting. @@ -1505,6 +1347,9 @@ if (rec) chips.push({ kind: 'appellation', key: slug, label: rec.name }); } el.innerHTML = chips.map(c => { + if (c.kind === 'mainGrapeToggle') { + return ``; + } const bulk = c.kind === 'region' || c.kind === 'country'; const cls = bulk ? 'filter-chip region-chip' : 'filter-chip'; const removeAria = fmt(LABELS.remove_filter_aria, { label: c.label }); @@ -1754,7 +1599,6 @@ if (type === 'grape') { filters.grapesAll.add(key); track('Omnisearch Result Picked', { type: 'grape', locale: LANG }); - refreshAllGrapeChipFilters(); applyFilter({ fit: true }); } else if (type === 'region') { setRegionNameSelection(key, true); @@ -1969,26 +1813,6 @@ } buildOmnisearch(); - // Rescope the Grapes facet typeahead/counts to the active grape field so - // "main grape only" surfaces principal-relevant grapes with principal counts - // (picks still land in filters.grapesAll). - function rescopeGrapeFacet() { - const gc = document.querySelector('.grape-chip-filter[data-role="all"]'); - if (gc) buildGrapeChipFilter(gc, filters.mainGrapeOnly ? 'principal' : 'all', filters.grapesAll); - } - const mgoEl = document.getElementById('main-grape-only'); - if (mgoEl) { - mgoEl.checked = filters.mainGrapeOnly; - if (filters.mainGrapeOnly) rescopeGrapeFacet(); // reflect a restored toggle on first paint - mgoEl.addEventListener('change', e => { - filters.mainGrapeOnly = e.target.checked; - try { localStorage.setItem('main_grape_only', filters.mainGrapeOnly ? '1' : '0'); } catch (err) {} - track('Grape Scope Toggled', { scope: filters.mainGrapeOnly ? 'main' : 'all', locale: LANG }); - rescopeGrapeFacet(); - applyFilter({ fit: true }); - }); - } - // ----- detail panel ----- const panel = document.getElementById('panel'); const panelBody = document.getElementById('panel-body'); diff --git a/scripts/_lib/map_template.py b/scripts/_lib/map_template.py index ea9d71b..f0b6a53 100644 --- a/scripts/_lib/map_template.py +++ b/scripts/_lib/map_template.py @@ -53,7 +53,6 @@ def build_labels(_: Callable[[str], str]) -> dict[str, str]: "search_h": _("Recherche"), "search_placeholder": _("nom d'appellation…"), "search_appellation_placeholder": _("Recherche d'appellation…"), - "search_grape_placeholder": _("Recherche de cépage…"), # Search-UX experiment (lab variants only — see _render_sidebar). "omnisearch_placeholder": _("Rechercher une appellation, un cépage, une région…"), "main_grape_only_label": _("Cépage principal uniquement"), @@ -2251,12 +2250,9 @@ def _emit(slug: str, meta: dict, ssr: str, has_card: bool) -> None: #sidebar .brand-title .brand-mark {{ width:18px; height:18px; flex:0 0 18px; display:inline-block }} #sidebar .subtitle {{ font-size:11px; color:#888; padding:0 16px 10px; border-bottom:1px solid #333 }} #sidebar h2 {{ font-size:11px; text-transform:uppercase; letter-spacing:0.08em; color:#888; padding:14px 16px 4px; margin:0 }} - /* `:not(.grape-search)` excludes the chip-filter's typeahead input — - it owns its own layout via `.grape-search-wrap`, which already - applies the 16/16 horizontal margin. Without the exclusion the - catch-all rule stacks margin on top of the wrap's, leaving the - grape input 32px narrower than the appellation search. */ - #sidebar input[type=text]:not(.grape-search):not(.omni-input) {{ width:calc(100% - 32px); margin:0 16px 8px; padding:7px 9px; box-sizing:border-box; background:#222; color:#eee; border:1px solid #444; border-radius:3px; font-size:13px }} + /* The omnisearch input owns its own layout (`.omni-input`); the catch-all + rule below is for any other text input in the sidebar. */ + #sidebar input[type=text]:not(.omni-input) {{ width:calc(100% - 32px); margin:0 16px 8px; padding:7px 9px; box-sizing:border-box; background:#222; color:#eee; border:1px solid #444; border-radius:3px; font-size:13px }} #sidebar input[type=text]:focus {{ outline:none; border-color:#934050 }} #sidebar input[type=text]:focus-visible {{ outline:2px solid #fff8e8; outline-offset:1px; border-color:#934050 }} /* Search-UX lab variants (a/b/c): unified omnisearch box + grouped dropdown @@ -2275,11 +2271,7 @@ def _emit(slug: str, meta: dict, ssr: str, has_card: bool) -> None: .omni-suggestions .suggestion .count {{ margin-left:auto; color:#9a9a9a; font-size:11px; font-variant-numeric:tabular-nums; flex:0 0 auto }} /* No-match feedback row (variant fix: the dropdown used to silently hide). */ .omni-empty {{ padding:10px 12px; color:#9a9a9a; font-size:12.5px; font-style:italic }} - .omni-subopt {{ padding:2px 16px 8px; border-bottom:1px solid #333 }} - .omni-subopt label {{ display:flex; align-items:center; gap:6px; font-size:12px; color:#bbb; cursor:pointer }} /* "Main grape only" relocated inside the Grapes facet (variant c). */ - .grape-subopt {{ padding:6px 16px 4px; border-bottom:none }} - .omni-subopt input {{ accent-color:#934050; flex:0 0 auto }} #lang-switcher {{ display:flex; gap:2px; padding:6px 12px 8px; border-bottom:1px solid #333 }} #lang-switcher a {{ color:#888; font-size:11px; text-transform:uppercase; letter-spacing:0.08em; text-decoration:none; padding:3px 8px; border-radius:3px }} #lang-switcher a:hover {{ color:#fff }} @@ -2315,36 +2307,17 @@ def _emit(slug: str, meta: dict, ssr: str, has_card: bool) -> None: .filter-chip.region-chip {{ border-color:#934050; box-shadow:inset 0 0 0 1px #934050; font-weight:600 }} .filter-chip button {{ background:none; border:none; color:#888; cursor:pointer; padding:0 4px; font-size:14px; line-height:1; border-radius:50% }} .filter-chip button:hover {{ color:#fff; background:#444 }} + /* The "main grape only" scope, rendered as a chip-shaped checkbox right + after the grape chips it refines. */ + .filter-chip.toggle-chip {{ padding:2px 9px 2px 6px; cursor:pointer; border-color:#934050; gap:5px }} + .filter-chip.toggle-chip:hover {{ background:#332028 }} + .filter-chip.toggle-chip input {{ accent-color:#934050; margin:0; width:12px; height:12px }} + .filter-chip.toggle-chip:has(input:checked) {{ background:#3a2730; color:#fff }} #active-filters #reset {{ background:transparent; color:#888; border:none; padding:2px 6px; cursor:pointer; font-size:11px; text-decoration:underline; flex:0 0 auto }} #active-filters #reset:hover {{ color:#fff }} #active-filters-chips:empty + #reset {{ display:none }} .facet-search {{ width:calc(100% - 32px); margin:4px 16px 6px; padding:5px 8px; box-sizing:border-box; background:#1f1f1f; color:#eee; border:1px solid #3a3a3a; border-radius:3px; font-size:12px }} .facet-search:focus {{ outline:none; border-color:#934050 }} - /* Grape chip filter — replaces the long-list checkbox facet with a - typeahead + selected-chip UX. Three instances on the page (simple - all, advanced principal, advanced accessory) all share these rules. */ - /* Horizontal spacing matches `.facet-search`'s `margin:4px 16px 6px` - so the grape search input lines up with the appellation search - input below it. The chip-tray and search-wrap each carry their - own 16px L/R margin instead of the chip-filter container padding. */ - .grape-chip-filter {{ padding:0 }} - .grape-chip-filter .chip-tray {{ display:flex; flex-wrap:wrap; gap:4px; margin:4px 16px 6px }} - .grape-chip-filter .chip-tray:empty {{ display:none }} - .grape-chip-filter .chip {{ display:inline-flex; align-items:center; gap:4px; padding:2px 4px 2px 8px; background:#3a2730; color:#fff; border:1px solid #934050; border-radius:11px; font-size:11px; line-height:1.3 }} - .grape-chip-filter .chip .canon {{ color:#cfa; opacity:0.7; font-style:italic }} - .grape-chip-filter .chip-x {{ background:none; border:none; color:#cfa; cursor:pointer; padding:0 4px; font-size:14px; line-height:1; border-radius:50% }} - .grape-chip-filter .chip-x:hover {{ color:#fff; background:#5a3045 }} - .grape-search-wrap {{ position:relative; margin:4px 16px 6px }} - .grape-search {{ width:100%; padding:5px 8px; box-sizing:border-box; background:#1f1f1f; color:#eee; border:1px solid #3a3a3a; border-radius:3px; font-size:12px }} - .grape-search:focus {{ outline:none; border-color:#934050 }} - .grape-suggestions {{ position:absolute; left:0; right:0; top:calc(100% + 2px); z-index:50; max-height:280px; overflow-y:auto; background:#1a1a1a; border:1px solid #3a3a3a; border-radius:3px; box-shadow:0 4px 12px rgba(0,0,0,0.4) }} - .grape-suggestions[hidden] {{ display:none }} - .grape-suggestions .suggestion {{ display:flex; align-items:center; gap:6px; padding:5px 8px; cursor:pointer; font-size:12px; color:#ddd; border-bottom:1px solid #2a2a2a }} - .grape-suggestions .suggestion:last-child {{ border-bottom:none }} - .grape-suggestions .suggestion.active, .grape-suggestions .suggestion:hover {{ background:#2a1f25 }} - .grape-suggestions .suggestion .name {{ flex:0 0 auto }} - .grape-suggestions .suggestion .canon {{ flex:1 1 auto; color:#999; font-style:italic; font-size:11px }} - .grape-suggestions .suggestion .count {{ flex:0 0 auto; margin-left:auto; color:#888; font-size:11px; font-variant-numeric:tabular-nums }} #sidebar > details > summary .facet-badge {{ display:inline-block; margin-left:6px; padding:1px 6px; background:#934050; color:#fff; border-radius:8px; font-size:10px; font-weight:600 }} #sidebar > details > summary .facet-badge:empty {{ display:none }} #sidebar > details > summary {{ display:flex; align-items:center }} @@ -2848,14 +2821,6 @@ def _split_template(t: str) -> tuple[str, str]:
-
- {labels[facet_grapes_h]} -
- -
-
-
-
{labels[facet_appellations_h]}
diff --git a/scripts/audit_vineyard_envelopes.py b/scripts/audit_vineyard_envelopes.py index 7529f3d..32de745 100644 --- a/scripts/audit_vineyard_envelopes.py +++ b/scripts/audit_vineyard_envelopes.py @@ -205,7 +205,9 @@ def main() -> int: ratio = env.area / parcels.area if parcels.area > 0 else 1.0 # Bridging onto other appellations' parcels. bridges: list[tuple[str, float]] = [] + bridge_km2 = 0.0 if added_km2 > 0: + others = [] for j in tree.query(added, predicate="intersects").tolist(): other = idx_slugs[j] if other == slug: @@ -217,16 +219,22 @@ def main() -> int: clipped = _clip(og, added.bounds) if clipped.is_empty: continue - # An umbrella whose parcels contain most of this record's is - # shared ground (regional over village), not a bridge. + # An appellation whose parcels cover at least half of this + # record's shares its ground (a regional over a village, a + # VDN over a table-wine AOC on the same slopes) — not a bridge. shared = _inter_km2(_clip(og, parcels.bounds), parcels) - if parcels.area > 0 and shared / _km2(parcels) >= 0.9: + if parcels.area > 0 and shared / _km2(parcels) >= 0.5: continue b = _inter_km2(clipped, added) if b > 0.005: bridges.append((other, b)) + others.append(_valid(clipped)) + # The total is the UNION of the neighbours' parcels inside the + # added area — overlapping neighbours (a climat under its village + # AOC) must not be summed twice. + if others: + bridge_km2 = _inter_km2(_valid(shapely.union_all(others)), added) bridges.sort(key=lambda t: -t[1]) - bridge_km2 = sum(b for _, b in bridges) row = { "slug": slug, "parts": p.get("parts"), From 9e934c670a31d5df9e425870787a4a9149b332cc Mon Sep 17 00:00:00 2001 From: Boris De Vloed Date: Wed, 23 Sep 2026 20:42:27 +0200 Subject: [PATCH 4/5] =?UTF-8?q?docs:=20delimitation=20sweep=20=E2=80=94=20?= =?UTF-8?q?how=20precisely=20the=20non-French=20specifications=20delimit?= =?UTF-8?q?=20their=20area?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 849 records read (every non-French record with a cadastral / exclusion / partial-municipality marker in its delimitation text, or a real same-country overlap with another appellation), classified by one reader per batch and re-verified against the full text for every high-rated record. Overlapping specifications almost never name each other; where a border is resolved it is by each document's own sub-municipal line. Cadastral identifiers exist in 99 records (ES 50 → SIGPAC, IT 20 → cadastral fogli, SI 12 + SK 7 → cadastral municipalities, DE 8 → Flurstücke); sub-municipal administrative units carry Portugal (freguesias), Romania, Greece and Cyprus; Italy's default is a road-by-road narrative trace with altitude limits. Recommended order and the CURATOR_TODO pointer included. Co-Authored-By: Claude Fable 5.1 --- CURATOR_TODO.md | 11 + docs/delimitation-sweep-2026-09-23.md | 370 ++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 docs/delimitation-sweep-2026-09-23.md diff --git a/CURATOR_TODO.md b/CURATOR_TODO.md index d1d3cdc..3dbeabc 100644 --- a/CURATOR_TODO.md +++ b/CURATOR_TODO.md @@ -572,6 +572,17 @@ vlaamse-landwijn (BGA §7 broad rule); appenzell-ausserrhoden, appenzell-innerrh - ~~Disclosure (owed regardless of the clip)~~ superseded by the branch above (the drawn shape is now a function of zoom, not of a hidden `v_source`): ~~emit `geom_source_villages` (from `v_source`) as a startup field (register it in `STARTUP_AOCS_FIELDS`), render a mode-aware `approx-line` when `viewMode === 'simple'` and it differs from `geom_source` (the label `meta_geom_approx_communal` exists in every catalogue and is unused), and re-render the open panel from `applyMode()` — today a mode switch leaves the card stale. `geom_approx_aires` fires only for `aires-csv-dgc`; plain `aires-csv` records (pineau-des-charentes) get no line either.~~ - Done 2026-09-22: `Feedback Flagged` / `Retracted` / `Note` now carry `view_mode`, and the report prints it — the flag's `geom_source` is the *advanced* provenance and was misread as "the visitor was looking at the parcellaire". +## Cross-country — delimitation sweep (2026-09-23) — 🟡 report written, nothing implemented + +Full report: [docs/delimitation-sweep-2026-09-23.md](docs/delimitation-sweep-2026-09-23.md); per-record classification, overlap pairs and source texts in `tmp/delimitation-sweep-2026-09-23/`. 849 non-French records read (every record with a cadastral / exclusion / partial-municipality marker or a real same-country overlap), 135 high-rated ones re-verified against the text. + +- Overlapping specifications almost never name each other (104 of 2,338 partners); where a border is resolved it is by each document's own sub-municipal line. +- Cadastral identifiers in 99 records: ES 50 (polígonos / parcelas — the Vinos de Pago estates and DO-level inclusions / exclusions; SIGPAC resolves them, the Priorat/Montsant code is the seed), IT 20 (fogli / particelle — Agenzia delle Entrate cadastral map, licence to confirm), SI 12 + SK 7 (cadastral municipalities + contour lines, open layers), DE 8 (Flurstücke, ALKIS open only in BB / SN / ST). +- Sub-municipal administrative units: PT freguesias (43, CAOP already on disk), RO sate (34, no open layer), GR / CY κοινότητες (38, GISCO LAU already at that level), BG землища. +- Italy: 278 narrative traces (roads, rivers, contours) — digitising, not parsing; 58 IT + 29 GR altitude limits could be a DEM clip of the zone, labelled as a display refinement. +- ≈ 80 records defer to an annexed map only (GR 29, PT 15, IT 11, CY 10, ES 9, DE 4): nothing to parse. +- Suggested order: ES polígonos → PT freguesias → SI/SK cadastral municipalities → IT fogli → DE Flurstücke → altitude clips. No step maps parcels the documents do not reference. + ## Spain ### Pliego URLs — ✅ complete (2026-05-10) diff --git a/docs/delimitation-sweep-2026-09-23.md b/docs/delimitation-sweep-2026-09-23.md new file mode 100644 index 0000000..70a6fe0 --- /dev/null +++ b/docs/delimitation-sweep-2026-09-23.md @@ -0,0 +1,370 @@ +# Delimitation sweep — how precisely the non-French specifications delimit their area (2026-09-23) + +Scope: every non-French record whose delimitation text carries a cadastral, exclusion or partial-municipality marker, or that overlaps another appellation of its country beyond a nested (regional-over-local) relationship — 849 records read in 91 batches by one reader each; every record the reader rated *high* was re-read by a second agent against the full text (135 verified, 69 corrected). France is out of scope (the INAO parcellaire already gives parcel precision). The texts are the extracted `geo_area` section of the EU single document, the MASAF disciplinare article 3 for Italy, or the national specification. + +Basis legend: **municipality-list** whole administrative units only · **partial-municipality** only part of a municipality, with the boundary described · **cadastral-units** explicit cadastral identifiers · **named-sites** lieux-dits / parajes / località / Rieden / dűlők · **altitude-limit** · **natural-boundary** rivers, ridges, coasts · **map-annex** the document defers to an annexed map. + +## What the sweep found + +**Overlapping specifications almost never reference each other.** The readers checked 2,338 overlap partners named in the record headers; a document explicitly addresses the shared border for 104 of them (Italy 74 of 1,479, Portugal 13 of 213, Spain 6 of 256, Slovenia 6 of 29, Cyprus 2 of 10). Where an overlap *is* resolved, it is resolved by each document's own sub-municipal line, not by naming the neighbour: Castel del Monte and Moscato di Trani both cut Trani, Andria and Corato along the SP 231; the two Lambrusco DOCs split the comune of Modena with a word-for-word identical trace; Menfi takes only fogli 1–7, 10, 11, 13–16 and 23 of Sciacca; the two Krasochoria Lemesou PDOs split Omodos at the Cha-Potami river, each claiming one bank. The rest of the 5,160 same-country overlaps are either whole-municipality lists that genuinely share municipalities (the Italian DOC families, the Portuguese regional IGPs) or narrative traces that split a municipality without saying which appellation is on the other side. + +**Three kinds of sub-municipal precision exist in the texts, and they map to very different open data.** + +1. **Cadastral identifiers — 99 records.** Spain (50): the polígono / parcela lists of the Vinos de Pago estates (Abadía Retuerta, Dominio de Valdepusa, Finca Élez, Guijoso, Dehesa del Carrizal, Pago Florentino, Vallegarcia, Chozas Carrascal, Los Balagueses, El Terrerazo, Vera de Estenas, Calzadilla, Urueña, Rosalejo, Dehesa Peñalba, Bolandín, Aylés, Tharsys, Urbezo, La Jaraba, Los Cerrillos, El Vicario, Campo de la Guardia, Campo de Calatrava, Río Negro, Sierra Sur de Jaén …) and the polígono-level inclusions or exclusions inside DO municipios (La Mancha, Rueda, Arlanza, Campo de Borja, Valdejalón, Ribera del Gállego-Cinco Villas, Cataluña, Tarragona, Conca de Barberà, Penedès, Montsant, Priorat, Uclés, Méntrida, Arribes, the eight Alicante subzones). All of it resolves against SIGPAC, whose polígono and recinto geometry is open data and whose download is already wired for one comarca. Italy (20): cadastral fogli and particelle — Menfi / Sciacca, Colline Joniche Tarantine (42 references), Conegliano-Valdobbiadene (the Rive and Cartizze fogli), Cinque Terre, the Valpolicella family, Soave Superiore and Recioto di Soave, Valtellina Superiore, Alto Adige, Trentino, Teroldego Rotaliano, Eloro, Delia Nivolelli, Casteggio, Asolo Montello, Collio Goriziano — resolvable against the Agenzia delle Entrate cadastral map (WMS, and open downloads per foglio since 2021; licence to confirm). Slovenia (12) and Slovakia (7): boundaries run along named katastrske občine / katastrálne územia, often combined with contour lines; both countries publish the cadastral-municipality layer openly (e-prostor, ZBGIS). Germany (8): Gemarkung / Flur / Flurstück lists for the six Einzellage PDOs, Großräschener See, Sächsischer Landwein, Saale-Unstrut and parts of Württemberg — ALKIS is open in Brandenburg, Saxony and Saxony-Anhalt, not in Rhineland-Palatinate or Baden-Württemberg. + +2. **Sub-municipal administrative units — the largest class outside Italy.** Portuguese cadernos delimit by freguesia (43 records), Romanian caiete by sat / localitate (34), Greek and Cypriot specifications by κοινότητα (35 + 3), Bulgarian ones by the землища of settlements (the readers first called these cadastral; the verifiers corrected them to administrative), Italian disciplinari by frazione. Where an open boundary layer exists at that level the gain is mechanical: DGT CAOP carries every Portuguese freguesia and is already on disk; GISCO LAU is already at community level for Greece and Cyprus; Slovenia's and Slovakia's cadastral municipalities are open. Romanian sate and Italian frazioni have no official open polygon layer (ISTAT's località abitate census polygons are the nearest thing for Italy). + +3. **Narrative boundary lines and altitude limits — Italy's default.** 278 Italian disciplinari include municipalities only in part and trace the internal line along roads, rivers, ridges and contour lines; 264 of them are in principle re-drawable from OSM roads and rivers, 157 involve an altitude threshold, and 58 state one outright (Alto Adige above / below 650 m at Scena, Valpolicella, Custoza …). Greece adds 29 altitude limits. This is digitising work, not parsing: a road-by-road trace is a person with a map, though a pure altitude limit can be applied mechanically with a DEM as a display refinement of the zone. About 80 records (Greece 29, Portugal 15, Italy 11, Cyprus 10, Spain 9, Germany 4) defer to an annexed map as the authority and give nothing else to parse. + +**Reading quality.** Every record the readers rated *high* was re-read by a second agent against the full text; 69 of 135 came back corrected, mostly on two grounds — quoted references that differ from the source only by a PDF line wrap (harmless) and priority downgrades where the text splits a municipality without naming the neighbouring appellation (Castelli Romani, Dolcetto d'Asti, Frascati) or lists settlement territories rather than cadastral units (Asenovgrad, Yuzhno Chernomorie). Medium and low rows carry a single reader's judgement. The per-record classification, the overlap pairs and the source texts are in `tmp/delimitation-sweep-2026-09-23/`. + +## Recommended order + +| step | records | source | cost | +|---|---:|---|---| +| Spain: generalise the Priorat/Montsant SIGPAC resolver to every pliego that names polígonos or parcelas (inclusions and exclusions) | 50 | SIGPAC (open, mechanism exists) | a parser for the inclusion/exclusion grammar, one comarca download per DO | +| Portugal: parse the freguesia lists of the cadernos and union CAOP freguesias instead of concelhos | 43 | DGT CAOP (on disk) | parser only | +| Slovenia + Slovakia: cadastral-municipality boundaries plus contour clipping | 19 | e-prostor / ZBGIS k.o. layers (open) + EU-DEM | one fetch each, a boundary-walk parser | +| Italy: fogli catastali | 20 | Agenzia delle Entrate cadastral map | licence check, per-foglio download | +| Germany: Flurstücke for the Einzellagen and the two Landweine | 8 | ALKIS where open (BB, SN, ST) | partial coverage only | +| Italy + Greece: altitude limits as a DEM clip of the zone | 58 + 29 | EU-DEM (open) | mechanical, display-only, must be labelled | +| Italy: road-by-road narrative traces | 278 | OSM + a person | not a pipeline task | + +Nothing in this sweep maps parcels the documents do not reference: every step above resolves a reference the specification itself makes. + +## Per country + +| country | records read | cadastral | partial municipality | altitude | named sites | natural boundary | map annex | municipality only | high | medium | low | +|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:| +| Italy | 437 | 20 | 278 | 58 | 155 | 239 | 11 | 156 | 23 | 257 | 157 | +| Spain | 142 | 50 | 67 | 1 | 38 | 16 | 9 | 40 | 45 | 48 | 49 | +| Greece | 87 | 0 | 35 | 29 | 22 | 8 | 29 | 12 | 1 | 61 | 25 | +| Portugal | 62 | 1 | 43 | 2 | 12 | 11 | 15 | 7 | 3 | 41 | 18 | +| Romania | 36 | 0 | 34 | 0 | 18 | 0 | 0 | 2 | 0 | 34 | 2 | +| Bulgaria | 15 | 0 | 5 | 0 | 0 | 0 | 0 | 9 | 0 | 6 | 9 | +| Slovenia | 15 | 12 | 4 | 12 | 5 | 15 | 0 | 0 | 12 | 3 | 0 | +| Germany | 11 | 8 | 7 | 0 | 4 | 2 | 4 | 0 | 8 | 3 | 0 | +| Cyprus | 10 | 0 | 3 | 3 | 0 | 2 | 10 | 0 | 0 | 3 | 7 | +| Hungary | 9 | 1 | 9 | 0 | 0 | 1 | 1 | 0 | 1 | 7 | 1 | +| Slovakia | 9 | 7 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 7 | 1 | +| Netherlands | 5 | 0 | 3 | 0 | 3 | 1 | 0 | 2 | 0 | 3 | 2 | +| Austria | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 4 | +| Croatia | 4 | 0 | 2 | 0 | 2 | 2 | 0 | 0 | 0 | 2 | 2 | +| Belgium | 3 | 0 | 1 | 0 | 0 | 2 | 0 | 1 | 0 | 2 | 1 | + +## High-priority records (cadastral references, or a within-municipality split naming the neighbour) + +| country | appellation | basis | references (first three) | mappable with | overlap partner addressed | key quote | +|---|---|---|---|---|---|---| +| de | Großräschener See ⚠ | cadastral-units, partial-municipality | Gemarkung Großräschen, Flur 5, Flurstück 1 260; Gemarkung Großräschen, Flur 6, Flurstück 676; Gemarkung Großräschen, Flur 6, Flurstück 579 | national-cadastre, cadastral-municipalities | | Zur g. g. A. „Großräschener See“ gehören die zulässiger Weise mit Reben bepflanzten Flächen der Gemarkung Großräschen, die im klimatischen Einflussbereich des gleichnamigen Sees mit einer Gewässerfläche von ca. 775 ha ge | +| de | Saale-Unstrut | cadastral-units, municipality-list, partial-municipality, map-annex | Gemarkungen ... im Landkreis Burgenlandkreis; Quedlinburg und Westerhausen im Landkreis Harz; Beyernaumburg, Lüttchendorf, Neehausen, Seeburg, Unterrißdorf und Wallhausen (Helme) im Landkreis Mansfeld-Südharz | cadastral-municipalities, administrative-units, annexed-map-only | | Zur geschützten Ursprungsbezeichnung gehören weiterhin die Rebflächen der Gemarkungen Werder (Havel), Phöben, Plessow und Neu Töplitz der Stadt Werder (Havel) im Landkreis Potsdam-Mittelmark des Landes Brandenburg. | +| de | Sächsischer Landwein ⚠ | cadastral-units, municipality-list, partial-municipality | die Fluren 3, 4, 6 in der Gemarkung Jessen; die Fluren 1 und 8 in der Gemarkung Kleindröben; der Flur 2 in der Gemarkung Schweinitz | cadastral-municipalities, national-cadastre, administrative-units, annexed-map-only | | Zur geschützten geografischen Angabe „Sächsischer Landwein“ gehören ferner die Rebflächen der Fluren 3, 4, 6 in der Gemarkung Jessen, die Fluren 1 und 8 in der Gemarkung Kleindröben und der Flur 2 in der Gemarkung Schwei | +| de | Uhlen Blaufüsser Lay | cadastral-units, named-sites | Gemarkung von Winningen im Landkreis Mayen-Koblenz; Parzelle 2219/1 (Gemarkung Winningen); Parzellen 2179, 2181/1, 2186, 2190, 2189/2 (Gemarkung Winningen) | national-cadastre, cadastral-municipalities | | Das Gebiet beginnt mit der Parzelle 2219/1 und endet flussabwärts mit den Parzellen 2179, 2181/1, 2186, 2190, 2189/2. | +| de | Uhlen Laubach | cadastral-units, named-sites | Gemarkung von Winningen im Landkreis Mayen-Koblenz; Parzellen 256/112, 2571/118, 2581/119, 2398/0, 2395/3, 2393/0 (Gemarkung Winningen); Parzelle 2222/1 (Gemarkung Winningen) | national-cadastre, cadastral-municipalities | | Das Gebiet beginnt flussabwärts mit den Parzellen 256/112, 2571/118, 2581/119, 2398/0, 2395/3, 2393/0 und endet flussabwärts mit der Parzelle 2222/1. | +| de | Uhlen Roth Lay ⚠ | cadastral-units, named-sites, natural-boundary | Gemarkungen von Kobern und Winningen im Landkreis Mayen-Koblenz; Taleinschnitt Belltal; Parzellennummern 262/54 und 156/53 | national-cadastre, cadastral-municipalities, roads-rivers-osm | | Das Gebiet beginnt mit der ersten Weinbergen flussabwärts des Taleinschnittes Belltal - Parzellennummern 262/54 und 156/53 und endet flussabwärts mit folgenden Parzellen: Nr. 6/1, 117/1 und 2394/3. | +| de | Württemberg | cadastral-units, municipality-list, partial-municipality, map-annex | Abstatt (835); Cannstatt (1463); Heilbronn (910) | cadastral-municipalities, administrative-units, annexed-map-only | | Die genaue Abgrenzung ergibt sich aus den Karten mit den parzellenmäßig abgegrenzten Rebflächen der oben genannten Gemeinden, welche unter www.ble.de/eu-qualitaetskennzeichen-wein einsehbar sind. | +| de | Würzburger Stein-Berg ⚠ | cadastral-units, named-sites | eingetragene Flurnummern der kreisfreien Stadt Würzburg, Bayern; Einzellage Würzburger Stein; SW-Ecke v. 7469 | national-cadastre, roads-rivers-osm | | Von SW-Ecke v. 7469 ostwärts entlang Weg 7468/1, entlang Ostgrenze von 5857 nordwärts bis Weg 5853/3, dort ostwärts bis östlichen Ende 5695. | +| es | Abadía Retuerta | cadastral-units | término municipal de Sardón de Duero, en la provincia de Valladolid; polígono 2; parcela 1 | sigpac-poligonos, national-cadastre | | La zona geográfica delimitada pertenece al término municipal de Sardón de Duero, en la provincia de Valladolid, sita dentro del polígono 2 incluye las siguientes parcelas: parcela 1, parcela 4, parcela 5, parcela 6, parc | +| es | Alicante | municipality-list, cadastral-units, named-sites, partial-municipality | Villena y las pedanías de Caudete (Albacete) limítrofes al término de la misma (Palacios, Mingote, Derramador, El Rincón y la Cañada); En concreto los polígonos de acuerdo al SIGPAC de 2019: 1,2,3,4,5,6 y 7; Del término municipal de Abanilla. Pol. 01, 02, 03, 04, 05, 06, 07, 08, 10 y 029. | sigpac-poligonos, cadastral-municipalities, administrative-units | | Del término municipal de Jumilla: Pol.056, 057, 058, 059, 068, 069, 070, 071, 072, 073,074, 075, 076, 077, 078, 079, 085, 086, 087, 088, 089, 090, 091, 092. | +| es | Alto Vinalopó | municipality-list, cadastral-units, named-sites, partial-municipality | Subzona Alto Vinalopó: Beneixama, Biar, Campo de Mirra, Cañada, Sax, Salinas, Banyeres de Mariola, Villena; las pedanías de Caudete (Albacete) limítrofes al término de la misma (Palacios, Mingote, Derramador, El Rincón y la Cañada); En concreto los polígonos de acuerdo al SIGPAC de 2019: 1,2,3,4,5,6 y 7 | sigpac-poligonos, cadastral-municipalities, administrative-units | | En concreto los polígonos de acuerdo al SIGPAC de 2019: 1,2,3,4,5,6 y 7. | +| es | L’Alcoià | municipality-list, cadastral-units, partial-municipality, named-sites | Subzona L’Alcoià: Castalla, Ibi, Onil, Tibi, Alcoy, Benifallim, Penáguila; las pedanías de Caudete (Albacete) limítrofes al término de la misma (Palacios, Mingote, Derramador, El Rincón y la Cañada). En concreto los polígonos de acuerdo al SIGPAC de 2019: 1,2,3,4,5,6 y 7; Del término municipal de Abanilla. Pol. 01, 02, 03, 04, 05, 06, 07, 08, 10 y 029. | sigpac-poligonos, cadastral-municipalities, administrative-units | | Villena y las pedanías de Caudete (Albacete) limítrofes al término de la misma (Palacios, Mingote, Derramador, El Rincón y la Cañada). En concreto los polígonos de acuerdo al SIGPAC de 2019: 1,2,3,4,5,6 y 7. | +| es | Medio Vinalopó | municipality-list, cadastral-units, partial-municipality, named-sites | Subzona Medio Vinalopó: Algueña, Elda, Hondón de las Nieves, Hondón de los Frailes, La Romana, Monóvar y su partida Mañán, Petrer, Pinoso y Novelda; la partida de Barbarroja, del término municipal de Orihuela; Del término municipal de Abanilla. Pol. 01, 02, 03, 04, 05, 06, 07, 08, 10 y 029. | sigpac-poligonos, cadastral-municipalities, administrative-units | | Del término municipal de Jumilla: Pol.056, 057, 058, 059, 068, 069, 070, 071, 072, 073,074, 075, 076, 077, 078, 079, 085, 086, 087, 088, 089, 090, 091, 092. | +| es | Arlanza ⚠ | municipality-list, cadastral-units, partial-municipality | Ciruelos de Cervera (polígono catastral n o 518); Los Balbases (polígono catastral n o 523); Dentro del Término municipal de Los Balbases, únicamente será apto para el cultivo del viñedo amparado la zona situada en el polígono catastral 523 | sigpac-poligonos, national-cadastre, cadastral-municipalities, administrative-units | | Dentro del Término municipal de Ciruelos de Cervera únicamente será apto para el cultivo de viñedo amparado la zona situada en el polígono catastral 518. | +| es | Arribes | municipality-list, cadastral-units | Dentro del municipio de Almaraz de Duero los polígonos 2, 3, 4, 12, 13, 14 y 16; del municipio de Muelas del Pan el polígono 1; Provincia de Salamanca: Ahigal de los Aceiteros, Aldeadávila de la Ribera, Corporario, Fregeneda (La), Hinojosa de Duero, Lumbrales, Masueco, Mieza, Peña (La), Pereña de la Ribera | sigpac-poligonos, national-cadastre, cadastral-municipalities, administrative-units | | Dentro del municipio de Almaraz de Duero los polígonos 2, 3, 4, 12, 13, 14 y 16 y del municipio de Muelas del Pan el polígono 1. | +| es | Aylés | cadastral-units, named-sites | término municipal de Mezalocha (Zaragoza); Polígono 16: parcelas 2, 3, 4, 12 y 13; Polígono 19: parcelas 212, 213 y 214 | national-cadastre, sigpac-poligonos, cadastral-municipalities | | El área de la finca «Aylés» susceptible de contener viñas que produzcan vino de pago comprende las siguientes parcelas del término municipal de Mezalocha (Zaragoza): | +| es | Bolandin | cadastral-units | término municipal de Ablitas (Comunidad Foral de Navarra); Polígono 5, parcelas 1658, 1659, 1660, 1661, 1662, 1663, 1884, 1885; recinto A parcial, E, F, G y H | sigpac-poligonos, national-cadastre, cadastral-municipalities | | Está ubicada en el término municipal de Ablitas (Comunidad Foral de Navarra) y está constituida por las siguientes parcelas catastrales: Polígono 5, parcelas 1658, 1659, 1660, 1661, 1662, 1663, 1884, 1885 y recinto A par | +| es | Calzadilla | cadastral-units | término municipal de Huete (Cuenca); polígono 510; parcelas 4, 4a, 4b, 4c | sigpac-poligonos, national-cadastre | | Incluyen las siguientes parcelas del término municipal de Huete (Cuenca), parcelas 4, 4a, 4b, 4c, 5, 5a, 5b, 6, 14, 14b, 42, 42a, 42b, 42c, 42d, 42e, 42f, 43a, 43b, 44, 44a, 44aab y 45, del polígono 510. | +| es | Campo de Borja | municipality-list, cadastral-units, partial-municipality | polígonos catastrales número 4, 5, 6, 7, 8, 9, 10 y 11 del término municipal de Mallén; polígonos catastrales número 1, 2, 3, 4, 5, 6, 7,8, 9, 10, 11, 12, 13, 14 y 19 del término municipal de Fréscano; Agón | sigpac-poligonos, national-cadastre, administrative-units | | así como en los polígonos catastrales número 4, 5, 6, 7, 8, 9, 10 y 11 del término municipal de Mallén, y en los polígonos catastrales número 1, 2, 3, 4, 5, 6, 7,8, 9, 10, 11, 12, 13, 14 y 19 del término municipal de Fré | +| es | Campo de Calatrava | municipality-list, cadastral-units, partial-municipality | polígono22 parcela 74 del municipio de Ciudad Real; Aldea del Rey; Almagro | sigpac-poligonos, national-cadastre, administrative-units | | Incluye los siguientes municipios de la provinciade Ciudad Real: Aldea del Rey, Almagro, ... Villar del Pozo, polígono22 parcela 74 del municipio de Ciudad Real. | +| es | Campo de La Guardia | cadastral-units | término municipal de La Guardia (Toledo); Parcelas 189 a,b,c y d, 270 a y b y 278 a, b c y d del polígono 39; Parcelas 60 y 54 del polígono 40 | sigpac-poligonos, national-cadastre | | Incluye las siguientes parcelas del término municipal de La Guardia (Toledo): - Parcelas 189 a,b,c y d, 270 a y b y 278 a, b c y d del polígono 39; - Parcelas 60 y 54 del polígono 40; y - Parcelas 7, 8, 9 a, b y c, 10, 8 | +| es | Cataluña | municipality-list, cadastral-units, partial-municipality, named-sites | Alcarràs: las parcelas n o . 9022, 9017 y 9005 del polígono catastral n o . 6 y las parcelas n o . 3, 57, 9001, 9003, 9004, 9007 y 9027 del polígono catastral n o . 15; Almacelles: las parcelas n o . 25, 180, 193 y 196 del polígono catastral n o . 5; Barcelona, la parcela n o . 1 del polígono catastral n o . 1 | sigpac-poligonos, national-cadastre, administrative-units | | Alcarràs: las parcelas n o . 9022, 9017 y 9005 del polígono catastral n o . 6 y las parcelas n o . 3, 57, 9001, 9003, 9004, 9007 y 9027 del polígono catastral n o . 15 | +| es | Chozas Carrascal | cadastral-units | término municipal de Requena (Valencia); pedanía de San Antonio; Polígono 14, parcelas 293, 294, 297, 300, 301 | sigpac-poligonos, national-cadastre | | La zona delimitada se encuentra en el término municipal de Requena (Valencia), en la pedanía de San Antonio, distribuida del siguiente modo: Polígono 14, parcelas 293, 294, 297, 300, 301 y Polígono 16, parcelas 101, 111, | +| es | Conca de Barberà | municipality-list, cadastral-units, partial-municipality | Del municipio de Vilanova de Prades los polígonos catastrales 11, 16, 20, 21 y la parcela 23 del polígono catastral 10.; Del municipio de Cabra del Camp el polígono 1, parcelas 13, 15, 18, 28, 29, 34, 66 y 77,.Polígono 4, parcelas 35 y 51.; Del municipio de Aiguamúrcia el polígono 35, parcela 14; polígono 36, parcela 1, polígono 41, parcela 13. | sigpac-poligonos, national-cadastre, administrative-units | | Del municipio de Vilanova de Prades los polígonos catastrales 11, 16, 20, 21 y la parcela 23 del polígono catastral 10. | +| es | Dehesa del Carrizal | cadastral-units | parcelas del término municipal de Retuerta del Bullaque (Ciudad Real), parcelas 449a, 449ec, 449ea, 449eb, 449ada, 449adb, 449adc y 449eca del polígono 9; las parcelas 860a, 860b y 860c del polígono 23; La superficie del viñedo asciende a 26,2764 hectáreas. | sigpac-poligonos, national-cadastre | | Incluyen las siguientes parcelas del término municipal de Retuerta del Bullaque (Ciudad Real), parcelas 449a, 449ec, 449ea, 449eb, 449ada, 449adb, 449adc y 449eca del polígono 9, y las parcelas 860a, 860b y 860c del polí | +| es | Dehesa Peñalba | cadastral-units | término municipal de Villabáñez (Valladolid), contando con una superficie total de 91,4287 ha; Polígono 6: parcelas 5 121, 5 122, 5 123, 5 124 y 5 125 y el recinto 3 de la parcela 9 003 (canal de riego); las referencias citadas anteriormente corresponden al SIGPAC publicado en 2020 | sigpac-poligonos, national-cadastre | | Polígono 6: parcelas 5 121, 5 122, 5 123, 5 124 y 5 125 y el recinto 3 de la parcela 9 003 (canal de riego). | +| es | Dominio de Valdepusa | cadastral-units | las parcelas 942a, 942h y 945a del polígono 13 del término municipal de Malpica de Tajo (Toledo); Registro Vitícola: Polígono17, Parcela 1, Subparcelas a y b, Parcela 2, Subparcelas ab, b y c, y Parcela 3, Subparcelas a, b, c, d, e y f; La superficie del viñedo es de 49,0848 hectáreas. | sigpac-poligonos, national-cadastre | | Incluye las parcelas 942a, 942h y 945a del polígono 13 del término municipal de Malpica de Tajo (Toledo) que se corresponden con las siguientes del Registro Vitícola: Polígono17, Parcela 1, Subparcelas a y b, Parcela 2, | +| es | El Terrerazo | cadastral-units | término municipal de Utiel (Valencia); Polígono 8: parcelas 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 456, 488; Polígono 14: parcelas 381, 382, 383, 384, 385, 373a | sigpac-poligonos, national-cadastre | | La superficie de viñedo actualmente establecida según registro se encuentra en el término municipal de Utiel (Valencia), distribuida del siguiente modo: - Polígono 8: parcelas 442, 443, 444, 445, 446, 447, 448, 449, 450, | +| es | El Vicario | cadastral-units | Incluye 86 parcelas de 9 polígonos del término municipal de Ciudad Real.; CIUDAD REAL / CIUDAD REAL / 202 / 47; CIUDAD REAL / CIUDAD REAL / 7 / 7 | sigpac-poligonos, national-cadastre | | Incluye 86 parcelas de 9 polígonos del término municipal de Ciudad Real. | +| es | Finca Élez | cadastral-units | término municipal de El Bonillo (Albacete); Polígono 95, parcelas 16d, 16da, 16db y 17; Polígono 97, parcelas 24 | sigpac-poligonos, national-cadastre | | Incluye las siguientes parcelas del término municipal de El Bonillo (Albacete): Polígono 95, parcelas 16d, 16da, 16db y 17. Polígono 97, parcelas 24. Polígono 100, parcelas 2, 3 y 8. | +| es | Guijoso | cadastral-units | término municipal de El Bonillo (Albacete); Polígono 115: parcelas 1ma, 1mb y 1mc; Polígono 116: parcelas 1aa, 1ab y 1af | sigpac-poligonos, national-cadastre | | Incluye las siguientes parcelas del término municipal de El Bonillo (Albacete): - Polígono 115: parcelas 1ma, 1mb y 1mc. - Polígono 116: parcelas 1aa, 1ab y 1af. - Polígono 119: parcelas 22a, 23a, 24b, 24i y 25d. - Políg | +| es | La Jaraba | cadastral-units | término municipal de El Provencio (Cuenca); polígono 9, parcelas 14b, 14d, 14f, 14h, 26d, 26e, 26h, 26i, 26j, 26k, 26m, 26n, 26v; Registro Vitícola | sigpac-poligonos, national-cadastre | | La zona delimitada se encuentra en el término municipal de El Provencio (Cuenca). Posee la siguiente distribución parcelaria, según el Registro Vitícola: polígono 9, parcelas 14b, 14d, 14f, 14h, 26d, 26e, 26h, 26i, 26j, | +| es | La Mancha | municipality-list, partial-municipality, cadastral-units | El Bonillo con excepción de los polígonos 95, parcelas 16 d, 16da, 16db y 17; polígono 97, parcela 24, polígono 100, parcelas 2,3 y 8; el polígono 115 parcelas 1ma, 1mb 1mc; polígono 116 parcelas 1aa, 1ab y 1af | sigpac-poligonos, national-cadastre, administrative-units | | También se incluye El Bonillo con excepción de los polígonos 95, parcelas 16 d, 16da, 16db y 17; polígono 97, parcela 24, polígono 100, parcelas 2,3 y 8, el polígono 115 parcelas 1ma, 1mb 1mc; polígono 116 parcelas 1aa, | +| es | Los Balagueses | cadastral-units | término municipal de Requena; Polígono 127 parcela 372 y recintos (4,9,34 y 35); parcela 57 recintos (15,2,46,95) | sigpac-poligonos, national-cadastre | | La zona de producción de los vinos amparados se encuentra en el término municipal de Requena, distribuida del siguiente modo: Polígono 127 parcela 372 y recintos (4,9,34 y 35), parcela 57 recintos (15,2,46,95) y recintos | +| es | Los Cerrillos | cadastral-units | término municipal de Argamasilla de Alba (Ciudad Real); POLÍGONO/PARCELA; 109 — 62,63 | sigpac-poligonos, national-cadastre | | Incluye las siguientes parcelas del término municipal de Argamasilla de Alba (Ciudad Real): POLÍGONO/PARCELA 109 62,63 111 15,30 y 9001 131 71 151 115 a 121,190,277,278,9001, 9002, 9004 a 9006,9008, 9023 | +| es | Méntrida | municipality-list, cadastral-units, partial-municipality | Argés (únicamente los polígonos 3 y 5); Guadamur (únicamente los polígonos 17 y 18); Situada al norte de la provincia de Toledo | sigpac-poligonos, administrative-units, national-cadastre | | Situada al norte de la provincia de Toledo: provincia de Toledo: Albarreal de Tajo, Alcabón, Aldeaencabo, Almendral de la Cañada, Almorox, Arcicollar, Argés (únicamente los polígonos 3 y 5), Barcience, Bargas, Buenaventu | +| es | Montsant | cadastral-units, municipality-list, partial-municipality | Falset: Los polígonos números 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 23, 26, 27, 28, 29, 30 y 31; Falset: Polígono 2 parcelas de la 1 a la 37, de la 41 a la 70, de la 72 a la 91, parte sur de la 93 (1,631 ha), 94, 95, parte de la 102 (2,5052 ha), 125, 127, de la 132 a la 135, de la 137 a la 145, 148 y de la 151 a la 153; Falset: Polígono 3 parte sur de la parcela 47 (2,1549 ha), parcelas 48 y 49, parte sur de la parcela 50 (1,419 ha) | sigpac-poligonos, national-cadastre | | Falset: Los polígonos números 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 23, 26, 27, 28, 29, 30 y 31; | +| es | Pago Florentino | cadastral-units, partial-municipality | Malagón (Ciudad Real): parcelas 30, 351a, 351b, 351ca, 351cb, 351d, y 351e del polígono 30; La superficie del viñedo asciende a 58,1192 hectáreas | sigpac-poligonos, national-cadastre | | Incluye las siguientes parcelas del término municipal de Malagón (Ciudad Real): parcelas 30, 351a, 351b, 351ca, 351cb, 351d, y 351e del polígono 30. | +| es | Prado de Irache | partial-municipality, cadastral-units, named-sites | Ayegui (Navarra): Norte: limita con parcelas 354, 1029, 535, 861; huertas de Zuloandía; camino de Igúzquiza | national-cadastre, roads-rivers-osm | | Norte: limita con parcelas 354, 1029, 535, 861, huertas de Zuloandía, camino de Igúzquiza y Hoya de Lucea, Gastisun y Sarnabiaga. | +| es | Priorat | cadastral-units, partial-municipality | la parte norte del municipio de Falset comprendida por los polígonos números 1, 4, 5, 6, 7, 21 y 25 enteros; Falset polígono 2: parcelas 38, 39, 40, 71, 92, parte oeste de la 93 (1,69 ha), 96, 97, 98, 99, 100, 101, parte norte de la 102 (0,16 ha); Falset polígono 3: parte norte de la 47 (17 ha), parte norte de la 50 (2,6 ha), parte norte de la 52 (3 ha), parte norte de la 53 (14 ha) | sigpac-poligonos, national-cadastre | | la parte norte del municipio de Falset comprendida por los polígonos números 1, 4, 5, 6, 7, 21 y 25 enteros; y por las parcelas 38, 39, 40, 71, 92, parte oeste de la 93 (1,69 ha), 96, 97, 98, 99,100, 101, parte norte de | +| es | Ribera del Gállego - Cinco Villas ⚠ | municipality-list, cadastral-units, partial-municipality, natural-boundary | En Zaragoza Polígonos Catastrales Nº. 1 al 36; Polígonos Nº 42 al 55, 61, 62, 66, 67, 204, 205, 206, 207, 208, 209, 210, 218, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233 y 234; polígonos pertenecientes a la cuenca del río Gállego | sigpac-poligonos, cadastral-municipalities, administrative-units | | **(En Zaragoza Polígonos Catastrales Nº. 1 al 36. Polígonos Nº 42 al 55, 61, 62, 66, 67, 204, 205, 206, 207, 208, 209, 210, 218, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233 y 234), polígonos pertenecientes a la cuen | +| es | Río Negro | cadastral-units | COGOLLUDO polígono 1, parcelas 5138 (22,45 ha), 5140, 5141 (156,15 ha), 5143, 5144, 5145, 5160, 5165, 9002, 9007, 9008; COGOLLUDO polígono 2, parcelas 5108, 5109 (39,27 ha), 5110, 5135, 5136, 5137, 5138 (41,49 ha); COGOLLUDO polígono 502, parcela 5789 (2,43 ha) | sigpac-poligonos, national-cadastre | | Incluye las siguientes parcelas, referidas al Sistema de Información Geográfica de Parcelas Agrícolas (SIGPAC), publicado a la fecha de la Decisión Favorable de la Autoridad competente, constituyendo una superficie conti | +| es | Rosalejo | cadastral-units | En el Término municipal de Anchuras: Polígono 5, parcelas: 319, 333; Anchuras Polígono 7, parcelas: 1 a 4, 70, 71, 73, 78, 87, 98, 99, 100, 108, 109, 111, 135 a 138, 140 a 146, 155 a 157, 1077, 1082, 1083, 60001; Anchuras Polígono 9, parcelas: 29 a 32, 34, 44, 46, 47, 49, 50, 51, 54 | sigpac-poligonos, national-cadastre | | La zona delimitada de DOP Rosalejo está ubicada en los términos municipales de Anchuras (Ciudad Real) y Sevilleja de la Jara (Toledo). Las parcelas afectadas son las siguientes, referidas al sistema de información geográ | +| es | Rueda | municipality-list, cadastral-units | Órbita (polígonos catastrales 1, 2, 4 y 5); Palacios de Goda (polígonos catastrales 14, 17, 18, 19 y 20); Coca (polígono 7, correspondiente a la pedanía de Villagonzalo de Coca) | sigpac-poligonos, cadastral-municipalities, administrative-units | | Provincia de Ávila: Blasconuño de Matacabras, Madrigal de las Altas Torres, Órbita (polígonos catastrales 1, 2, 4 y 5) y Palacios de Goda (polígonos catastrales 14, 17, 18, 19 y 20). | +| es | Sierra Sur de Jaén | municipality-list, cadastral-units, partial-municipality | Alcalá la Real ,Castillo de Locubín, Frailes, Fuensanta de Martos, Los Villares y Valdepeñas de Jaén; Alcaudete (polígonos catastrales actuales del 1 al 12 y del 18 al 29, inclusive estos); Martos (polígonos catastrales actuales del 33 al 42, ambos incluidos) | sigpac-poligonos, national-cadastre, administrative-units | | así como las zonas de sierra pertenecientes a los términos municipales de Alcaudete (polígonos catastrales actuales del 1 al 12 y del 18 al 29, inclusive estos) y Martos (polígonos catastrales actuales del 33 al 42, ambo | +| es | Tarragona ⚠ | municipality-list, cadastral-units, partial-municipality | Cabra del Camp (excepto las parcelas 13,15, 18, 28, 29, 34, 66 y 77 del polígono 1; las parcelas 35 y 51 del polígono 4); Tivissa (excepto los polígonos 2, 4, 6, 7, 8, 9, 10, 11, 12, 21, 22 y 23; las parcelas 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 253 y 254 del Polígono 17; la parcela 29 del polígono 24); Garcia, (excepto parcela 66 del Polígono 3; parcelas 101, 105 y de la 111 a la 113 del Polígono 6; los Polígonos 7, 8, 9, 10 y 11; las parcelas de la 1 a la 13, de la 17 a la 38, 45, 133 y 134 del Polígono12; las parcelas de la 70 a la 102, 104, 111, de la 220 a la 234, 314 y la 315 Polígono 13; la parcela 3 del Polígono 15; la parcela 65 del Polígono 22; las parcelas de la 26 a la 43, de la 60 a la 68, de la 70 a la 81, la 83, 84, 86, 87 y 88 del polígono 23) | sigpac-poligonos, national-cadastre, administrative-units | | Tivissa (excepto los polígonos 2, 4, 6, 7, 8, 9, 10, 11, 12, 21, 22 y 23; las parcelas 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 253 y 254 del Polígono 17; la parcela 29 del polígono 24) | +| es | Tharsys ⚠ | cadastral-units, natural-boundary, map-annex | término municipal de Requena (Valencia); Polígono: 47; Parcelas: 205, 212, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,232, 233, 234, 235, 316, 880 y 923a | sigpac-poligonos, national-cadastre, roads-rivers-osm, annexed-map-only | | La superficie de viñedo según medición SIGPAC es de 19,5 Ha., está situada en el término municipal de Requena (Valencia). Incluyendo las siguientes parcelas, según la nomenclatura del Sistema de Información Geográfica de | +| es | Uclés ⚠ | municipality-list, cadastral-units, partial-municipality | De Cuenca: Acebrón (El), Alcázar del Rey, Almendros, Belinchón, Carrascosa del Campo, Fuente de Pedro Naharro, Horcajo de Santiago, Huelves, Huete, Langa, Loranca del Campo, Paredes, Pozorrubio, Rozalén del Monte, Saelices, Tarancón, Torrubia del Campo, Tribaldos, Uclés, Valparaíso de Arriba, Valparaíso de Abajo, Vellisca, Villamayor de Santiago, Villarrubio y Zarza de Tajo; Cabezamesada: polígonos 501 a 505 (a.i.), 507 y 508; Corral de Almaguer: polígonos 12, 17 a 25 (a.i.), polígonos 72 y 73 | sigpac-poligonos, national-cadastre, administrative-units | | De Toledo: Cabezamesada: polígonos 501 a 505 (a.i.), 507 y 508; Corral de Almaguer: polígonos 12, 17 a 25 (a.i.), polígonos 72 y 73 y Santa Cruz de la Zarza. | +| es | Urbezo ⚠ | cadastral-units, partial-municipality, map-annex | polígono catastral 35 del término municipal de Cariñena, Comunidad Autónoma de Aragón, provincia de Zaragoza; 232,0214 ha; 51 parcelas: 1, 6, 8, 9, 12, 13, 14, 17, 18, 20, 21, 24, 26, 27, 28, 31, 33, 34, 35, 36, 48, 51, 53, 54, 55, 63, 67, 68, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93 y 94 | sigpac-poligonos, national-cadastre, roads-rivers-osm, annexed-map-only | | Se corresponde con polígono catastral 35 del término municipal de Cariñena, Comunidad Autónoma de Aragón, provincia de Zaragoza. | +| es | Urueña | cadastral-units | municipio de Urueña, en la provincia de Valladolid; superficie total de 78 ha; Polígono 1: parcelas 114, 117, 121, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 140, 168, 5154, 5155, 5156, 5157, 5158, 5159, 9022, 9023, 9024 y 10120 | sigpac-poligonos, national-cadastre | | El área geográfica delimitada de la DOP URUEÑA se encuentra ubicada en el municipio de Urueña, en la provincia de Valladolid, contando con una superficie total de 78 ha, constituyendo una superficie continua de terreno d | +| es | Valdejalón ⚠ | municipality-list, partial-municipality, cadastral-units, natural-boundary | Zaragoza (en Zaragoza, polígonos catastrales 152, 153, 154, 155 y 178, pertenecientes a la cuenca del río Jalón); polígonos catastrales 152, 153, 154, 155 y 178; cuenca del río Jalón | sigpac-poligonos, national-cadastre, administrative-units, roads-rivers-osm | | Zaragoza (en Zaragoza, polígonos catastrales 152, 153, 154, 155 y 178, pertenecientes a la cuenca del río Jalón). | +| es | Vallegarcía | cadastral-units | parcela catastral Tel. 448 del polígono 9 del término municipal de Retuerta del Bullaque, de la provincia de Ciudad Real; superficie de 1 521 hectáreas | national-cadastre, sigpac-poligonos | | La zona geográfica delimitada corresponde a la parcela catastral Tel. 448 del polígono 9 del término municipal de Retuerta del Bullaque, de la provincia de Ciudad Real, con una superficie de 1 521 hectáreas. | +| es | Vera de Estenas | cadastral-units | término municipal de Utiel (Valencia), Polígono 37, parcelas 101, 104, 141, 143, 144, 146, 147, 148, 149; término municipal de Utiel (Valencia), Polígono 38, parcelas 195, 196, 197, 204, 205, 206a1 y a2, b, d, e, f y g, 207, 208, 337 a, b y d, 218, 220, y 534 | national-cadastre, sigpac-poligonos | | La zona delimitada se encuentra en el término municipal de Utiel (Valencia), distribuida del siguiente modo: Polígono 37, parcelas 101, 104, 141, 143, 144, 146, 147, 148, 149 y Polígono 38, parcelas 195, 196, 197, 204, 2 | +| gr | playies-abelou | municipality-list, partial-municipality, map-annex | Κοινοτήτων Παλαιόκαστρο, Καλλιθέα, Μαραθόκαμπος, Πυθαγόρειο, Δρακαίοι, Νεοχώρι, Σκουραίϊκα; που βρίσκονται εκτός των καθορισμένων ζωνών παραγωγής οίνων ΠΟΠ.ΣΑΜΟΣ; GR412 Σάμος | administrative-units, annexed-map-only | ΠΟΠ Σάμος (named in the text, not listed as an overlap partner in the record header): yes | Η οριοθετημένη περιοχή για την παραγωγή των οίνων Π.Γ.Ε. Πλαγιές Αμπέλου περιλαμβάνει τις περιοχές των Κοινοτήτων Παλαιόκαστρο, Καλλιθέα, Μαραθόκαμπος, Πυθαγόρειο, Δρακαίοι, Νεοχώρι, Σκουραίϊκα που βρίσκονται εκτός των κ | +| hu | Etyek-Buda | cadastral-units, municipality-list, partial-municipality | Budapest (XII. kerület 9597/1 hrsz., XXII. Kerület - Budafok); XII. kerület 9597/1 hrsz.; XXII. Kerület - Budafok | national-cadastre, administrative-units | | Budapest (XII. kerület 9597/1 hrsz., XXII. Kerület - Budafok) | +| it | Alto Adige ⚠ | municipality-list, partial-municipality, cadastral-units, altitude-limit, natural-boundary, named-sites | il limite altimetrico di 650 metri nel comune di Scena; zona di produzione del vino «Santa Maddalena»; strada statale del Brennero n. 12 con il confine comunale di Laives, in prossimita' del km 427,700 | administrative-units, dem-altitude, roads-rivers-osm | | All'interno della zona di cui sopra sono da escludersi tutti i territori appartenenti alla zona di produzione del vino «Santa Maddalena» di cui al successivo punto 3.4; | +| it | Amarone della Valpolicella | cadastral-units, partial-municipality, altitude-limit, natural-boundary, named-sites, municipality-list | segue il confine S-E del foglio 4° Comune di Sant’Ambrogio di Valpolicella; segue il limite SUD del foglio XIII° del Comune di Negrar sez. C; segue la linea di quota 500 lungo le pendici montuose della vallata Valpantena, partendo da località Sasso, in Comune di Negrar | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | soave: yes | Da qui devia a N-E seguendo Via M. Kolbe, segue il confine S-E del foglio 4° Comune di Sant’Ambrogio di Valpolicella. | +| it | Asolo Montello | municipality-list, partial-municipality, cadastral-units, altitude-limit, natural-boundary, named-sites, map-annex | Sottozona Venegazzù ... porzione della frazione di Venegazzù corrispondente al foglio catastale n° 16 (comune di Volpago del Montello); confine tra i fogli catastali del comune di Volpago del Montello n° 16 e 21; confine tra il foglio catastale n° 16 e i fogli catastali n° 13 e 12 (Volpago del Montello) | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | | Sottozona Venegazzù, interessa parte del territorio del comune di Volpago del Montello e precisamente la porzione della frazione di Venegazzù corrispondente al foglio catastale n° 16. | +| it | Casteggio | partial-municipality, cadastral-units, named-sites | confine di foglio 4 del Comune di Borgo Priolo; SS 10 (Ing. Adolfo Mazza); strada vicinale “Cascina Gioiello” | national-cadastre, roads-rivers-osm, administrative-units | | Proseguiamo verso destra lungo il confine di foglio 4 del Comune di Borgo Priolo fino al congiungimento con la strada Comunale “Della Cattabrega”, qui svoltiamo a sinistra sulla medesima fino all’incrocio con via “Valle | +| it | Cinque Terre | cadastral-units, partial-municipality, municipality-list, named-sites, natural-boundary | gli interi comuni di Riomaggiore, Vernazza e Monterosso; parte del territorio del comune di La Spezia, denominato "Tramonti di Biassa" e "Tramonti di Campiglia"; la mulattiera che dal Monte della Madonna (quota 527) | national-cadastre, cadastral-municipalities, administrative-units, roads-rivers-osm, dem-altitude | | dalla strada litoranea La Spezia - Manarola in corrispondenza dell'ingresso della galleria di Lemmen si scende seguendo la linea di delimitazione del Foglio di mappa n. 30 con i Fogli n. 31 e 32 fino al mare | +| it | Colli della Toscana centrale | municipality-list, partial-municipality, natural-boundary | partendo dalla Vetta alle Croci in provincia di Firenze; i limiti dei comuni di Pontassieve, Rufina, Pelago, Reggello, che rimangono così compresi per l'intero territorio; lungo la strada statale n. 73 fino alla località Palazzo | administrative-units, roads-rivers-osm | Chianti [chianti]: yes | e da questo punto si identifica con la zona di produzione del vino Chianti fino ad incontrare, in località Casa Faia in confine di Quarrata. | +| it | Colline Joniche Tarantine | partial-municipality, cadastral-units, natural-boundary, municipality-list | fogli di mappa nn 46, 47, 48 e 49 del comune di Ginosa (interamente inclusi); fogli di mappa n. 66, 68, 84, 87 e 102 del comune di Ginosa (interamente inclusi); confine sud del foglio 103 (interamente incluso) | national-cadastre, cadastral-municipalities, roads-rivers-osm, administrative-units, dem-altitude | | Proseguendo in direzione Sud, lungo i confini dei fogli di mappa n. 66, 68, 84, 87 e 102 del comune di Ginosa (interamente inclusi), si giunge ad intersecare la strada comunale di Leuci. | +| it | Collio Goriziano ⚠ | partial-municipality, cadastral-units, natural-boundary | attraversando le particelle catastali 680/3, 685/2, 685/1, 542 e 544/5; dal cavalcavia della ferrovia Gorizia-Udine, prende la strada che dal quadrivio di Madonna del Fante porta direttamente a Piedimonte del Calvario; costeggia il corso del fiume Isonzo fino ad incontrarsi con il confine di Stato | national-cadastre, roads-rivers-osm, administrative-units | friuli-isonzo: yes | Da qui, seguendo il piede della collina, toccando Case Papalin ed attraversando le particelle catastali 680/3, 685/2, 685/1, 542 e 544/5, si ricongiunge con la strada che a nord porta a Villanova di Farra passando per qu | +| it | Conegliano Valdobbiadene - Prosecco | municipality-list, partial-municipality, cadastral-units, named-sites, altitude-limit, natural-boundary, map-annex | comuni di: Conegliano - San Vendemiano - Colle Umberto - Vittorio Veneto – Tarzo - Cison di Valmarino - San Pietro di Feletto - Refrontolo - Susegana - Pieve di Soligo - Farra di Soligo -- Follina - Miane – Vidor - Valdobbiadene; la località Fornace (q. 175); la curva di livello di quota 500 | national-cadastre, administrative-units, roads-rivers-osm, dem-altitude, annexed-map-only | | Dal punto di congiunzione dei suddetti mappali il confine corre tra i mappali nn. 547 e 735, taglia i mappali nn. 540 e 543, seguendo la stessa direzione dell'ultimo tratto di divisione tra i mappali nn. 547 e 735 fino a | +| it | Delia Nivolelli | cadastral-units, partial-municipality | comune di Mazara del Vallo: il territorio compreso nei seguenti fogli di mappa: 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30; il comune di Marsala: il territorio compreso nei seguenti fogli di mappa: 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 19, 20, 21, 22, 23, 24, 25; comune di Petrosino: il territorio compreso nei seguenti fogli di mappa: ex comune di Mazara del Vallo: 54, 73, 74, 75, 76, 90, 91, 92, 93, 94, 112, 113, 114, 115, 133, 134, 150, 151 | national-cadastre, cadastral-municipalities, administrative-units | | comprende la parte del territorio dalla provincia di Trapani ed in particolare i territori comunali di Mazara del Vallo, Marsala, Petrosino e Salemi, individuati dalle sottoelencate particelle catastali: | +| it | Eloro | partial-municipality, cadastral-units, natural-boundary, named-sites | punto del reticolo del nuovo catasto terreni del comune di Noto y = 3390 ed x = 39050; lungo il confine nord del foglio di mappa n. 221; confine amministrativo tra i comuni di Avola e Noto | national-cadastre, roads-rivers-osm, administrative-units | Avola [avola]: yes | Tale zona è così delimitata: partendo dalla foce del Fiume Asinaro si segue il confine amministrativo tra i comuni di Avola e Noto fino al punto del reticolo del nuovo catasto terreni del comune di Noto y = 3390 ed x = 3 | +| it | Esino ⚠ | municipality-list, partial-municipality, named-sites, natural-boundary | l'intero territorio amministrativo della provincia di Ancona; provinciale Esanatoglia-Fabriano; fosso di Collamato in località Fonticelle | roads-rivers-osm, dem-altitude, administrative-units | Verdicchio dei Castelli di Jesi [verdicchio-dei-castelli-di-jesi]: yes | comprende l'intero territorio amministrativo della provincia di Ancona ed il seguente territorio amministrativo compreso nella provincia di Macerata che delimita le zone di produzione del Verdicchio di Matelica e del Ver | +| it | Gioia del Colle | municipality-list, partial-municipality | comprende in provincia di Bari tutto il territorio dei comuni di: Acquaviva delle Fonti, Adelfia, Casamassima, Cassano Murge, Castellana Grotte, Conversano, Gioia del Colle, Grumo Appula, Noci, Putignano, Rutigliano, Sammichele di Bari, Sannicandro di Bari, Santeramo in Colle, Turi; quello del comune di Altamura con esclusione nell’interno di esso del territorio appartenente alla zona di produzione del vino «Gravina»; di cui all’articolo 3 del disciplinare pubblicato nella Gazzetta Ufficiale n. 10 del 12 gennaio 1983 | administrative-units | | quello del comune di Altamura con esclusione nell’interno di esso del territorio appartenente alla zona di produzione del vino «Gravina» di cui all’articolo 3 del disciplinare pubblicato nella Gazzetta Ufficiale n. 10 de | +| it | Menfi | cadastral-units, partial-municipality, named-sites, natural-boundary | tutto il territorio amministrativo del Comune di Menfi (Ag) identificato catastalmente con i fogli di mappa dal numero 1 al numero 99; parte del territorio amministrativo del Comune di Sciacca (Ag) identificato catastalmente con i fogli di mappa numero 1,2,3,4,5,6,7,10,11,13,14,15,16 e 23; ad Est il confine è rappresentato dai limiti orientali dei fogli di mappa catastali numero 23, 16, 15, 11, 6, 7 e 4 | national-cadastre, cadastral-municipalities, roads-rivers-osm, administrative-units | Sciacca: yes | -parte del territorio amministrativo del Comune di Sciacca (Ag) identificato catastalmente con i fogli di mappa numero 1,2,3,4,5,6,7,10,11,13,14,15,16 e 23 | +| it | Recioto della Valpolicella | partial-municipality, cadastral-units, named-sites, altitude-limit, natural-boundary | segue il confine S-E del foglio 4° Comune di Sant’Ambrogio di Valpolicella; segue il limite SUD del foglio XIII° del Comune di Negrar sez. C; segue la linea di quota 500 lungo le pendici montuose della vallata Valpantena | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | Soave [soave]: yes | Da questo punto la delimitazione nord della zona del "Valpolicella" segue la linea di quota 500 lungo le pendici montuose della vallata Valpantena, partendo da località Sasso, in Comune di Negrar. | +| it | Recioto di Soave ⚠ | partial-municipality, cadastral-units, named-sites, natural-boundary, map-annex | incontra il confine del foglio catastale 13 del comune di Monte Forte d’Alpone che segue fino a circa 110 metri in via Cervia; comprende in tutto o in parte il territorio dei comuni di Soave, Monteforte d'Alpone, San Martino Buon Albergo, Mezzane di Sotto, Roncà, Montecchia di Crosara, San Giovanni Ilarione, Cazzano di Tramigna, Colognola ai Colli, Caldiero, Illasi e Lavagno in provincia di Verona; che comprende in parte il territorio dei comuni di Soave e Monteforte d’Alpone ed è così delimitata | national-cadastre, roads-rivers-osm, dem-altitude, annexed-map-only, administrative-units | | incontra il confine del foglio catastale 13 del comune di Monte Forte d’Alpone che segue fino a circa 110 metri in via Cervia. | +| it | Soave Superiore | partial-municipality, cadastral-units, natural-boundary, map-annex | comuni di Soave, Monteforte d'Alpone, San Martino Buon Albergo, Mezzane di Sotto, Roncà, Montecchia di Crosara, San Giovanni Ilarione, Cazzano di Tramigna, Colognola ai Colli, Caldiero, Illasi e Lavagno in provincia di Verona; incontra il confine del foglio catastale 13 del comune di Monte Forte d’Alpone che segue fino a circa 110 metri in via Cervia; fino ad incrociare il Progno di Mezzane a quota mt. 92 | roads-rivers-osm, national-cadastre, dem-altitude, administrative-units, annexed-map-only | | incontra il confine del foglio catastale 13 del comune di Monte Forte d’Alpone che segue fino a circa 110 metri in via Cervia. | +| it | Teroldego Rotaliano | cadastral-units, partial-municipality, named-sites, natural-boundary | la porzione del Campo Rotaliano, ricadente nei comuni di Mezzolombardo, Mezzocorona e nella frazione di Grumo del comune di S. Michele all’Adige; lungo il limite sud delle p.f. 232-231-221/2, in comune catastale di Grumo; le susseguenti p.f. 993, 1002, 1046, 1059 in comune catastale di Mezzolombardo | national-cadastre, cadastral-municipalities, roads-rivers-osm, administrative-units | | devia verso ovest, lungo il limite sud delle p.f. 232-231-221/2, in comune catastale di Grumo, e le susseguenti p.f. 993, 1002, 1046, 1059 in comune catastale di Mezzolombardo. | +| it | Trentino | municipality-list, cadastral-units, partial-municipality | Altavalle limitatamente ai comuni catastali di Faver, Grumes e Valda; Altopiano della Vigolana limitatamente ai comuni catastali di Bosentino e Vigolo Vattaro; Borgo Chiese limitatamente ai comuni catastali di Cimego I, Cimego II e Condino | cadastral-municipalities, national-cadastre, administrative-units | | Altavalle limitatamente ai comuni catastali di Faver, Grumes e Valda, Altopiano della Vigolana limitatamente ai comuni catastali di Bosentino e Vigolo Vattaro | +| it | Valpolicella | municipality-list, partial-municipality, cadastral-units, altitude-limit, natural-boundary, named-sites | segue il confine S-E del foglio 4° Comune di Sant’Ambrogio di Valpolicella; segue il limite SUD del foglio XIII° del Comune di Negrar sez. C; passando attraverso il bosco a quota 410 mt | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | Soave [soave]: yes | segue quindi il confine nord del Comune di Cazzano fino ad incrociare il punto di confine tra i 3 Comuni di Tregnago, Cazzano di Tramigna e S.Giovanni Ilarione (dove incontra il confine della zona del Soave). | +| it | Valpolicella Ripasso | municipality-list, partial-municipality, cadastral-units, altitude-limit, natural-boundary, named-sites | segue il confine S-E del foglio 4° Comune di Sant’Ambrogio di Valpolicella; segue il limite SUD del foglio XIII° del Comune di Negrar sez. C; passando attraverso il bosco a quota 410 mt | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | Soave [soave]: yes | Con questa strada discende fino a Proale (quota 449) e poi, sinuoso al largo di Mazzano, segue il limite SUD del foglio XIII° del Comune di Negrar sez. C e lo segue fino a Via Prael, dove tocca Casa Prael (casa di quota | +| it | Valtellina Superiore | partial-municipality, cadastral-units, altitude-limit, named-sites, natural-boundary | prosegue in direzione nord secondo il limite del foglio catastale n. 80; per il tratto fino alla curva altimetrica m 550 s.l.m.; per la curva di livello di 650 metri passando per le località Ca' Farina e Ca' Paini in comune di Montagna | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | | In corrispondenza del tornante a sinistra prima della località Quigna Superiore, prosegue in direzione nord secondo il limite del foglio catastale n. 80 sino ad intersecare la strada nuova detta del Bim. | +| it | Vigneti della Serenissima | cadastral-units, altitude-limit, partial-municipality, named-sites, natural-boundary | la curva di livello 700 m, che rappresenta il limite nord della stessa delimitazione; strada SP 9 di Costabella; il torrente Giara | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | | la delimitazione si sposta sul torrente Giara che segue fino al foglio 024100_B_0005 (Schio/Magrè sez B Foglio 0005). Segue il confine del foglio catastale 024100_B_0001 (Schio/Magrè sez B Foglio 0001) fino ad incontrare | +| pt | Portalegre | cadastral-units, altitude-limit, partial-municipality, natural-boundary | o limite sul da região é definido pela linha do caminho-de-ferro Elvas-Torres das Vargens, até junto da ribeira de Seda; Como limite oeste, segue pela ribeira de Seda até à confluência da ribeira das Perdigoas, próximo do Pisão; Segue pela ribeira das Perdigoas, continuando em linha reta até ao limite norte do concelho de Portalegre | national-cadastre, dem-altitude, roads-rivers-osm, administrative-units | | No do concelho de Sousel, estão delimitados na freguesia de Casa Branca os prédios rústicos nºs 15, 16, 21 e 22 da secção N. São excluídas as áreas de altitude superior a 700 m; | +| pt | Távora-Varosa ⚠ | partial-municipality, municipality-list | a parte da freguesia de Várzea de Abrunhais que não pertence à Região Demarcada do Douro; Do município de Armamar, as freguesias de Cimbres, Goujoim, da União de Freguesias de Aricera e Goujoim, Queimada, Queimadela, Santa Cruz, São Cosmado, União das Freguesias de São Romão Santiago e Tões da freguesia de Armamar; Do município de Lamego, as freguesias de Britiande, Cepões da União de Freguesias de Cepões, Meijinhos e Melcões, Ferreirim, Lalim, Vila Nova de Souto de El -Rei | administrative-units, annexed-map-only | douro: yes; duriense: yes; porto: yes; porto-baixo-corgo: yes; porto-cima-corgo: yes; douro-cima-corgo: yes | Do município de Lamego, as freguesias de Britiande, Cepões da União de Freguesias de Cepões, Meijinhos e Melcões, Ferreirim, Lalim, Vila Nova de Souto de El -Rei e a parte da freguesia de Várzea de Abrunhais que não pert | +| pt | Terras de Cister | partial-municipality, municipality-list | Armamar: a parte da freguesia de Aldeias que não pertence à Região Demarcada do Douro; Lamego: a parte da freguesia de Várzea de Abrunhais que não pertence à Região Demarcada do Douro; Tabuaço: a parte da freguesia de Sendim que não pertence à Região Demarcada do Douro | administrative-units | Douro [douro]: yes; Duriense [duriense]: yes; Porto [porto]: yes | •Tabuaço: freguesias de Arcos, Chavães, Granja do Tedo, Longa, a freguesia de Paradela da União das Freguesias de Paradela e Granjinha, a União das Freguesias de Pinheiros e Vale de Figueira e a parte da freguesia de Sen | +| si | Belokranjec ⚠ | cadastral-units, altitude-limit, natural-boundary, named-sites | po plastnici 500 m nad naselji Ravnace, Drage in Dole; pod naseljem Mačkovec pri Suhorju seka cesto Novo mesto-Metlika; do severne meje katastrske občine Talčji vrh | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | | Meja vinorodnega okoliša Bela krajina poteka od državne meje s Hrvaško na Gorjancih po plastnici 500 m nad naselji Ravnace, Drage in Dole, pod naseljem Mačkovec pri Suhorju seka cesto Novo mesto-Metlika in poteka po plas | +| si | bizeljcan ⚠ | cadastral-units, natural-boundary | od naselja Breg po reki Savi do državne meje s Hrvaško; po državni meji s Hrvaško; po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi | cadastral-municipalities, roads-rivers-osm, administrative-units | stajerska-slovenija: yes; podravje: yes | Meja vinorodnega okoliša Bizeljsko – Sremič poteka od naselja Breg po reki Savi do državne meje s Hrvaško, po državni meji s Hrvaško, po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi, po južnih me | +| si | bizeljsko-sremic ⚠ | cadastral-units, natural-boundary | od naselja Breg po reki Savi do državne meje s Hrvaško; po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi; po južnih mejah katastrskih občin Marijina vas in Paneče do naselja Razbor | cadastral-municipalities, roads-rivers-osm, administrative-units | stajerska-slovenija: yes; podravje: yes | Meja vinorodnega okoliša Bizeljsko – Sremič poteka od naselja Breg po reki Savi do državne meje s Hrvaško, po državni meji s Hrvaško, po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi, po južnih me | +| si | Cviček ⚠ | altitude-limit, cadastral-units, natural-boundary, named-sites | na vinogradniških legah, ki ležijo nad 210 metri nadmorske višine; po južni meji katastrske občine Gotna vas; po zahodni in severni meji katastrske občine Bršljin proti vzhodu | dem-altitude, cadastral-municipalities, roads-rivers-osm, administrative-units | dolenjska: yes | Cviček se prideluje le na območju vinorodnega okoliša Dolenjska, na vinogradniških legah, ki ležijo nad 210 metri nadmorske višine. | +| si | dolenjska ⚠ | cadastral-units, altitude-limit, natural-boundary, named-sites | po južni meji katastrske občine Gotna vas; po zahodni in severni meji katastrske občine Bršljin proti vzhodu; ob reki Savi do južne meje katastrske občine Podkraj in po njej proti zahodu | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | | po severnih mejah katastrskih občin Stare Žage in Pribišje do plastnice 450 m nad naseljem Gornje Laze, po plastnici 450 m ob pobočju Gorjancev do državne meje s Hrvaško (pri naselju Vrbje nad Kostanjevico) | +| si | kras ⚠ | cadastral-units, altitude-limit, natural-boundary | po južni meji katastrske občine Štorje do plastnice 500 m; proti severu preko Struge na plastnico 400 m na cesti Senožeče-Sežana; poteka po plastnici 400 m do ceste Majcni-Griže pod vzpetino Brdo | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | | po južni meji katastrske občine Štorje do plastnice 500 m, proti severu preko Struge na plastnico 400 m na cesti Senožeče-Sežana in poteka po plastnici 400 m do ceste Majcni-Griže pod vzpetino Brdo | +| si | Metliška črnina ⚠ | cadastral-units, altitude-limit, natural-boundary, named-sites | po plastnici 500 m nad naselji Ravnace, Drage in Dole; seka cesto Novo mesto-Metlika; do severne meje katastrske občine Talčji vrh | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | | gre po pobočju Kočke rebri nad naseljem Kot pri Semiču, kjer preide na plastnico 400 m in poteka po njej do severne meje katastrske občine Talčji vrh, po severni meji katastrske občine Talčji Vrh obkroži Vinsko goro | +| si | podravje ⚠ | cadastral-units, altitude-limit, natural-boundary | po obronkih Pohorja po plastnici 600 m, nad kraji Ruše, Log, Zgornje Radvanje, Razvanje, Pivola; po plastnici 600 m po južnem pobočju Paškega Kozjaka proti zahodu do reke Pake; po zahodnih mejah katastrskih občin Letuš in Podvrh | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | | Zunanja meja vinorodnega okoliša Slovenska Štajerska poteka od kraja Ruše (nad Mariborom) do ceste Vitanje-Stranice po obronkih Pohorja po plastnici 600 m, nad kraji Ruše, Log, Zgornje Radvanje, Razvanje, Pivola, Slivniš | +| si | posavje ⚠ | cadastral-units, altitude-limit, natural-boundary, named-sites | po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi; po južnih mejah katastrskih občin Marijina vas in Paneče; po južni meji katastrske občine Gotna vas | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | stajerska-slovenija: yes | Meja vinorodnega okoliša Bizeljsko – Sremič poteka od naselja Breg po reki Savi do državne meje s Hrvaško, po državni meji s Hrvaško, po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi, po južnih me | +| si | prekmurje ⚠ | cadastral-units, natural-boundary | do južne meje katastrske občine Žitkovci; do zahodne meje katastrske občine Tešanovci; po meji katastrske občine Tešanovci do naselja Brezje | cadastral-municipalities, roads-rivers-osm, administrative-units | | Meja prvega območja prekmurskega vinorodnega okoliša poteka od naselja Korovci po državni meji z Avstrijo in Madžarsko do južne meje katastrske občine Žitkovci in po njej do ceste Kamovci-Dobrovnik, po tej cesti do nasel | +| si | primorska ⚠ | altitude-limit, natural-boundary, cadastral-units | po južni meji katastrske občine Štorje do plastnice 500 m; poteka proti jugu po plastnici 300 m do državne meje z Italijo nad sabotinsko cesto; po plastnici 400 m nad zaselkom Podgrič | dem-altitude, roads-rivers-osm, cadastral-municipalities, administrative-units | | po južni meji katastrske občine Štorje do plastnice 500 m, proti severu preko Struge na plastnico 400 m na cesti Senožeče-Sežana in poteka po plastnici 400 m do ceste Majcni-Griže pod vzpetino Brdo | +| si | stajerska-slovenija | partial-municipality, cadastral-units, altitude-limit, natural-boundary | po obronkih Pohorja po plastnici 600 m; po plastnici 600 m po južnem pobočju Paškega Kozjaka; do zahodne meje katastrske občine Letuš, po zahodnih mejah katastrskih občin Letuš in Podvrh | cadastral-municipalities, dem-altitude, roads-rivers-osm, administrative-units | | Zunanja meja vinorodnega okoliša Slovenska Štajerska poteka od kraja Ruše (nad Mariborom) do ceste Vitanje-Stranice po obronkih Pohorja po plastnici 600 m, nad kraji Ruše, Log, Zgornje Radvanje, Razvanje, Pivola, Slivniš | +| sk | juznoslovenska ⚠ | municipality-list, cadastral-units | ohraničená hranicami katastrálnych území obcí; Bratislava-Čunovo; Bratislava-Jarovce | cadastral-municipalities, national-cadastre, administrative-units | | Južnoslovenská vinohradnícka oblasť je vinohradnícka oblasť, ktorá je ohraničená hranicami katastrálnych území obcí: Abrahám, Andovce, Bajč, Bajtava, Baloň, Bátorove Kosihy, Belá, Bíňa, Blahová, Branovo, Bratislava-Čunov | + +## Overlap pairs whose documents address the shared border + +Pairs (same country, not parent/child) where at least one side's delimitation text explicitly names the other appellation or splits the municipality they share. + +| country | appellation | partner | overlap km² | class | how | +|---|---|---|---:|---|---| +| cy | krasochoria-lemesou-afamis | krasochoria-lemesou-laona | 13.85 | PARTIAL | Not named explicitly, but the text splits the community they share (Όμοδος) at the Χα-Ποτάμι river, claiming only the eastern bank; Laona's text claims the western bank of the same river in the same c | +| cy | krasochoria-lemesou-laona | krasochoria-lemesou-afamis | 13.85 | PARTIAL | Not named explicitly, but the shared community Όμοδος is split at the Χα-Ποτάμι river and only the western side is claimed, exactly the complement of Afamis' eastern side; the recorded 13.85 km² overl | +| es | 3 Riberas | Rioja | 4.35 | SLIVER | Explicit exclusion clause: land within Navarre already covered by DOCa Rioja is removed from the zone, but the exclusion is stated only in the abstract (no municipality split, no cadastral or geograph | +| es | Valencia | Alicante [alicante] | | | Villena (an Alicante DO municipality) is admitted only for registered parcels of La Viña Coop V members, i.e. the shared municipality is split — but DO Alicante is never named. | +| es | Valencia | Alto Vinalopó [alicante-alto-vinalopo] | | | Same Villena clause: Villena belongs to the Alto Vinalopó subzone and is admitted only through named cooperative members' registered parcels. | +| es | Clariano | Almansa [almansa] | | | The shared municipalities Almansa and Caudete are split: only the 24 + 3 named parajes, and only Registro Vitícola parcels traditionally assigned to DOP Valencia, are included — though DO Almansa is n | +| es | Valle del Miño-Ourense | Ribeira Sacra [ribeira-sacra] | | | Shared municipalities are split rather than taken whole: Nogueira de Ramuín only Moura and San Miguel do Campo, A Peroxa only Gueral, A Peroxa and Vilarrubín, Coles and O Pereiro de Aguiar only by lis | +| es | Valle del Miño-Ourense | Ribeiras do Sil [ribeira-sacra-ribeiras-do-sil] | | | Same parish-level split of Nogueira de Ramuín, Coles, A Peroxa and O Pereiro de Aguiar, which is where this subzone is touched; the subzone itself is not named. | +| gr | playies-abelou | ΠΟΠ Σάμος (named in the text, not listed as an overlap partner in the record header) | | | The PGI zone is defined as those seven communities minus the delimited PDO Samos zones ('εκτός των καθορισμένων ζωνών παραγωγής οίνων ΠΟΠ.ΣΑΜΟΣ'), i.e. an explicit within-community split against a nam | +| it | Castel del Monte | Moscato di Trani | 1538.02 | PARTIAL | The partner DOC is not named, but the text splits comuni it shares (Trani, Andria, Corato, Ruvo, Canosa di Puglia are listed as 'in parte') and gives a usable closed line through them along the SP 231 | +| it | Cilento | Paestum | 1408.46 | PARTIAL | Not by name, but the perimeter explicitly splits Capaccio — the municipality both cover — running along the provinciale per Capaccio and then up the Capaccio/Trentinara communal boundary, so the in-mu | +| it | Castel del Monte Bombino Nero | Moscato di Trani | 1388.22 | PARTIAL | Partner not named, but Trani, Andria and Corato are declared 'in parte' and the text supplies the internal line (SP 231, via vecchia Corato-Terlizzi, Terlizzi bypass, SS 96) that cuts them. | +| it | Castel del Monte Nero di Troia Riserva | Moscato di Trani | 1388.22 | PARTIAL | Partner not named; Trani, Andria and Corato are 'in parte' and the internal cut is given by the SP 231 / via vecchia Corato-Terlizzi / SS 96 trace. | +| it | Castel del Monte Rosso Riserva | Moscato di Trani | 1388.22 | PARTIAL | Partner not named; Trani, Andria and Corato are included only 'in parte' and the text supplies the internal boundary line through them. | +| it | Castel del Monte | Murgia | 904.99 | PARTIAL | Not named as an appellation; the southern limit is drawn inside the shared upland comuni along the southern municipal borders of Toritto, Bitonto and Ruvo via Murgia Lama Rosa (q.485) and Il Feltro (q | +| it | Castel del Monte Bombino Nero | Murgia | 756.35 | PARTIAL | Not named; the shared upland is cut by the southern municipal borders of Toritto, Bitonto and Ruvo via Murgia Lama Rosa (q.485) and Il Feltro (q.631). | +| it | Castel del Monte Nero di Troia Riserva | Murgia | 756.35 | PARTIAL | Not named; the shared Murgia upland is cut along the southern municipal borders of Toritto, Bitonto and Ruvo via Murgia Lama Rosa (q.485) and Il Feltro (q.631). | +| it | Castel del Monte Rosso Riserva | Murgia | 756.35 | PARTIAL | Not named; the shared upland is bounded by the southern municipal borders of Toritto, Bitonto, Ruvo and Andria via Murgia Lama Rosa (q.485) and Il Feltro (q.631). | +| it | Castelli Romani | Roma | 672.2 | PARTIAL | The comune of Roma is included only 'in parte' and the line explicitly cuts it: it starts at via Casilina x G.R.A., follows the ring road south-west to via Laurentina and returns along via Casilina at | +| it | Castel del Monte | Barletta | 502.08 | PARTIAL | The trace explicitly follows the Canosa/Barletta municipal boundary ('fino ad arrivare ai confini comunali di Canosa e Barletta'), excluding the comune of Barletta; the name matches the partner DOC, t | +| it | Marsala | Alcamo | 328.11 | PARTIAL | The comune of Alcamo is explicitly carved out of the Trapani province zone by name ('esclusi i comuni di Pantelleria, Favignana ed Alcamo'), which is exactly the comune shared with DOC Alcamo. | +| it | Lambrusco di Sorbara | Lambrusco Grasparossa di Castelvetro | 313.34 | PARTIAL | Both DOCs take only PART of the comune di Modena and the southern stretch of the trace is word-for-word identical in the two disciplinari (Marzaglia - ferrovia - autostrada del Sole - torrente Cerca - | +| it | Lambrusco Grasparossa di Castelvetro | Lambrusco di Sorbara | 313.34 | PARTIAL | Both DOCs include only PART of the comune di Modena and the northern stretch of this trace is verbatim identical to Sorbara's (Marzaglia - ferrovia - autostrada del Sole - torrente Cerca - Vaciglio - | +| it | Martina | Ostuni | 222.67 | PARTIAL | Only 'parte del territorio comunale di ... Ostuni' is included, and the line is traced along the Ceglie-Ostuni municipal boundary to km 6,000 and the Ostuni-Cisternino road to km 8,000 — i.e. the comu | +| it | Menfi | Sciacca | 191.01 | PARTIAL | The comune shared with DOC Sciacca is split cadastrally: only fogli 1-7, 10, 11, 13-16 and 23 of Sciacca are included, with their western/southern/eastern/northern limits described foglio by foglio. | +| it | Collio Goriziano | friuli-isonzo | 103.31 | PARTIAL | Not named, but the whole delimitation is a within-municipality split of exactly the communes shared with Friuli Isonzo (Gorizia, Cormons, Farra d'Isonzo, Gradisca area): the hill zone is cut off from | +| it | Brachetto d'Acqui | nizza | 74.67 | PARTIAL | Not by naming the DOCG, but it splits the shared comune of Nizza Monferrato, admitting only the right bank of the torrente Belbo — a mappable line inside a municipality both appellations claim. | +| it | Castelli Romani | Montecompatri Colonna | 71.19 | PARTIAL | Colonna is included whole while Montecompatri is included only 'in parte'; the line runs along the borders of Lariano, Rocca di Papa and Rocca Priora to Colle di Fuori and then the strada Valle dei Go | +| it | Matino | Alezio | 65.38 | PARTIAL | Only part of comune Alezio is taken in and the line 'costeggia, escludendolo, l’abitato di Alezio' — the municipality shared with DOC Alezio is split by an explicit traced line. | +| it | Castelli Romani | Aprilia | 60.28 | PARTIAL | Aprilia is 'in parte': the line follows the Roma-Latina provincial border to ponte Guardapassi in comune di Aprilia, then the fosso Leschione and fosso di Carano, giving an explicit cut of the shared | +| it | Cirò | melissa | 50.97 | PARTIAL | The text explicitly includes only PART of the comune di Melissa (which Melissa DOC also covers) and draws the internal line in detail — up the torrente Perticara, across Valle di Casa to the Cirò Mari | +| it | Melissa | Cirò | 50.97 | PARTIAL | The northern limit follows the boundary of comune Cirò: 'fino ad incrociare sul torrente Lipuda, quello del comune di Cirò e lungo questi prosegue verso nord – ovest' — the shared comune line with the | +| it | Genazzano | Cesanese di Olevano Romano | 37.97 | PARTIAL | The shared comune of Olevano Romano is included only 'in parte' and the trace runs along Genazzano's comune boundary and the fiume Sacco inside it, though the Cesanese DOC itself is never named. | +| it | Circeo | Terracina | 20.15 | SLIVER | Only implicitly: the DOC Terracina is not named, but the text states that just part of the comune di Terracina is included and the perimeter (canale Sisto, Migliare 56-58, strada Mediana) draws that i | +| it | Sebino | Cellatica | 17.32 | PARTIAL | Cellatica is named twice: the provincial road to Cellatica is the eastern limit of the Brescia portion, and the whole comune of Cellatica is explicitly excluded from the Sebino delimitation. | +| it | Amarone della Valpolicella | soave | 16.07 | PARTIAL | The line reaches the tri-point of Tregnago, Cazzano di Tramigna and S. Giovanni Ilarione and the text states this is 'dove incontra il confine della zona del Soave', i.e. it explicitly anchors its own | +| it | Lago di Corbara | Orvieto | 7.81 | PARTIAL | The text explicitly splits the shared comune di Orvieto, admitting only the frazioni Corbara, Fossatello, Colonnetta di Prodo, Prodo and Titignano and tracing the resulting line road by road; it names | +| it | Aprilia | nettuno | 4.24 | SLIVER | The text takes only 'parte del territorio comunale di Nettuno' and draws the exact line (railway, provinciale Nettuno-Cisterna, fiume Astura, Tre Cancelli) that splits the comune the two appellations | +| it | Ortrugo dei Colli Piacentini | Provincia di Pavia | 1.3 | SLIVER | Not named as an IGT, but the text explicitly removes the only territory the two could share: 'Caminata (escluso le isole amministrative in provincia di Pavia)', and elsewhere follows the provincial bo | +| it | Alba | Barbera d'Alba [barbera-d-alba] | | | Not named, but the text splits the comuni they share (Alba, Bra, Cherasco, Neive, Barbaresco, Novello ...) with a metre-level trace, so the shared ground is resolved below comune level. | +| it | Alba | Alta Langa [alta-langa] | | | Not named, but both texts cut the comune of Alba on the Tanaro's orographic bank ('la destra orografica del Tanaro'), which is the same dividing line Alta Langa uses for Alba. | +| it | Alba | Dolcetto d'Alba [dolcetto-d-alba] | | | Not named; the same partial-comune trace (Alba, Barbaresco, Neive, Novello, Cherasco) sets the sub-municipal limit in the shared area. | +| it | Alcamo | Valle Belice [valle-belice] | | | Not named, but Alcamo takes only part of Gibellina, Calatafimi and Castellammare del Golfo, the comuni shared with the Valle Belice IGT, and fixes that part with a river/road trace. | +| it | Alcamo | Monreale [monreale] | | | Not named as an appellation, but the comuni that form the Monreale DOC core (Monreale, Camporeale, San Cipirello, San Giuseppe Jato, Partinico) are explicitly taken 'in parte' and bounded by the Jato/ | +| it | Alcamo | Marsala [marsala] | | | Not named; the Trapani-province comuni shared with Marsala (Calatafimi, Castellammare del Golfo, Balestrate, Gibellina) are included only in part along the narrative line. | +| it | Alghero | Romangia [romangia] | | | Romangia is not named, but the comune both zones share (Sassari) is explicitly split, the Alghero share being the south-western part bounded north by the Argentiera / SS 291 / SS 131 / SS 127-bis line | +| it | Cannellino di Frascati | Montecompatri Colonna [montecompatri-colonna] | | | The text takes Montecompatri only 'in parte' and runs the line along the Roma/Montecompatri, Roma/Colonna, Colonna/Frascati and Montecompatri/Grottaferrata communal borders — i.e. it explicitly splits | +| it | Cerveteri | Roma [roma] | | | The comune of Roma, which the two appellations share, is explicitly split: only 'parte dei territori dei comuni di Roma' is included and the perimeter is traced metre by metre, but the DOC Roma itself | +| it | Cesanese di Olevano Romano | Genazzano [genazzano] | | | The shared comune of Genazzano is named and split: only 'parte di quello di Genazzano' is included, and the internal line is traced in detail (fiume Sacco, Valle Fonte Noce, colle Salomone, case Birba | +| it | Colli della Toscana centrale | Chianti [chianti] | | | A whole segment of the perimeter is defined as coincident with the Chianti production zone: 'da questo punto si identifica con la zona di produzione del vino Chianti fino ad incontrare, in località Ca | +| it | Colli Martani | Todi [todi] | | | The comune of Todi is explicitly taken only in part ('parte del territorio dei comuni di Todi...'), and the perimeter runs through Todi territory (provinciale Todi-Montenero, Todi-Avigliano, Asproli), | +| it | Colli Martani | Spoleto [spoleto] | | | Spoleto is listed as included only in part and the line is traced through it (torrente Tessino, provinciale Spoleto-Acquasparta, zona di Baiano di Spoleto); the Spoleto DOC is not named as such. | +| it | Colli Martani | Cannara [cannara] | | | Cannara appears in the 'parte del territorio' list, so the shared comune is split by the narrative line; the Cannara DOC is not named. | +| it | Colli Martani | Bettona [bettona] | | | Bettona is included only in part and the perimeter explicitly follows 'il confine tra i comuni di Torgiano e Bettona'; the Bettona DOC is not named. | +| it | Copertino | Galatina [galatina] | | | The comune of Galatina, which the two appellations share, is explicitly included only in part, and the cut inside it is traced: the limit starts on the SS 101 at quota 50 near Collemeto (Galatina fraz | +| it | Curtefranca | Ronchi di Brescia [ronchi-di-brescia] | | | The shared comune di Brescia is explicitly only partly included ('parte del territorio del comune di Brescia') and the narrative traces that part via SS 11, Mandolossa, the S. Anna hill and the Bresci | +| it | Custoza | Garda [garda] | | | The shared lakeside comuni (Peschiera del Garda, Lazise, Castelnuovo del Garda, Pastrengo, Bussolengo, Sona) are explicitly only partly included and the internal line is traced, but the Garda DOC is n | +| it | Custoza | Bardolino Superiore [bardolino-superiore] | | | Same narrative splits Lazise, Castelnuovo del Garda, Peschiera and Pastrengo, which are shared with Bardolino; the appellation itself is not named. | +| it | Custoza | Bardolino [bardolino] | | | The perimeter follows the Lazise-Castelnuovo and Lazise-Pastrengo communal boundaries and the Garda shore, splitting the shared comuni, without naming Bardolino. | +| it | Custoza | bardolino-la-rocca [bardolino-la-rocca] | | | Covered by the same split of the shared comuni; the sub-zone is not named. | +| it | Custoza | bardolino-montebaldo [bardolino-montebaldo] | | | Covered by the same split of the shared comuni; the sub-zone is not named. | +| it | Custoza | bardolino-sommacampagna [bardolino-sommacampagna] | | | Sommacampagna is the start/end point of the perimeter and only partly included, but the Bardolino sub-zone is not named. | +| it | Dolcetto d'Asti | Nizza [nizza] | | | The shared comune di Nizza Monferrato is explicitly split: only the part on the right bank of the torrente Belbo belongs to Dolcetto d'Asti, so the Belbo is the operative line between the two zones (t | +| it | Dolcetto d'Asti | Canelli [canelli] | | | The shared comune di Canelli is likewise split along the destra orografica of the torrente Belbo, giving a concrete within-municipality boundary against the Canelli zone. | +| it | Eloro | Avola [avola] | | | The perimeter starts at the mouth of the Fiume Asinaro and runs along the Avola–Noto municipal boundary, so the comune of Avola is explicitly used as the northern limit (named as comune, not as appell | +| it | Esino | Verdicchio dei Castelli di Jesi [verdicchio-dei-castelli-di-jesi] | | | The Macerata-province portion is defined explicitly as the territory "che delimita le zone di produzione del Verdicchio di Matelica e del Verdicchio dei Castelli di Jesi", so the partner appellation's | +| it | Frascati | Montecompatri Colonna [montecompatri-colonna] | | | The shared municipality is split: only 'in parte' Montecompatri is included, and the line is traced along the Roma/Montecompatri, Roma/Colonna and Colonna/Frascati borders and the Colonna administrati | +| it | Frascati Superiore | Montecompatri Colonna [montecompatri-colonna] | | | Montecompatri is taken in only 'in parte' and the trace runs along the Roma/Montecompatri and Colonna/Frascati municipal borders and the Colonna exclave, so the shared municipality is split, though th | +| it | Friuli Aquileia | Friuli Grave [friuli-grave] | | | Not named, but the text takes Santa Maria La Longa, Palmanova, Trivignano Udinese, Chiopris-Viscone and Gonars only 'in parte' — exactly the comuni Friuli Grave also splits — and its northern limit ru | +| it | Friuli Colli Orientali | Friuli Grave [friuli-grave] | | | Grave is not named, but the whole western/southern leg (Madonna–Tarcento station–railway–Tricesimo-Nimis–ponte di Nimis sul Torre–Savorgnano–power line–Rubignacco–Borgo Viola–SS.356–Azzano–Natisone–Ma | +| it | Friuli Grave | Friuli Colli Orientali [friuli-colli-orientali] | | | Not named, but Grave takes Cividale del Friuli, Manzano, Premariacco, Corno di Rosazzo, Faedis, Tarcento, Bùttrio and Moimacco only 'in parte' and runs the identical Madonna–Tarcento–Rubignacco–Civida | +| it | Friuli Isonzo | Collio Goriziano [collio-goriziano] | | | Collio is not named as a denomination, but the shared comuni (Cormons, Capriva del Friuli, Mossa, San Lorenzo Isontino, Gorizia, Farra d'Isonzo) are explicitly taken only 'in parte', and through Cormo | +| it | Friuli Isonzo | Carso [carso] | | | Carso is not named, but Monfalcone, Sagrado, Savogna d'Isonzo, Ronchi dei Legionari and Staranzano are taken only 'in parte' and the eastern limit climbs the karst edge (colle La Rocca q.88, M. Cosich | +| it | Friuli Latisana | Friuli Annia [friuli-annia] | | | Annia is not named, but Muzzana del Turgnano and Castions di Strada — the comuni shared with Annia — are taken only in part, and the limit is drawn through them along SS.353, SS.14 and the San Gervasi | +| it | Friuli Latisana | Friuli Grave [friuli-grave] | | | Grave is not named, but Castions di Strada is split 'in parte' here, and the M.o del Paradiso / SS.353 segment is the mirror of Grave's line at the Castions di Strada–Pocenia boundary, Grave expressly | +| it | Ostuni | Brindisi [brindisi] | | | The text includes only PART of the comune di Brindisi ('in parte il territorio di: Latiano Ceglie Messapico Brindisi') and runs the line along 'il confine comunale di Brindisi' through località Ferriz | +| it | Ostuni | Martina [martina] | | | The line follows the Ostuni municipal boundary with Cisternino, Locorotondo and Martina Franca, then splits Ceglie Messapico (a comune shared with the Martina Franca zone) along the Cisternino–Ceglie | +| it | Recioto della Valpolicella | Soave [soave] | | | The perimeter explicitly keys itself to the Soave zone at the Tregnago / Cazzano di Tramigna / S. Giovanni Ilarione tri-point: "dove incontra il confine della zona del Soave", then runs along the Cazz | +| it | Tarquinia | Roma [roma] | | | The comune of Roma, which the Roma DOC covers, is explicitly included only 'in parte', and the traced line runs along the Grande Raccordo Anulare and the Tiber down to the Canale di Porto, cutting the | +| it | Tarquinia | Colli Etruschi Viterbesi [colli-etruschi-viterbesi] | | | Shared Viterbo-province comuni (Tessennano, Tuscania, Arlena di Castro, Monteromano) are included only in part, with the line following the Canino-Tessennano-Tuscania road and the Viterbo/Vetralla mun | +| it | Tarquinia | Colli Cimini [colli-cimini] | | | Ronciglione and Capranica, in the Colli Cimini area, are included only in part; the line runs from Madonna del Piano along the Capranica-Ronciglione road and the via Cassia Antica to the Sutri boundar | +| it | Valpolicella | Soave [soave] | | | At the Tregnago / Cazzano di Tramigna / S. Giovanni Ilarione tripoint the text says the line meets "il confine della zona del Soave", and it then runs down to the Cazzano–Soave provincial road near qu | +| it | Valpolicella Ripasso | Soave [soave] | | | The line is said to meet "il confine della zona del Soave" at the Tregnago / Cazzano di Tramigna / S. Giovanni Ilarione tripoint, then follows the Cazzano–Soave provincial road. | +| pt | Távora-Varosa | douro | 682.61 | PARTIAL | Explicitly: the Lamego entry takes only 'a parte da freguesia de Várzea de Abrunhais que não pertence à Região Demarcada do Douro', naming the Douro demarcated region as the excluded neighbour and spl | +| pt | Távora-Varosa | duriense | 682.61 | PARTIAL | Covered by the same RDD clause — Duriense is the IG of the Região Demarcada do Douro, so the exclusion of the RDD part of Várzea de Abrunhais is the boundary with it. | +| pt | Távora-Varosa | porto | 682.61 | PARTIAL | Same clause: the RDD (Porto's delimitation) part of Várzea de Abrunhais is carved out; the RDD text conversely claims the Quintas de Foutoura, do Prado and das Várzeas in that freguesia. | +| pt | Távora-Varosa | porto-baixo-corgo | 682.61 | PARTIAL | Várzea de Abrunhais (Lamego) belongs to the Baixo Corgo sub-region via the three named quintas, and Távora-Varosa takes exactly the remainder of that freguesia. | +| pt | Távora-Varosa | porto-cima-corgo | 682.61 | PARTIAL | The RDD exclusion clause applies to the whole demarcated region; additionally São João da Pesqueira is split at freguesia level — Távora-Varosa takes Pereiros and Riodades, Cima Corgo takes Vilarouco | +| pt | Távora-Varosa | douro-cima-corgo | 682.61 | PARTIAL | Same as porto-cima-corgo: the RDD clause plus the complementary freguesia split of São João da Pesqueira (Pereiros/Riodades vs Vilarouco). | +| pt | Encostas d'Aire | obidos | 255.34 | PARTIAL | Not named, but Caldas da Rainha is split at freguesia level: Encostas d'Aire takes Carvalhal Benfeito, Salir de Matos and Santa Catarina, which are exactly the freguesias the Óbidos caderno does NOT t | +| pt | Óbidos | encostas-d-aire | 255.34 | PARTIAL | Not named, but the shared municipality Caldas da Rainha is split at freguesia level and the two lists are disjoint: Óbidos takes A dos Francos, Alvorninha, Landal, São Gregório, Vidais; Encostas d'Air | +| pt | Minho | douro | 123.28 | WIDE | Not named, but Resende is split at freguesia level by excluding Barrô — and Barrô is precisely the Resende freguesia that the Douro/Porto (Baixo Corgo) text claims ('a freguesia de Barrô, do concelho | +| pt | Minho | porto-baixo-corgo | 123.28 | WIDE | Same clause: 'com exceção da freguesia de Barrô' removes exactly the Resende freguesia listed in the Baixo Corgo sub-region. | +| pt | Terras de Cister | Douro [douro] | | | The text explicitly subtracts the Região Demarcada do Douro from three freguesias (Aldeias, Várzea de Abrunhais, Sendim), naming the Douro demarcated region as the dividing boundary. | +| pt | Terras de Cister | Duriense [duriense] | | | Addressed through the same 'Região Demarcada do Douro' exclusion, which is the territory the Duriense IG covers; the IG name itself is not used. | +| pt | Terras de Cister | Porto [porto] | | | Addressed through the same 'Região Demarcada do Douro' exclusion, coextensive with the Porto DO; the name Porto is not used. | +| ro | tarnave | Jidvei [jidvei] | | | Jidvei is named as sub-denumirea JIDVEI of Târnave and given its own delimited commune/village list (Jidvei, Cetatea de Baltă, Şona), so the internal boundary between the two names is defined by enume | +| si | posavje | stajerska-slovenija | 630.1 | PARTIAL | The Bizeljsko-Sremič leg runs explicitly 'po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijine vasi' — i.e. it adopts the southern boundary of the Šmarje-Virštanj sub-district of Štajer | +| si | bizeljcan | stajerska-slovenija | 630.01 | PARTIAL | The northern limit is defined as 'po južni meji Šmarsko-virštanjskega vinorodnega okoliša' — the southern border of the Šmarje-Virštanj sub-district, which is a podokoliš of Štajerska Slovenija, so th | +| si | bizeljcan | podravje | 630.01 | PARTIAL | Same clause: Šmarje-Virštanj sits inside the Štajerska Slovenija okoliš of the Podravje region, so the shared line is defined by reference to that district's southern boundary. | +| si | bizeljsko-sremic | stajerska-slovenija | 630.01 | PARTIAL | The boundary explicitly follows the southern border of the Šmarje-Virštanj wine district, a sub-district of Štajerska Slovenija, so the contact line is defined by reference to the neighbour. | +| si | bizeljsko-sremic | podravje | 630.01 | PARTIAL | Via the same Šmarje-Virštanj clause, Šmarje-Virštanj being inside Štajerska Slovenija / Podravje. | +| si | Cviček | dolenjska | 146.65 | PARTIAL | The text defines Cviček as produced only inside the Dolenjska okoliš and then restricts it further to sites above 210 m, so the relation to Dolenjska is an explicit altitude-based subset of the same p | +| sk | karpatska-perla | malokarpatska | 591.27 | PARTIAL | The text explicitly names 'Malokarpatská vinárska oblasť' and defines its area as the municipalities that form it; all 18 municipalities listed here are a subset of the municipalities in the malokarpa | + +## Medium-priority records (altitude limits, narrative partial municipalities, annexed maps) + + +**Italy** (257): Abruzzo [partial-municipality]; Aglianico del Taburno [partial-municipality/natural-boundary/named-sites]; Aglianico del Vulture [partial-municipality/named-sites]; Aglianico del Vulture Superiore [partial-municipality/named-sites]; Alba [partial-municipality/natural-boundary/named-sites]; Alcamo [partial-municipality/altitude-limit/natural-boundary/named-sites]; Aleatico di Gradoli [partial-municipality/natural-boundary/named-sites]; Alezio [partial-municipality/natural-boundary/named-sites]; Alghero [partial-municipality/natural-boundary]; Alta Langa [partial-municipality/natural-boundary]; Alta Valle della Greve [partial-municipality/natural-boundary/named-sites]; Alto Mincio [partial-municipality/natural-boundary/named-sites]; Ansonica Costa dell'Argentario [partial-municipality]; Aprilia [partial-municipality/natural-boundary]; Arcole [partial-municipality/natural-boundary/named-sites]; Arghillà [partial-municipality/named-sites]; Asolo Prosecco [partial-municipality/altitude-limit/natural-boundary/named-sites]; Assisi [partial-municipality/natural-boundary/named-sites]; Asti [partial-municipality/named-sites]; Atina [partial-municipality/altitude-limit/natural-boundary/named-sites]; Barbaresco [partial-municipality]; Barbera d'Alba [partial-municipality/natural-boundary]; Barbera d'Asti [partial-municipality/natural-boundary]; Barbera del Monferrato [partial-municipality/natural-boundary]; Barbera del Monferrato Superiore [partial-municipality/natural-boundary]; Bardolino [partial-municipality/altitude-limit/natural-boundary]; Bardolino Superiore [partial-municipality/named-sites/altitude-limit/natural-boundary]; Barletta [partial-municipality/natural-boundary]; Barolo [partial-municipality/natural-boundary]; Bianchello del Metauro [partial-municipality/named-sites/natural-boundary]; Bianco Capena [partial-municipality/named-sites/natural-boundary]; Bianco di Pitigliano [partial-municipality/natural-boundary]; Boca [partial-municipality]; Bolgheri [partial-municipality]; Bonarda dell'Oltrepò Pavese [partial-municipality/natural-boundary]; Bosco Eliceo [partial-municipality/natural-boundary/named-sites]; Botticino [partial-municipality/natural-boundary]; Brachetto d'Acqui [partial-municipality/natural-boundary]; Breganze [partial-municipality/natural-boundary/named-sites]; Canelli [partial-municipality/natural-boundary]; Cannellino di Frascati [partial-municipality/natural-boundary/named-sites]; Cannonau di Sardegna [partial-municipality/altitude-limit/natural-boundary/named-sites]; Capalbio [partial-municipality/natural-boundary/named-sites]; Carso [partial-municipality/natural-boundary/named-sites]; Casauria [partial-municipality/map-annex/natural-boundary/named-sites]; Casavecchia di Pontelatone [partial-municipality/named-sites]; Castel del Monte [partial-municipality/natural-boundary/named-sites]; Castel del Monte Bombino Nero [partial-municipality/named-sites]; Castel del Monte Nero di Troia Riserva [partial-municipality/named-sites]; Castel del Monte Rosso Riserva [partial-municipality/named-sites]; Castel San Lorenzo [partial-municipality/natural-boundary/named-sites]; Castelfranco Emilia [partial-municipality/named-sites]; Castelli di Jesi Verdicchio Riserva [partial-municipality/natural-boundary/named-sites]; Castelli Romani [partial-municipality/named-sites/natural-boundary]; Cellatica [partial-municipality/named-sites/natural-boundary]; Cerasuolo di Vittoria [partial-municipality/named-sites/natural-boundary/altitude-limit]; Cerveteri [partial-municipality/natural-boundary/named-sites]; Cesanese del Piglio [partial-municipality/natural-boundary/named-sites]; Cesanese di Affile [partial-municipality/altitude-limit/natural-boundary/named-sites]; Cesanese di Olevano Romano [partial-municipality/natural-boundary/named-sites]; Chianti [partial-municipality/natural-boundary/named-sites]; Chianti Classico [partial-municipality/natural-boundary]; Cilento [partial-municipality/natural-boundary/altitude-limit]; Circeo [partial-municipality/natural-boundary]; Cirò [partial-municipality/natural-boundary]; Colli Albani [partial-municipality/natural-boundary]; Colli Altotiberini [partial-municipality/natural-boundary]; Colli Berici [partial-municipality/natural-boundary]; Colli Bolognesi [partial-municipality/natural-boundary/named-sites]; Colli Bolognesi Pignoletto [partial-municipality/natural-boundary/named-sites]; Colli d'Imola [partial-municipality]; Colli del Trasimeno [partial-municipality/natural-boundary/named-sites]; Colli dell'Etruria Centrale [partial-municipality/natural-boundary/named-sites]; Colli della Sabina [partial-municipality/named-sites/natural-boundary/map-annex]; Colli di Conegliano [partial-municipality/altitude-limit/named-sites/natural-boundary]; Colli di Faenza [partial-municipality/named-sites]; Colli di Luni [partial-municipality/altitude-limit/named-sites/natural-boundary]; Colli di Parma [partial-municipality/natural-boundary]; Colli di Scandiano e di Canossa [partial-municipality/natural-boundary]; Colli Euganei [partial-municipality/natural-boundary/named-sites]; Colli Euganei Fior d'Arancio [partial-municipality/natural-boundary/named-sites]; Colli Lanuvini [partial-municipality/natural-boundary]; Colli Martani [partial-municipality/natural-boundary]; Colli Orientali del Friuli Picolit [partial-municipality/natural-boundary]; Colli Perugini [partial-municipality/natural-boundary]; Colli Pesaresi [partial-municipality/natural-boundary/altitude-limit]; Colli Piacentini [partial-municipality/natural-boundary]; Colli Romagna centrale [partial-municipality]; Colli Tortonesi [partial-municipality/natural-boundary]; Colli Trevigiani [partial-municipality/altitude-limit/natural-boundary]; Collina Torinese [partial-municipality/natural-boundary]; Colline di Levanto [partial-municipality/altitude-limit/natural-boundary]; Colline Saluzzesi [partial-municipality/altitude-limit/named-sites/natural-boundary]; Colline Teramane Montepulciano d’Abruzzo [partial-municipality/natural-boundary]; Conselvano [partial-municipality/natural-boundary]; Contea di Sclafani [partial-municipality/natural-boundary]; Copertino [partial-municipality/named-sites]; Cori [partial-municipality/natural-boundary/altitude-limit]; Cortese dell'Alto Monferrato [partial-municipality/natural-boundary/altitude-limit]; Corti Benedettine del Padovano [partial-municipality/natural-boundary/named-sites]; Cortona [partial-municipality/altitude-limit/named-sites/natural-boundary]; Curtefranca [partial-municipality/natural-boundary/named-sites]; Custoza [partial-municipality/natural-boundary/named-sites]; dell'Emilia [natural-boundary]; Dogliani [partial-municipality/natural-boundary/named-sites]; Dolcetto d'Alba [partial-municipality/natural-boundary]; Dolcetto d'Asti [partial-municipality/natural-boundary]; Erice [partial-municipality/named-sites/altitude-limit/natural-boundary]; Est! Est!! Est!!! di Montefiascone [partial-municipality/named-sites/natural-boundary]; Etna [partial-municipality/altitude-limit/named-sites/natural-boundary]; Falanghina del Sannio [partial-municipality/natural-boundary/named-sites]; Falerio [altitude-limit]; Falerno del Massico [partial-municipality/natural-boundary]; Fortana del Taro [altitude-limit/natural-boundary]; Franciacorta [partial-municipality/natural-boundary/named-sites]; Frascati [partial-municipality/natural-boundary/named-sites]; Frascati Superiore [partial-municipality/natural-boundary/named-sites]; Friuli Aquileia [partial-municipality/natural-boundary]; Friuli Colli Orientali [partial-municipality/natural-boundary/named-sites]; Friuli Grave [partial-municipality/natural-boundary/named-sites]; Friuli Isonzo [partial-municipality/natural-boundary/named-sites]; Friuli Latisana [partial-municipality/natural-boundary]; Gambellara [partial-municipality/natural-boundary/named-sites]; Garda [partial-municipality/natural-boundary/altitude-limit]; Garda Colli Mantovani [partial-municipality/natural-boundary/altitude-limit]; Gavi [partial-municipality/natural-boundary]; Genazzano [partial-municipality/natural-boundary/named-sites]; Ghemme [partial-municipality/natural-boundary]; Grance Senesi [partial-municipality/map-annex]; Gravina [partial-municipality/altitude-limit/named-sites]; Greco di Bianco [partial-municipality/altitude-limit/natural-boundary]; Grignolino del Monferrato Casalese [partial-municipality/natural-boundary]; Gutturnio [partial-municipality/natural-boundary/altitude-limit/named-sites]; Lacrima di Morro [partial-municipality]; Lago di Caldaro [partial-municipality/altitude-limit/named-sites/natural-boundary]; Lago di Corbara [partial-municipality/named-sites/natural-boundary]; Lambrusco di Sorbara [partial-municipality/natural-boundary/named-sites]; Lambrusco Grasparossa di Castelvetro [partial-municipality/natural-boundary/named-sites]; Lambrusco Mantovano [partial-municipality/natural-boundary]; Lambrusco Salamino di Santa Croce [partial-municipality/natural-boundary]; Lamezia [partial-municipality/natural-boundary/named-sites]; Lessini Durello [partial-municipality/natural-boundary/altitude-limit/named-sites]; Lison [partial-municipality/natural-boundary/named-sites]; Lison-Pramaggiore [partial-municipality/natural-boundary/named-sites]; Locorotondo [partial-municipality/named-sites]; Lugana [partial-municipality/natural-boundary/altitude-limit/named-sites]; Malvasia di Bosa [partial-municipality/natural-boundary/named-sites]; Malvasia di Casorzo d'Asti [partial-municipality/named-sites/map-annex]; Marino [partial-municipality/natural-boundary/altitude-limit]; Martina [partial-municipality/named-sites]; Matino [partial-municipality]; Melissa [partial-municipality/natural-boundary/named-sites]; Merlara [partial-municipality/natural-boundary]; Monreale [partial-municipality/altitude-limit/named-sites/natural-boundary]; Montecompatri Colonna [partial-municipality/natural-boundary/named-sites]; Montecucco [partial-municipality/natural-boundary/named-sites]; Montecucco Sangiovese [partial-municipality/natural-boundary/named-sites]; Montefalco [partial-municipality/natural-boundary/named-sites]; Montefalco Sagrantino [partial-municipality/natural-boundary/named-sites/unclear]; Montello Rosso [partial-municipality/altitude-limit/natural-boundary/named-sites]; Monteregio di Massa Marittima [partial-municipality/natural-boundary/named-sites]; Montescudaio [partial-municipality/natural-boundary/named-sites]; Monti Lessini [partial-municipality/natural-boundary/named-sites/altitude-limit]; Morellino di Scansano [partial-municipality/natural-boundary/named-sites]; Moscadello di Montalcino [partial-municipality/natural-boundary/named-sites/altitude-limit]; Moscato di Trani [partial-municipality/natural-boundary/named-sites]; Narni [partial-municipality/natural-boundary/named-sites]; Nebbiolo d'Alba [partial-municipality/natural-boundary]; Offida [partial-municipality/natural-boundary]; Oltrepò Pavese [partial-municipality/natural-boundary]; Oltrepò Pavese metodo classico [partial-municipality/natural-boundary]; Oltrepò Pavese Pinot grigio [partial-municipality/natural-boundary]; Orcia [partial-municipality/natural-boundary/altitude-limit/named-sites]; Orta Nova [partial-municipality/natural-boundary/named-sites]; Ortrugo dei Colli Piacentini [partial-municipality/natural-boundary/named-sites]; Orvieto [partial-municipality/natural-boundary/named-sites]; Ostuni [partial-municipality/natural-boundary]; Parrina [partial-municipality/natural-boundary]; Pellaro [partial-municipality/named-sites]; Penisola Sorrentina [partial-municipality/natural-boundary]; Piave [partial-municipality/natural-boundary/unclear]; Piave Malanotte [partial-municipality/natural-boundary/unclear]; Pinerolese [altitude-limit/partial-municipality/named-sites]; Pinot nero dell'Oltrepò Pavese [partial-municipality/natural-boundary]; Pomino [partial-municipality/natural-boundary/named-sites]; Pornassio [partial-municipality/natural-boundary]; Portofino [partial-municipality/natural-boundary]; Primitivo di Manduria [partial-municipality/named-sites/natural-boundary]; Primitivo di Manduria Dolce Naturale [partial-municipality/named-sites/natural-boundary]; Ramandolo [partial-municipality/named-sites/natural-boundary]; Recioto di Gambellara [partial-municipality/named-sites/natural-boundary]; Reno [partial-municipality/natural-boundary]; Rimini [partial-municipality]; Riviera del Brenta [partial-municipality/natural-boundary]; Riviera ligure di Ponente [partial-municipality/natural-boundary]; Roero [partial-municipality/natural-boundary]; Roma [partial-municipality/natural-boundary]; Romagna [partial-municipality/natural-boundary]; Romagna Albana [partial-municipality/natural-boundary]; Rosso di Cerignola [partial-municipality/natural-boundary]; Rosso di Montepulciano [partial-municipality/named-sites/natural-boundary]; Rosso Piceno [natural-boundary/partial-municipality/named-sites]; S. Anna di Isola Capo Rizzuto [partial-municipality/natural-boundary/named-sites]; Salice Salentino [partial-municipality]; San Colombano al Lambro [partial-municipality/natural-boundary]; San Ginesio [partial-municipality/altitude-limit/natural-boundary]; San Martino della Battaglia [partial-municipality/natural-boundary]; San Severo [partial-municipality/natural-boundary]; San Torpè [partial-municipality/natural-boundary]; Sangue di Giuda [partial-municipality/natural-boundary/named-sites]; Sannio [partial-municipality/natural-boundary]; Sant'Antimo [partial-municipality/natural-boundary/named-sites]; Savuto [partial-municipality/natural-boundary]; Scanzo [partial-municipality/named-sites]; Scavigna [partial-municipality/named-sites]; Sciacca [partial-municipality/natural-boundary]; Sebino [partial-municipality]; Serrapetrona [partial-municipality/named-sites/natural-boundary]; Sforzato di Valtellina [altitude-limit/partial-municipality/natural-boundary]; Sibiola [partial-municipality/natural-boundary]; Soave [partial-municipality/natural-boundary/altitude-limit/map-annex]; Sovana [partial-municipality/natural-boundary]; Spoleto [partial-municipality/natural-boundary]; Squinzano [partial-municipality/natural-boundary]; Tarquinia [partial-municipality/natural-boundary/named-sites]; Terre Alfieri [partial-municipality/named-sites/natural-boundary]; Terre di Offida [partial-municipality/natural-boundary/named-sites/altitude-limit]; Terre di Veleja [partial-municipality/natural-boundary/altitude-limit/named-sites]; Val d'Arbia [partial-municipality/natural-boundary/altitude-limit/named-sites]; Val d'Arno di Sopra [partial-municipality/natural-boundary]; Val di Cornia Rosso [partial-municipality/natural-boundary]; Val di Magra [partial-municipality/natural-boundary/map-annex/named-sites]; Val Polcèvera [partial-municipality/natural-boundary/named-sites]; Val Tidone [partial-municipality/map-annex/natural-boundary/named-sites]; Valcalepio [partial-municipality/altitude-limit/natural-boundary/named-sites]; Valdadige [partial-municipality]; Valdichiana toscana [partial-municipality/natural-boundary/named-sites]; Vallagarina [partial-municipality/natural-boundary/named-sites]; Valle d'Aosta [partial-municipality/altitude-limit/natural-boundary]; Velletri [partial-municipality/altitude-limit/natural-boundary]; Veneto Orientale [partial-municipality/natural-boundary]; Verdicchio dei Castelli di Jesi [partial-municipality/natural-boundary]; Verdicchio di Matelica [partial-municipality/natural-boundary]; Verdicchio di Matelica Riserva [partial-municipality/natural-boundary]; Verduno Pelaverga [partial-municipality/natural-boundary]; Vernaccia di Oristano [partial-municipality/named-sites/natural-boundary]; Vernaccia di Serrapetrona [partial-municipality]; Vesuvio [partial-municipality/natural-boundary]; Vicenza [partial-municipality/natural-boundary/named-sites]; Vignanello [partial-municipality/natural-boundary]; Villamagna [partial-municipality/altitude-limit/natural-boundary/named-sites]; Vin Santo del Chianti [partial-municipality/natural-boundary/named-sites/altitude-limit]; Vin Santo del Chianti Classico [partial-municipality/natural-boundary/named-sites]; Vin Santo di Montepulciano [partial-municipality/named-sites]; Vino Nobile di Montepulciano [partial-municipality/named-sites]; Vittoria [partial-municipality/named-sites/natural-boundary/altitude-limit]; Zagarolo [partial-municipality/natural-boundary/named-sites] + +**Greece** (61): achaia [altitude-limit/map-annex]; anavyssos [partial-municipality/map-annex]; anchialos [partial-municipality/named-sites/natural-boundary]; argolida [altitude-limit/map-annex]; arhanes [named-sites]; arkadia [altitude-limit]; dafnes [partial-municipality/named-sites/natural-boundary]; elassona [altitude-limit/map-annex]; evros [altitude-limit]; goumenissa [altitude-limit/partial-municipality]; grevena [map-annex]; handakas-candia [partial-municipality]; ikaria [altitude-limit]; ilia [altitude-limit/named-sites/map-annex]; ilion [partial-municipality/named-sites]; imathia [altitude-limit]; iraklio [altitude-limit]; karditsa [altitude-limit/map-annex]; karystos [partial-municipality/altitude-limit]; kastoria [altitude-limit/map-annex]; kiklades [altitude-limit]; klimenti [altitude-limit]; kozani [altitude-limit]; krania [partial-municipality]; krannona [altitude-limit]; lefkada [partial-municipality/named-sites]; lesvos [altitude-limit]; malvasia-handakas-candia [partial-municipality/named-sites]; Μαντινεία [partial-municipality/named-sites]; mavrodafni-kefallinias [partial-municipality/named-sites/map-annex]; mavrodafni-patron [partial-municipality/named-sites/map-annex]; messinia [altitude-limit]; metaxaton [named-sites/map-annex]; meteora [altitude-limit/map-annex]; moschato-patron [named-sites/partial-municipality/map-annex]; moschatos-riou-patras [named-sites/partial-municipality]; nemea [partial-municipality]; opountia-lokridas [partial-municipality]; patra [partial-municipality/named-sites]; peloponnisos [map-annex]; peza [altitude-limit/partial-municipality]; pieria [altitude-limit]; pisatis [map-annex]; playies-egialias [altitude-limit/map-annex]; playies-enou [partial-municipality/named-sites/map-annex]; playies-knimidas [altitude-limit/partial-municipality]; Πλαγιές Πάικου [altitude-limit/partial-municipality]; playies-pentelikou [partial-municipality/named-sites/natural-boundary]; pylia [altitude-limit/map-annex]; rapsani [partial-municipality]; rethimno [altitude-limit/map-annex]; rodos [partial-municipality/named-sites]; samos [partial-municipality]; sitia [partial-municipality/named-sites/natural-boundary]; tegea [named-sites/partial-municipality/map-annex]; thapsana [partial-municipality/map-annex]; thasos [altitude-limit/natural-boundary]; trifilia [altitude-limit/map-annex]; Τύρναβος [partial-municipality]; velvedo [partial-municipality/map-annex]; zitsa [partial-municipality/named-sites/natural-boundary] + +**Spain** (48): 3 Riberas []; Bajo Vinalopó [cadastral-units/named-sites]; El Comtat [cadastral-units/named-sites]; La Marina Alta [named-sites/cadastral-units]; La Marina Baja [cadastral-units]; L’Alacantí [cadastral-units]; Almansa [partial-municipality]; Barbanza e Iria [partial-municipality]; Betanzos [partial-municipality/map-annex]; Bullas [partial-municipality/named-sites/natural-boundary]; Costa de Cantabria [altitude-limit/natural-boundary/unclear]; Costers del Segre [partial-municipality/named-sites]; Artesa de Segre [partial-municipality/named-sites]; Pallars [partial-municipality/named-sites]; Jerez-Xérès-Sherry [partial-municipality/named-sites]; Los Palacios [partial-municipality/natural-boundary]; Manzanilla de Sanlúcar [partial-municipality]; Ladera de Monterrei [partial-municipality]; Valle de Monterrei [partial-municipality]; Montilla-Moriles [partial-municipality/map-annex/named-sites]; Murcia [partial-municipality/named-sites/natural-boundary]; Pago de Arínzano [named-sites/partial-municipality/natural-boundary]; Penedès [named-sites]; Rías Baixas [partial-municipality]; Condado do Tea [partial-municipality]; Ribeira do Ulla [partial-municipality]; Ribeira Sacra [partial-municipality]; Amandi [partial-municipality]; Chantada [partial-municipality]; Quiroga-Bibei [partial-municipality]; Ribeiras do Miño [partial-municipality]; Ribeiras do Sil [partial-municipality]; Ribeiro [partial-municipality/named-sites]; Ribera del Guadiana [partial-municipality/natural-boundary]; Ribera del Queiles [partial-municipality/natural-boundary]; Rioja [partial-municipality/natural-boundary/named-sites/map-annex]; Rioja Alavesa [partial-municipality/natural-boundary/named-sites/map-annex]; Rioja Alta [partial-municipality/natural-boundary/named-sites/map-annex]; Rioja Oriental [partial-municipality/natural-boundary/named-sites/map-annex]; Sierras de Málaga [partial-municipality/named-sites/natural-boundary]; Terras do Navia [partial-municipality]; Toro [partial-municipality/named-sites]; Valdepeñas [partial-municipality]; Valencia [named-sites/partial-municipality]; Clariano [named-sites/partial-municipality]; Valle del Miño-Ourense [partial-municipality/map-annex]; Vinos de Madrid [named-sites/partial-municipality]; Arganda [named-sites/partial-municipality] + +**Portugal** (41): Alenquer [partial-municipality/map-annex]; Alentejo [partial-municipality/natural-boundary]; Borba [partial-municipality/natural-boundary]; Évora [partial-municipality/natural-boundary/named-sites]; Granja/Amareleja [partial-municipality/natural-boundary]; Moura [partial-municipality/natural-boundary]; Redondo [partial-municipality/natural-boundary]; Reguengos [partial-municipality/natural-boundary]; Arruda [partial-municipality/map-annex]; Beira Interior [partial-municipality]; Carcavelos [partial-municipality/named-sites/natural-boundary/map-annex]; Dão [partial-municipality/map-annex]; Do Tejo [natural-boundary/map-annex]; Tomar [natural-boundary/map-annex]; Douro [partial-municipality/named-sites]; Baixo Corgo [partial-municipality/named-sites]; Cima Corgo [partial-municipality]; Douro Superior [partial-municipality/named-sites]; Duriense [partial-municipality/named-sites]; Encostas d'Aire [partial-municipality/unclear]; Lagoa [partial-municipality/map-annex]; Lagos [partial-municipality/map-annex]; Lisboa [partial-municipality]; Minho [partial-municipality]; Óbidos [partial-municipality/map-annex]; Pico [altitude-limit/partial-municipality/named-sites]; Portimão [partial-municipality/map-annex]; Porto [map-annex/partial-municipality/named-sites/unclear]; Baixo Corgo [partial-municipality/named-sites]; Cima Corgo [partial-municipality/unclear]; Douro Superior [named-sites/partial-municipality]; Tavira [partial-municipality/map-annex]; Terras da Beira [partial-municipality]; Terras do Dão [partial-municipality]; Torres Vedras [partial-municipality/map-annex]; Transmontano [partial-municipality/named-sites]; Trás-os-Montes [partial-municipality]; Chaves [partial-municipality]; Planalto Mirandês [partial-municipality]; Valpaços [partial-municipality/named-sites/map-annex]; Vinho Verde [partial-municipality] + +**Romania** (34): aiud [partial-municipality/named-sites]; alba-iulia [partial-municipality/named-sites]; Babadag [partial-municipality]; banat [partial-municipality/named-sites]; banu-maracine [partial-municipality/named-sites]; Bohotin [partial-municipality]; Colinele Dobrogei [partial-municipality]; Coteşti [partial-municipality/named-sites]; Crişana [partial-municipality/named-sites]; dealu-bujorului [partial-municipality/named-sites]; Dealu Mare [partial-municipality/named-sites]; Dealurile Crişanei [partial-municipality]; Dealurile Munteniei [partial-municipality]; Dealurile Olteniei [partial-municipality]; Dealurile Sătmarului [partial-municipality]; dealurile-transilvaniei [partial-municipality/named-sites]; Dealurile Vrancei [partial-municipality]; Drăgăşani [partial-municipality/named-sites]; Huşi [partial-municipality/named-sites]; Iaşi [partial-municipality/named-sites]; Jidvei [partial-municipality]; Lechinţa [partial-municipality]; mehedinti [partial-municipality/named-sites]; Murfatlar [partial-municipality/named-sites]; nicoresti [partial-municipality/named-sites]; Odobeşti [partial-municipality]; Oltina [partial-municipality]; Panciu [partial-municipality]; sarica-niculitel [partial-municipality/named-sites]; Sebeş-Apold [partial-municipality]; Ştefăneşti [partial-municipality]; tarnave [partial-municipality/named-sites]; Terasele Dunării [partial-municipality]; Viile Timişului [named-sites/partial-municipality] + +**Hungary** (7): Balaton-felvidék [partial-municipality]; Debrői Hárslevelű [partial-municipality/map-annex]; Dunántúli [partial-municipality/natural-boundary]; Kőszeg [partial-municipality]; Mátra [partial-municipality]; Pannon [partial-municipality]; Tolna [partial-municipality] + +**Slovakia** (7): karpatska-perla [altitude-limit]; nitrianska [cadastral-units]; Skalický rubín [cadastral-units]; Stredoslovenská [cadastral-units]; TOKAJSKÉ VÍNO zo slovenskej oblasti [cadastral-units]; Vinohradnícka oblasť Tokaj [cadastral-units]; vychodoslovenska [cadastral-units] + +**Bulgaria** (6): chernomorski-raion [partial-municipality]; Дунавска равнина [partial-municipality]; iuzhno-chernomorie [partial-municipality]; karnobat [partial-municipality]; khaskovo [settlement-territories]; trakiiska-nizina [partial-municipality] + +**Cyprus** (3): krasochoria-lemesou [partial-municipality/altitude-limit/map-annex]; krasochoria-lemesou-afamis [partial-municipality/altitude-limit/natural-boundary/map-annex]; krasochoria-lemesou-laona [partial-municipality/altitude-limit/natural-boundary/map-annex] + +**Germany** (3): Mecklenburger Landwein [partial-municipality/map-annex]; Monzinger Niederberg [partial-municipality/natural-boundary]; Rheinischer Landwein [partial-municipality/map-annex] + +**Netherlands** (3): Achterhoek - Winterswijk [partial-municipality/named-sites]; Ambt Delden [partial-municipality/named-sites]; Oolde [partial-municipality/natural-boundary/named-sites] + +**Slovenia** (3): goriska-brda [partial-municipality/natural-boundary/altitude-limit]; teran [partial-municipality/altitude-limit/natural-boundary]; vipavska-dolina [partial-municipality/altitude-limit/natural-boundary] + +**Belgium** (2): Côtes de Sambre et Meuse [partial-municipality/natural-boundary]; Maasvallei Limburg [natural-boundary] + +**Croatia** (2): pokuplje [partial-municipality/natural-boundary]; prigorje-bilogora [partial-municipality/natural-boundary] + +## Verifier corrections + +- **Alto Adige** (it): All eight quoted references are present verbatim in the text (allowing for source line-wrapping): the Scena 650 m altitude limit (ll. 78-79), the Santa Maddalena exclusion (ll. 63-64), the SS12 Brennero / Laives km 427,700 start point (l. 26), the quote 829/786/742 Masi Schmalz-Egger-Moar-Trattoria — evidence: “Il limite segue poi la strada provinciale Merano-Bolzano in direzione sud fino il km 206,89, include lì sulla sua destra la p.f. 264/1 del c.c. Postal e prosegue sulla strada provinciale fino al km 20” +- **Aprilia** (it): References: all six check out verbatim against the file (only differences are the source's hard line wraps, e.g. "ferrovia Roma-Napoli (incrocio..." spans lines 10-11 and "strada Grugnole-Tre / Cancelli-Palmolive" spans lines 16-17). Basis: correct as given — the opening sentence explicitly says "pa — evidence: “partendo dal punto d’incontro fra i confini amministrativi di Aprilia e di Cisterna con il confine provinciale della provincia di Roma, il limite sale lungo il confine provinciale di Roma, sino a inco” +- **Arlanza** (es): Basis and priority are correct; three references are not verbatim, so confirmed=false on the reference check alone. The text is a whole-municipio list for Burgos and Palencia (municipality-list), names two explicit cadastral identifiers — polígono catastral 518 and 523 (cadastral-units) — and states — evidence: “Dentro del Término municipal de Los Balbases, únicamente será apto para el cultivo del viñedo amparado la zona situada en el polígono catastral 523 Dentro del Término municipal de Ciruelos de Cervera ” +- **asenovgrad** (bg): The full text is a single sentence listing the землища (settlement cadastral territories) of twelve named settlements in oblast Plovdiv, as whole units. (1) Basis "cadastral-units" is wrong: the text gives no cadastral identifiers at all — no EKATTE codes, no поземлен имот / парцел / имотен номер, o — evidence: “Районът за производство на вино със ЗНП (ГКНП) „Асеновград” е очертан при следните граници на землищата на населените места – гр. Асеновград, с. Нови извор, с. Горнослав, с. Дебър, с. Златовръх, с. Му” +- **Belokranjec** (si): All 8 references are verbatim in the text (the three containing "500 m" / "400 m" / "350 m" match once the file's non-breaking spaces, bytes c2 a0, between the number and "m" are normalised — not fabrications). Four of the five basis tags hold: cadastral-units (katastrske občine Talčji vrh/Talčji Vr — evidence: “gre po tej cesti do ceste Črnomelj-Semič in po njej do južne meje katastrske občine Kot, po vzhodni meji katastrske občine Kot do južne meje katastrske občine Semič, obkroži naselje Vavpča vas” +- **bizeljcan** (si): All five references are verbatim in the text (the file has a plain hyphen in "Razbor-Breg", matching the reference). cadastral-units is correct: "katastrskih občin Marijina vas in Paneče" names two explicit cadastral municipalities. natural-boundary is correct: the Sava river ("po reki Savi" ... "do — evidence: “Meja vinorodnega okoliša Bizeljsko – Sremič poteka od naselja Breg po reki Savi do državne meje s Hrvaško, po državni meji s Hrvaško, po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijin” +- **bizeljsko-sremic** (si): All five references are present verbatim in the text (four in the boundary sentence on line 3, the fifth in the sub-district sentence on line 5), so the reference list is correct. `cadastral-units` is correct: "katastrskih občin Marijina vas in Paneče" names two cadastral municipalities as explicit — evidence: “Meja vinorodnega okoliša Bizeljsko – Sremič poteka od naselja Breg po reki Savi do državne meje s Hrvaško, po državni meji s Hrvaško, po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijin” +- **Brachetto d'Acqui** (it): All four references are present in the text (the two "Provincia di ...:" references join a heading line to the following list line with ": " instead of the file's newline, but the wording and the comune order are identical, so they are faithful). The basis is correct: two "l'intero territorio ammini — evidence: “l’intero territorio amministrativo dei seguenti comuni: Vesime, Cessole, Loazzolo, Bubbio, Monastero Bormida, Rocchetta Palafea, Montabone, Fontanile, Mombaruzzo, Maranzana, Quaranti, Castel Boglione,” +- **Canelli** (it): All eight cited references are present verbatim in the text (three span line wraps, the only difference being line-break whitespace), and the basis is correct: whole municipalities are enumerated (Calamandrana, Calosso, Canelli, Cassinasco, Coazzolo, Castiglione Tinella, S. Stefano Belbo) = municipa — evidence: “San Marzano Oliveto: viene esclusa la porzione a nord del territorio comunale confinante con Castelnuovo Calcea, Nizza Monferrato.” +- **Cannellino di Frascati** (it): All eight references are present verbatim in the text (only line-wrap breaks differ), and the basis is correct: three whole comuni (municipality-list), Roma and Montecompatri included only in part with a described traverse (partial-municipality), the fosso Valpignola (natural-boundary), and toponyms — evidence: “Tale zona comprende per intero il territorio amministrativo dei comuni di Frascati, Grottaferrata, Monte Porzio Catone, ed in parte quelli di Roma e Montecompatri.” +- **Castel del Monte** (it): References: all 8 are present verbatim (several only span a PDF line wrap — "ai confini / comunali di Canosa e Barletta" and "via vecchia Corato-/Terlizzi" — and match exactly once rejoined). Basis: correct as given. "partial-municipality" is explicit ("in parte i territori comunali di Andria, Canos — evidence: “Tale zona è così delimitata: dal punto di partenza dell’antico ponte romano sul fiume Ofanto, si segue ad est, il deflusso del fiume che segna confine con la provincia di Foggia, fino ad arrivare ai c” +- **Castelli Romani** (it): Priority is wrong: high requires explicit cadastral identifiers or a within-municipality split that names the neighbouring appellation, and this text has neither. There is no cadastral vocabulary anywhere (no foglio/particella/mappale/catastale), and no neighbouring appellation is named — Frascati, — evidence: “La delimitazione della zona stessa viene di seguito descritta: partendo in senso antiorario, in comune di Roma dall’incrocio della via Casilina con il G.R.A., segue in direzione sud-ovest il percorso ” +- **Cerveteri** (it): All eight references are present verbatim in /private/tmp/claude-501/-Users-boris-playground-open-wine-map/f9e43876-ad08-488f-8089-b826e85e50cf/scratchpad/delim/texts/cerveteri.txt (only line-wrap whitespace differs, e.g. "Santa\nMarinella" and "e a sud\ndella località Spalle di S. Maria"), so the r — evidence: “Tale zona è così delimitata: +a nord-ovest il limite segue dalla foce verso nord il fiume Mignone sino alla località Pietrara, prende poi per la strada che porta a c.le Lazi e prima di giungervi, piega” +- **Cesanese di Olevano Romano** (it): All eight references are verbatim in the text (several wrap across source line breaks but match exactly, curly apostrophes included), and the basis is right: whole-territory inclusion of Olevano Romano (municipality-list), an explicit "parte di quello di Genazzano" with a traced internal line (parti — evidence: “Tale zona è così delimitata: partendo dall’incrocio del confine comunale di Roiate e Olevano Romano con quello tra le province di Roma e Frosinone, località la Morra Rossa, il limite segue quest’ultim” +- **chernomorski-raion** (bg): All eight quoted references are genuinely in the text, verbatim (Балчик l.5, Каварна l.14, Шумен l.30, Смядово l.32-33, Върбица l.36, Варна l.58, Велики Преслав l.34-35, Каспичан l.25-26); several are truncated prefixes of longer entries but nothing is fabricated. The basis is wrong on two of three — evidence: “Районът за производство на вино със ЗНП „Черноморски район” е очертан при следните граници на землищата на населените места:” +- **Cirò** (it): All eight references are present verbatim in the text (allowing for line-wrap): ref 1 at lines 6-8, ref 2 at line 10, ref 3 at lines 14-15, ref 4 at lines 22-23, ref 5 at lines 23-24, ref 6 at lines 27-28, ref 7 at lines 28-29, ref 8 at lines 31-32. The basis is correct: municipality-list (Cirò and — evidence: “devono essere prodotte nella zona di produzione appresso indicata, che comprende in tutto i territori dei comuni di Cirò e Cirò Marina e in parte i territori dei comuni di Melissa e Crucoli. Tale zona” +- **Collio Goriziano** (it): Basis and priority hold; one reference string was paraphrased. Checked all 8 references against the full 36-line text (whitespace-normalised): 7 are verbatim, including both halves of the ellipsis-joined "strada nazionale n. 56, intersecando la ferrovia Gorizia-Udine ... fino al casello in prossimit — evidence: “tale zona e' delimitata da una linea che dal cavalcavia della ferrovia Gorizia-Udine, prende la strada che dal quadrivio di Madonna del Fante porta direttamente a Piedimonte del Calvario.” +- **Copertino** (it): References: all eight are present verbatim in /private/tmp/claude-501/-Users-boris-playground-open-wine-map/f9e43876-ad08-488f-8089-b826e85e50cf/scratchpad/delim/texts/copertino.txt (only PDF line-wraps intervene, e.g. "tutto il territorio / amministrativo di:" and "i territori comunali di : / Galat — evidence: “dalla strada statale Salentina di Gallipoli (n. 101) a quota 50, in prossimità di Collemeto; il limite segue, verso ovest, la strada che conduce a S. Barbara toccando la Masseria Bassi; giunto a quota” +- **Cviček** (si): References: all eight are present. Seven match byte-for-byte. The eighth ("po severnih mejah katastrskih občin Stare Žage in Pribišje do plastnice 450 m nad naseljem Gornje Laze") matches only after whitespace normalisation — the file stores U+00A0 non-breaking spaces (c2 a0) in "450 m nad". That is — evidence: “po zahodni meji katastrske občine Svibno, po južni meji katastrske občine Podkum, po potokih Sopota in Globočnjak, obkroži naselje Mamolj, poteka po cesti Zgornji Mamolj-Gradišče-Gradiške Laze-Šmartno” +- **Dolcetto d'Asti** (it): References and basis are correct; priority is not. The quoted reference appears in the text verbatim (lines 11-12, differing only by the file's hard line-wrap after 'dei comuni di'). The basis tags all hold: clause a) is a list of whole municipalities ('l'intero territorio dei comuni di ...') = muni — evidence: “b) la porzione di territorio situata sulla destra orografica del torrente Belbo dei comuni di Calamandrana, Canelli, Nizza Monferrato.” +- **dolenjska** (si): All eight references are verbatim in the text, and cadastral-units, altitude-limit, natural-boundary and named-sites are all correctly supported: eight named katastrske občine each with a specified side (Gotna vas, Bršljin, Podkraj, Svibno, Podkum, Vintarjevec, Stare Žage, Pribišje), the 450 m conto — evidence: “po severnih mejah katastrskih občin Stare Žage in Pribišje do plastnice 450 m nad naseljem Gornje Laze, po plastnici 450 m ob pobočju Gorjancev do državne meje s Hrvaško (pri naselju Vrbje nad Kostanj” +- **Дунавска равнина** (bg): All eight references are present verbatim in the full text (checked by exact fixed-string match), and municipality-list + partial-municipality are correct: the area is defined as the землища of whole общини listed by област, and a long exclusion clause removes named village землища from ten-plus of — evidence: “Следните землища на населени места, както са изброени по общини, се изключват от очертанията на района: Община Белоградчик: с. Граничак, с. Праужда, с. Салаш, с. Стакевци.” +- **Esino** (it): All eight references are verbatim in the text (lines 6-7, 9, 11-12, 14, 22, 24, 25-26, 41), and high priority is justified: the Macerata portion is a sub-municipal metes-and-bounds perimeter that explicitly names the neighbouring appellations it borders ("che delimita le zone di produzione del Verdi — evidence: “Raggiunge poi le propaggini di Monte-Castel S. Maria secondo la direttrice che da S. Angelo (q. 549) va a Case il Poggio (q. 507), attraverso le quote 684, 592, 529.” +- **Frascati** (it): References and basis are correct; only the priority is wrong. All eight quoted references appear verbatim in /private/tmp/claude-501/-Users-boris-playground-open-wine-map/f9e43876-ad08-488f-8089-b826e85e50cf/scratchpad/delim/texts/frascati.txt (checked after joining the PDF's hard line wraps). The b — evidence: “Tale zona è così delimitata: sulla via Casilina, appena superato il Km. 21 al ponte di Pantano, il limite segue in direzione sud-est il fosso Valpignola sino ad incontrare il confine comunale tra Roma” +- **Frascati Superiore** (it): References: all eight are present verbatim in the text (only line-wrap whitespace differs); nothing is invented. Basis: correct and unchanged. municipality-list is explicit ("per intero il territorio amministrativo dei comuni di Frascati, Grottaferrata, Monte Porzio Catone"); partial-municipality is — evidence: “Tale zona è così delimitata: sulla via Casilina, appena superato il Km. 21 al ponte di Pantano, il limite segue in direzione sud-est il fosso Valpignola sino ad incontrare il confine comunale tra Roma” +- **Genazzano** (it): All 8 references are present verbatim in the text (differences are only line-wrap breaks), so references are kept unchanged. partial-municipality is correct and well evidenced ("ed in parte quelli di Olevano Romano, San Vito Romano, Cave ... e Paliano"), and natural-boundary is correct (fiume Sacco, — evidence: “nordovest, la strada che costeggia a ovest Colle Tocciano e a est Colle Cerreto passando per le quote 258, 299, 343 e da quest'ultima per una retta spezzata in direzione nord raggiunge quota 355 (Coll” +- **Großräschener See** (de): Basis is wrong on one element and one reference is not verbatim. (1) named-sites is unsupported: the text lists no named vineyard site, Lage, Gewann or lieu-dit; the only geographic feature invoked is the lake, which the delimiting sentence does not even name ("des gleichnamigen Sees") and which is — evidence: “Die mit Flur und Flurstück eingegrenzte Fläche der g. g. A. Großräschener See liegt in der g. g. A. Brandenburg. Die folgenden Flurstücke gehören zum abgegrenzten Gebiet g. g. A. Großräschener See.” +- **iambol** (bg): The eight quoted references are all verbatim in the text, but the reference list is truncated: the sentence enumerates sixteen settlements and the reader stopped at с. Роза, dropping с. Чарган, с. Победа, с. Недялско, с. Малък манастир, с. Лозенец, с. Първенец, с. Болярово and с. Тенево. The basis i — evidence: “Районът за производство на вино със ЗНП “Ямбол” е очертан при следните граници на землищата на населените места – гр. Ямбол, с. Ботево, с. Иречеково, с. Калчево, с. Каменец, с. Козарево, с. Меден клад” +- **iuzhno-chernomorie** (bg): Three failures. (1) Basis: the delimitation is drawn "при следните граници на землищата на населени места" — by the boundaries of settlement land territories, listed purely by settlement name (с./гр. + name). There is not a single explicit cadastral identifier anywhere in the 36-line text: no EKATTE — evidence: “Районът за производство на вино със ЗНП “Южно Черноморие” е очертан при следните граници на землищата на населени места:” +- **juznoslovenska** (sk): All 8 references are present verbatim (three only straddle a line wrap in the source file: "ohraničená hranicami / katastrálnych území obcí", "Bratislava-Podunajské / Biskupice", "Dunajská / Streda"). municipality-list and cadastral-units are correct: the operative clause delimits the region by "hra — evidence: “Južnoslovenská vinohradnícka oblasť je vinohradnícka oblasť, ktorá je ohraničená hranicami katastrálnych území obcí: Abrahám, Andovce, Bajč, Bajtava, Baloň, Bátorove Kosihy, Belá, Bíňa,Blahová, Branov” +- **karlovo** (bg): The full text is three lines and contains exactly one sentence. All eight quoted references do appear verbatim (only "с. Каравелово" is split by a line wrap, "с." ending line 4 and "Каравелово" opening line 5 — a wrap artifact, not a discrepancy), but the reader's list is incomplete: the sentence al — evidence: “Районът за производство на вино със ЗНП “Карлово” е очертан при следните граници на землищата на населените места – гр. Хисаря, с. Паничери, с. Найден Герово, с. Старосел, с. Каравелово, с. Московец, ” +- **karnobat** (bg): All 8 quoted references are verbatim in the text, but the list is incomplete: the text names 12 settlements, and the reader's quote was truncated mid-list, dropping с. Огнен, с. Венец, с. Деветак and с. Лозенец (the last in a different oblast, Ямбол — relevant to the 680 km2 iambol overlap). Basis c — evidence: “Районът за производство на вино със ЗНП “Карнобат” е очертан при следните граници на землищата на населените места – с. Екзарх Антимово, с. Житосвят, с. Зимен, с. Искра, с. Сигмен, с. Хаджиите, с. Асп” +- **khaskovo** (bg): References are all verbatim in the text, but the list is truncated: 11 of 20 settlements captured, with four merged into a single string. Missing are с. Елена, с. Узунджово, с. Харманли, с. Чернодъб, с. Сива река, с. Маточина, с. Младиново, с. Щит, гр. Свиленград. The basis is wrong on both labels. — evidence: “Районът за производство на вино със ЗНП “Хасково” е очертан при следните граници на землищата на населените места – с. Орлово, с. Поляново, с. Долно Войводино, с. Царева поляна, с. Тънково, с. Сусам, ” +- **kras** (si): All eight references are present verbatim in the single boundary sentence, and priority=high is justified twice over: the text carries an explicit cadastral identifier (katastrska občina Štorje, mappable against Slovenia's public k.o. layer) and it adopts a named neighbouring appellation's border (" — evidence: “mimo naselja Žirje do vzpetine Predlovec, po južni meji katastrske občine Štorje do plastnice 500 m” +- **krasochoria-lemesou-afamis** (cy): Two defects. (1) One reference is not verbatim: "Αριθμός συνημμένων χαρτών: 1" does not occur in the text. The source is a broken table layout — line 9 reads "Αριθμός συνημμένων 1" and the word "χαρτών" sits alone on line 10; there is no colon and the words are not contiguous. Replaced with t — evidence: “Τα τεμάχια των κοινοτήτων Βουνί, Κοιλάνι, Μανδριά και Όμοδος (ανατολικά του ποταμού Χα - Ποτάμι) με υψόμετρο 750 μέτρα και άνω.” +- **krasochoria-lemesou-laona** (cy): Two corrections. (1) One reference is not verbatim: the file renders the map annex as a two-line table, "Αριθμός συνημμένων 1" / "χαρτών" (lines 9-10), not "Αριθμός συνημμένων χαρτών: 1" — the colon and word order are reconstructed. Replaced with the verbatim line "b. Χάρτης οριοθετημένης περι — evidence: “Τα τεμάχια των κοινοτήτων Άρσος, Βάσα και Όμοδος (δυτικά του ποταμού Χα - Ποτάμι) με υψόμετρο 750 μέτρα και άνω.” +- **Lago di Corbara** (it): All eight references are present verbatim in the text (only line-wrap whitespace differs), and the basis is correct: the text names one whole comune (Baschi) → municipality-list; explicitly takes only "parte del territorio amministrativo del Comune di Orvieto" and then describes the boundary → parti — evidence: “cosi' delimitato: "partendo dal lato destro del fiume Tevere all'altezza della diga di Corbara, il confine segue la destra della strada che dal guado del fiume Tevere si innesta sulla strada comunale ” +- **liubimets** (bg): The eight claimed references are all present verbatim, but the basis and priority are both wrong. The text delimits the zone as "границите на землищата на населените места" — the boundaries of the land areas (землища) of 21 named settlements, all "находящи се в област Хасково". A землище is a whole — evidence: “Районът за производство на вино със ЗНП “Любимец” е очертан при следните граници на землищата на населените места – с. Белица, с. Георги Добрево, с. Коларово, с. Левка, с. Лозен, с. Малко градище, с. ” +- **malokarpatska** (sk): References are all fine: every one of the eight strings is verbatim in the text (the anchor phrase only wraps a line break between "hranicami" and "katastrálnych"), and the seven Bratislava boroughs all appear in the list. The basis and priority are wrong. The whole text is one flat enumeration of u — evidence: “Malokarpatská vinohradnícka oblasť je vinohradnícka oblasť, ktorá je ohraničená hranicami katastrálnych území obcí: Báhoň, Banka, Bernolákovo, Bílkove Humence, Bíňovce, Blatné, Bojničky, Boleráz, Boro” +- **Martina** (it): References: all eight are in the text verbatim (allowing for the PDF's line wraps), so no reference is corrected. Basis: correct as given. The text lists whole municipal territories (Martina Franca, Crispiano, Alberobello) = municipality-list; it explicitly takes only "parte del territorio comunale — evidence: “parte del territorio comunale di Ceglie Messapico e Ostuni che resta delimitato da una linea di confine che da Specchia Tarantina, in agro di Martina, segue la strada asfaltata che passando per la Mas” +- **Matino** (it): References: all seven are present in the file, verbatim apart from PDF line-wrapping (and, in the first one, an interleaved stray page number "1" between "l’intero" and "territorio" on lines 5-7). No fabricated quotes. Basis is correct: the text names one whole comune (Matino) plus "in parte i terri — evidence: “dal centro abitato di Tuglie la linea di delimitazione segue, verso ovest, la strada che conduce ad Alezio, passando per le contrade Villa Filomena e Villa Ronci, costeggia, escludendolo, l’abitato di” +- **Metliška črnina** (si): All eight references are present in the text and the key quote is accurate (the only textual difference is that the source uses a non-breaking space between the figure and "m", e.g. "500 m" — not a substantive discrepancy). cadastral-units is correct: the text names explicit cadastral municipalities — evidence: “po vzhodni meji katastrske občine Kot do južne meje katastrske občine Semič, obkroži naselje Vavpča vas” +- **Montecompatri Colonna** (it): All eight references are present verbatim in the text (three of them wrap across line breaks in the OCR'd source, but the wording is exact), and the key quote matches lines 12-15. The basis is correct as given: partial-municipality is stated outright and then specified by a closed metes-and-bounds t — evidence: “Tale zona pertanto comprende tutto il territorio comunale di Colonna e parte di quelli di Montecompatri, Zagarolo e Roccapriora, ed è delimitata come appresso:” +- **Нова Загора** (bg): The reference strings are all verbatim, but the basis and priority are wrong and the reference list is truncated. The text delimits the zone as the whole землища (settlement land territories) of 32 named settlements, taken entire; it contains no explicit cadastral identifiers whatsoever — no EKATTE — evidence: “Районът за производство на вино със ЗНП „Нова Загора“ е очертан при следните граници на землищата на населените места – с. Горно Александрово, с. Братя Кунчеви, с. Винарово, с. Гавраилово, с. Гурково,” +- **Penedès** (es): The eight quoted references are all verbatim in the text (lines 131-138), but the list is incomplete: the pliego names ten unidades geográficas menores, omitting "Turons de Vilafrana" (spelled thus in the source, not "Vilafranca") and "Vall Bitlles-Anoia". The basis is wrong on cadastral-units: the — evidence: “Se relacionan las parcelas que forman cada una de las anteriores unidades geográficas menores, en el siguiente enlace:” +- **plovdiv** (bg): The eight claimed references are all present verbatim (с. Първенец only after undoing the file's line wrap, where line 4 ends "с." and line 5 opens "Първенец"), but the basis and the priority are both wrong. The whole delimitation is one sentence that enumerates the "граници на землищата на населени — evidence: “Районът за производство на вино със ЗНП “Пловдив” е очертан при следните граници на землищата на населените места – с. Брестник, с. Марково, гр. Перущица, с. Драгомир, с. Марково, с. Първенец, с. Брес” +- **podravje** (si): All 8 references are present verbatim in the text (line 6, the Štajerska Slovenija perimeter), and the key quote matches character-for-character, so the reference extraction is sound; I added 4 further cadastral references the reader missed (Veliki Boč/Zgornji Slemen, Ruše, and the Prekmurje ones Ži — evidence: “po reki Savinji do zahodne meje katastrske občine Letuš, po zahodnih mejah katastrskih občin Letuš in Podvrh, se pri naselju Šmartno nad Prekopo odcepi proti zahodu” +- **Douro Superior** (pt): All eight references are verbatim in the text (lines 3-14), so none is fabricated; I only expanded reference 6 to include the Vila Flor freguesia list on lines 9-11 that the reader dropped. The basis is wrong on one count: `cadastral-units` requires explicit cadastral identifiers, and the text conta — evidence: “as propriedades que foram de D. Maria Angélica de Sousa Pinto Barroso, na freguesia de Frechas, e as da Sociedade Clemente Meneres, nas freguesias de Avantos, Carvalhais, Frechas e Romeu, do concelho ” +- **posavje** (si): All 8 quoted references are present verbatim (grep -F confirms exactly one occurrence of each, including the capitalised "Talčji Vrh" variant on the Bela krajina line). cadastral-units is correct: the text names 13 explicit cadastral units (Marijina vas, Paneče, Gotna vas, Bršljin, Podkraj, Svibno, — evidence: “Meja vinorodnega okoliša Bizeljsko – Sremič poteka od naselja Breg po reki Savi do državne meje s Hrvaško, po državni meji s Hrvaško, po južni meji Šmarsko-virštanjskega vinorodnega okoliša do Marijin” +- **prekmurje** (si): All eight references are present verbatim in /private/tmp/claude-501/-Users-boris-playground-open-wine-map/f9e43876-ad08-488f-8089-b826e85e50cf/scratchpad/delim/texts/prekmurje.txt (references 1-6 in the first-area sentence on line 4, references 7-8 in the second-area sentence on line 5), and the ke — evidence: “Meja prvega območja prekmurskega vinorodnega okoliša poteka od naselja Korovci po državni meji z Avstrijo in Madžarsko do južne meje katastrske občine Žitkovci in po njej do ceste Kamovci-Dobrovnik, p” +- **primorska** (si): References: all 8 quoted references are present verbatim in the file (verified by literal substring match), as is the key quote. Priority: high is justified — the text carries an explicit cadastral identifier ("katastrske občine Štorje") and, in the same Kras passage, runs along the boundary of a na — evidence: “mimo naselja Žirje do vzpetine Predlovec, po južni meji katastrske občine Štorje do plastnice 500 m, proti severu preko Struge na plastnico 400 m na cesti Senožeče-Sežana in poteka po plastnici 400 m ” +- **Recioto di Soave** (it): Basis and priority are right; one reference is not verbatim, so confirmed=false on that ground alone. Checked all eight quotes against the file (whitespace-normalised, so line-wrap breaks do not count as mismatches): seven are exact, including the key quote at lines 48-49. The eighth, "località Tess — evidence: “da qui segue poi la strada per località Cereghi, Fornace, Tessari a quota 250, corre lungo il vaio Muni fino alla località Soejo” +- **Ribera del Gállego - Cinco Villas** (es): All three references are present verbatim in the text (the second one only broken by the file's hard line wraps), and priority=high is correctly justified by explicit cadastral identifiers (polígonos catastrales). The basis is wrong only by omission: the text marks the municipio of Zaragoza with an — evidence: “Zaragoza**, Zuera. +**(En Zaragoza Polígonos Catastrales Nº. 1 al 36. Polígonos Nº 42 al 55, 61, 62, 66, 67, 204, 205, 206, 207, 208, 209, 210, 218, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233 y 2” +- **Sächsischer Landwein** (de): All eight references are verbatim in the text (the Dresden entry is line 5 truncated with an explicit ellipsis; the Schlieben/Ortrand/Jessen entries are exact substrings of line 10, en dash in "Elbe–Elster" included), and three of the four basis tags hold: cadastral-units (Gemarkung + numbered Flur) — evidence: “Die genaue Abgrenzung ist unter www.ble.de/eu-qualitaetskennzeichen-wein einsehbar.” +- **Sebino** (it): All six references are present verbatim in the text (only line-wrapping differs), and the basis is correct: the text lists 19 whole comuni ("comprende l'intero territorio comunale di Capriolo, Paratico, ...") and then carves out one municipality partially ("la parte ovest del territorio comunale di — evidence: “e la parte ovest del territorio comunale di Brescia, meglio identificato come zona della collina di S. Anna, fra il confine comunale di Brescia, a sud la ex s.s. n. 11 e la strada provinciale per il p” +- **sliven** (bg): The references are all present verbatim (allowing for the source's line-wrapping, e.g. "с. Г.\nАлександрово"), but the list was truncated at "с. Тополчaне", dropping the final 14 settlements the text names (Чинтулово, Чокоба, Шивачево, Блатец, Трапоклово, Езеро, Пъдарево, Мокрен, Асеновец, Брястово, — evidence: “Районът за производство на вино със ЗНП “Сливен” е очертан при следните граници на землищата на населените места – гр. Сливен, кв. Речица - Сливен, с. Близнец, с. Кермен, с. Г. Александрово, с. Гавраи” +- **stara-zagora** (bg): All eight references are present verbatim, but the basis and priority are wrong. The whole delimitation is a single sentence listing the землища (whole settlement territories) of seven named places in Stara Zagora province. There is no cadastral identifier of any kind — no parcel, imot or EKATTE num — evidence: “Районът за производство на вино със ЗНП “Стара Загора” е очертан при следните граници на землищата на населените места – гр. Стара Загора, с. Богомилово, с. Васил Левски, с. Кирилово, с. Тракия, с. Хр” +- **sungurlare** (bg): The references are all genuinely in the text, but the basis and priority are both wrong. The text is a plain enumeration of the землища (whole settlement land territories) of 19 named settlements — 17 in Burgas oblast, 2 (Мокрен, Пъдарево) in Sliven oblast — taken in their entirety. There are no cad — evidence: “Районът за производство на вино със ЗНП “Сунгурларе” е очертан при следните граници на землищата на населените места – гр. Сунгурларе, с. Славянци с. Вълчин, с. Грозден, с. Есен, с. Климаш, с. Костен,” +- **tarnave** (ro): Basis is correct and stays as classified; priority is overstated and one reference is not verbatim. (1) PRIORITY: high requires either explicit cadastral identifiers or a within-municipality split that names the neighbouring appellation. The text contains no cadastral units at all — no parcel, plot, — evidence: “-com. Brateiu -satul Buzd;” +- **Tarragona** (es): Basis and priority are both correct and need no change: the text is a plain list of municipios (municipality-list), four entries carry explicit polígono/parcela identifiers (cadastral-units), and those four use "excepto" to state that only part of the municipality is included and precisely which cad — evidence: “Garcia, (excepto parcela 66 del Polígono 3; parcelas 101, 105 y de la 111 a la 113 del Polígono 6; los Polígonos 7, 8, 9, 10 y 11; las parcelas de la 1 a la 13, de la 17 a la 38, 45, 133 y 134 del Pol” +- **Távora-Varosa** (pt): All seven references are present verbatim in the text (allowing for line-wrap whitespace), and high priority is correct: the Lamego entry is a within-municipality split that explicitly names the neighbouring appellation (Região Demarcada do Douro), which is exactly the overlap case being swept for. — evidence: “Do município de Lamego, as freguesias de Britiande, Cepões da União de Freguesias de Cepões, Meijinhos e Melcões, Ferreirim, Lalim, Vila Nova de Souto de El -Rei e a parte da freguesia de Várzea de Ab” +- **Tharsys** (es): Basis and priority are correct, but one reference is not verbatim. The delimitation is genuinely cadastral: it names SIGPAC Polígono 47 in Requena and enumerates 25 parcelas, so basis=cadastral-units holds and high priority is justified on the cadastral-identifier criterion alone (no neighbouring-ap — evidence: “La superficie de viñedo según medición SIGPAC es de 19,5 Ha., está situada en el término municipal de Requena (Valencia). Incluyendo las siguientes parcelas, según la nomenclatura del Sistema de Infor” +- **Uclés** (es): Basis and priority are correct; one reference is truncated. The Toledo references are verbatim: "Cabezamesada: polígonos 501 a 505 (a.i.), 507 y 508", "Corral de Almaguer: polígonos 12, 17 a 25 (a.i.), polígonos 72 y 73" and the whole municipality "Santa Cruz de la Zarza" all appear exactly as recor — evidence: “De Cuenca: Acebrón (El), Alcázar del Rey, Almendros, Belinchón, Carrascosa del Campo, Fuente de Pedro Naharro, Horcajo de Santiago, Huelves, Huete, Langa, Loranca del Campo, Paredes, Pozorrubio, Rozal” +- **Uhlen Roth Lay** (de): Basis and priority are correct: the text bounds the site with five explicit cadastral parcel numbers (cadastral-units), uses named sites (Uhlen Roth Lay, Belltal), and anchors its upstream end on a natural feature (the Taleinschnitt Belltal plus downstream direction along the Mosel), so high priorit — evidence: “Das Gebiet beginnt mit der ersten Weinbergen flussabwärts des Taleinschnittes Belltal - Parzellennummern 262/54 und 156/53 und endet flussabwärts mit folgenden Parzellen: Nr. 6/1, 117/1 und 2394/3.” +- **Urbezo** (es): All eight references are genuinely in the text (the parcel reference differs only by a dropped colon: the text reads "51 parcelas: 1, 6, 8, ..."; the 51 listed numbers match exactly). High priority is correct — the text gives explicit cadastral identifiers (polígono catastral 35 plus 51 enumerated p — evidence: “Está limitada al norte, por el término municipal de Alfamén; al sur, por el camino de Longares a Almonacid de la Sierra; al este, por el Camino del Rutal de Alfamén al Santuario de la Virgen de Laguna” +- **Valdejalón** (es): Basis and priority are correct; one reference is not verbatim. The text is a single sentence listing municipios de la provincia de Zaragoza (municipality-list), admitting Zaragoza only in part and stating how (partial-municipality), by explicit cadastral identifiers "polígonos catastrales 152, 153, — evidence: “Zaragoza (en Zaragoza, polígonos catastrales 152, 153, 154, 155 y 178, pertenecientes a la cuenca del río Jalón).” +- **Clariano** (es): References check out: all four quoted strings appear verbatim in the text (the CLARIANO list on the subzona line, the two Almansa paraje runs and the Caudete parajes in the following paragraph). Basis is also right — four subzonas of whole términos municipales (municipality-list), parajes as named s — evidence: “Componen también la zona de producción las parcelas inscritas en el Registro Vitícola y explotadas por socios de cooperativas o titulares de bodegas inscritas en los Registros del Consejo Regulador qu” +- **Valle del Miño-Ourense** (es): Basis is correct on all three tags: the text delimits by parroquia inside named municipios (partial-municipality), enumerates whole términos municipales as the outer frame (municipality-list), and cites a graphic annex ("En el anexo I de este pliego de condiciones se muestra de forma gráfica el terr — evidence: “deberán proceder exclusivamente de viñedos de la zona de producción y elaboración, constituida por los terrenos aptos para la producción de uva pertenecientes a los términos municipales y parroquias s” +- **Würzburger Stein-Berg** (de): Priority high is correct and the cadastral-units / named-sites bases hold, but the classification is wrong on one basis tag and three references are not verbatim. + +BASIS. cadastral-units is right: line 3 states "Alle genannten Zahlen sind ausschließlich eingetragene Flurnummern der kreisfreien Stadt — evidence: “Die Besonderheit an dieser abgegrenzten g. U.-Fläche liegt in der Steilheit, seiner Nähe zum Fluss als Wärmespeicher und dem vorherrschenden Grundgestein des Mittleren Muschelkalks, wodurch die g. U.-” From c0ce4563f472a051b71e9b4f6de74f043dfa9eed Mon Sep 17 00:00:00 2001 From: Boris De Vloed Date: Sun, 4 Oct 2026 11:06:08 +0200 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20geometry,=20search,=20SEO=20and=20F?= =?UTF-8?q?R-corpus=20sweep=20(2026-09-24=20=E2=86=92=2010-04)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Geometry - GR area-units layer: Greek PGIs draw LAU commune unions from the ΥΠΑΑΤ spec prose (containers vs members, localities as proxies, former επαρχίες, boundary lines through Wikidata/GeoNames landmarks); pins in gr/commune_overrides.json, gr/landmarks.json. - ES: compound municipios + municipio mergers tables, FEGA SIGPAC footprint reading (Sierra Sur de Jaén, Priorat, Montsant), IET Galician parroquias incl. redrawing whole-municipio MAPA zones (Monterrei). - IT comune parser: exclusion clauses, "Provincia di X:" headers, multi-word misses, ISTAT spelling folds. RO/PT/DE commune resolvers. - Detector review: outlier whitelist with cited evidence, overlap audit classes (BORDER/TIER/GENERALISATION/SOURCE-DRAWN/SUSPICIOUS), aires near-exact + alias binding (Calvados Domfrontais). - Zoom-LOD: adaptive footprint radius, bridging/water audit, lod + zoom on feedback events. - FR parcellaire: curator gap fills + audit, rows carried forward from the 2026-05-11 release (Saint-Sardos, Languedoc Montpeyroux), aires supplements (Cévennes, Côtes de Thau, Maures). FR corpus - SIQO supplements + stage-02 drop guard; promoted GIs registry (Languedoc Montpeyroux → Montpeyroux); name overrides (Marc d'Alsace Gewurztraminer). - Stage 02: homologation dates, unnumbered 2024 layout, aire table label column, proximity sentence, whole-département aires, page-cut lists, 2026 eau-de-vie template, linear DEPT_HEADER_RE. - FR wine regions replace INAO comités as the region facet. IT - MASAF in-PDF variety annex read as roster or accessory complement per Article 2 wording (Bolgheri Sassicaia, regional IGTs). Search / SEO - Punctuation fold, derived ELOT / BG search forms, BG official transliteration bracket, Wikidata region search terms + exonyms. - SSR card revealed when the app cannot render (Googlebot saw blank entity pages); map-word titles, fact-first descriptions, Latin-first GR/BG titles, IndexNow on canonical URLs only, stale entity pages pruned, language switcher keeps the appellation. - WebMCP tools behind the origin-trial token. Fixes the omnisearch gate test harness for the shared openAppellation. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 1071 +++++++- CURATOR_TODO.md | 1135 +++++++- GEOMETRY_SOURCES.md | 45 +- docs/analytics.md | 11 +- docs/deploy.md | 10 + docs/seo-geo-assessment-2026-09-26.md | 1262 +++++++++ locale/en/LC_MESSAGES/messages.po | 759 ++++-- locale/es/LC_MESSAGES/messages.po | 764 ++++-- locale/fr/LC_MESSAGES/messages.po | 720 +++-- locale/messages.pot | 678 +++-- locale/nl/LC_MESSAGES/messages.po | 762 ++++-- raw/wikipedia/grape_overrides.json | 2 + scripts/01_scrape_cahiers.py | 50 +- scripts/01d_resolve_register.py | 25 +- scripts/02_extract_cahiers.py | 626 ++++- scripts/02b_translate_grapes.py | 9 +- scripts/03_generate_wiki.py | 17 +- scripts/04_build_maps.py | 369 ++- scripts/_lib/aires.py | 158 +- scripts/_lib/aires_supplements.json | 84 + scripts/_lib/appellation_notes.json | 14 + scripts/_lib/appellation_urls.json | 100 +- scripts/_lib/appellation_urls.py | 22 +- scripts/_lib/assets/app.js | 759 +++++- scripts/_lib/augment/_shared.py | 12 + scripts/_lib/augment/it.py | 40 +- scripts/_lib/augment/ro.py | 1 + scripts/_lib/bg/geometry.py | 18 +- scripts/_lib/bg/region.py | 6 +- scripts/_lib/ch/reglement.py | 6 + scripts/_lib/content_block.py | 193 +- scripts/_lib/de/geometry.py | 82 +- scripts/_lib/de/region.py | 14 +- scripts/_lib/env.py | 15 + scripts/_lib/es/commune_list.py | 381 ++- scripts/_lib/es/compound_municipios.json | 301 +++ scripts/_lib/es/geometry.py | 516 +++- scripts/_lib/es/municipio_mergers.json | 89 + scripts/_lib/es/parroquia.py | 296 +++ scripts/_lib/es/parroquia_overrides.json | 42 + scripts/_lib/es/pliego_parcels.py | 86 +- scripts/_lib/es/sigpac.py | 193 +- scripts/_lib/es/sigpac_fega_urls.json | 28 + scripts/_lib/es/subzona.py | 69 +- scripts/_lib/fr/register_overrides.json | 10 +- scripts/_lib/fr/siqo.py | 171 ++ scripts/_lib/fr/siqo_supplements.json | 41 + scripts/_lib/fr_wine_region.py | 303 ++- scripts/_lib/geom_chain.py | 401 ++- scripts/_lib/geometry_outlier_overrides.json | 235 +- scripts/_lib/geometry_overlap_overrides.json | 235 +- scripts/_lib/gr/commune.py | 686 ++++- scripts/_lib/gr/commune_overrides.json | 488 ++++ scripts/_lib/gr/eniaio_engrafo.py | 21 + scripts/_lib/gr/geometry.py | 717 ++++- scripts/_lib/gr/landmarks.json | 50 + scripts/_lib/gr/nuts.py | 99 +- scripts/_lib/gr/region.py | 72 +- scripts/_lib/gr/specifikacija.py | 25 + scripts/_lib/grape_lexicon.py | 7 + scripts/_lib/hu/geometry.py | 10 + scripts/_lib/it/comune.py | 473 +++- scripts/_lib/it/masaf.py | 200 +- scripts/_lib/map_template.py | 639 ++++- scripts/_lib/parcellaire.py | 165 +- scripts/_lib/parcellaire_carry_forward.json | 39 + ...anguedoc-montpeyroux-2026-05-11.geojson.gz | Bin 0 -> 82527 bytes .../saint-sardos-2026-05-11.geojson.gz | Bin 0 -> 181990 bytes scripts/_lib/parcellaire_gap_fills.json | 85 + scripts/_lib/parcellaire_gaps.py | 317 +++ scripts/_lib/promoted_gis.json | 14 + scripts/_lib/pt/commune_list.py | 77 +- scripts/_lib/pt/geometry.py | 30 +- scripts/_lib/region_exonyms.json | 267 ++ scripts/_lib/region_search_terms.json | 2317 +++++++++++++++++ scripts/_lib/region_search_terms.py | 103 + scripts/_lib/ro/caiet.py | 4 +- scripts/_lib/ro/commune.py | 328 ++- scripts/_lib/ro/geometry.py | 121 +- scripts/_lib/romanise.py | 358 +++ scripts/_lib/summaries.py | 28 +- scripts/_lib/vineyard_envelope.py | 46 +- scripts/_lib/wiki.py | 1 + scripts/audit_coverage.py | 43 +- scripts/audit_geometry_outliers.py | 201 +- scripts/audit_geometry_overlaps.py | 146 +- scripts/audit_parcellaire_gaps.py | 181 ++ scripts/audit_vineyard_envelopes.py | 24 +- scripts/be/02_extract_pliegos.py | 1 - scripts/bg/02_extract_pliegos.py | 14 +- scripts/ch/02_extract_reglements.py | 14 +- scripts/cy/02_extract_pliegos.py | 6 +- scripts/cz/02_extract_pliegos.py | 1 - scripts/de/02_extract_pliegos.py | 1 - scripts/deploy.py | 78 +- scripts/es/00_fetch_data.py | 293 ++- scripts/extract_parcellaire_carry_forward.py | 62 + scripts/gr/02_extract_pliegos.py | 6 +- scripts/hr/02_extract_pliegos.py | 1 - scripts/hu/02_extract_pliegos.py | 1 - scripts/it/02f_extract_masaf.py | 18 +- scripts/refresh_region_search_terms.py | 697 +++++ scripts/rerun_terroir_facts.py | 22 +- scripts/ro/02_extract_pliegos.py | 62 +- scripts/ro/02f_extract_national_specs.py | 1 + scripts/si/02_extract_pliegos.py | 1 - scripts/sk/02_extract_pliegos.py | 1 - .../gr_eniaio_engrafo_2024_makedonia.html | 2 + .../it_masaf_allegato_register_table.txt | 34 + .../fixtures/it_masaf_allegato_sassicaia.txt | 29 + tests/test_aires_lookup.py | 66 + tests/test_aires_supplements.py | 84 + tests/test_bg_parser.py | 44 + tests/test_de_parser.py | 82 + tests/test_deploy_envs.py | 58 + tests/test_entity_jsonld.py | 358 ++- tests/test_es_commune_matching.py | 784 ++++++ tests/test_es_parroquias.py | 611 +++++ tests/test_es_pliego_parcels.py | 445 ++++ tests/test_fr_cahier_parser.py | 368 +++ tests/test_fr_register_match.py | 4 +- tests/test_fr_siqo_supplements.py | 165 ++ tests/test_fr_wine_region.py | 105 + tests/test_geometry_outliers.py | 385 +++ tests/test_geometry_overlaps.py | 66 + tests/test_gr_area_units.py | 262 ++ tests/test_gr_parser.py | 364 ++- tests/test_grape_translate_sources.py | 46 + tests/test_it_parser.py | 471 ++++ tests/test_parcellaire_carry_forward.py | 111 + tests/test_parcellaire_gap_fills.py | 113 + tests/test_promoted_gis.py | 135 + tests/test_pt_parser.py | 109 + tests/test_region_scan_wiring.py | 47 + tests/test_region_search_terms.py | 243 ++ tests/test_ro_parser.py | 444 +++- tests/test_romanise.py | 183 ++ tests/test_search_forms_wiring.py | 52 + tests/test_vineyard_envelope.py | 33 +- 139 files changed, 26753 insertions(+), 2448 deletions(-) create mode 100644 docs/seo-geo-assessment-2026-09-26.md create mode 100644 scripts/_lib/aires_supplements.json create mode 100644 scripts/_lib/es/compound_municipios.json create mode 100644 scripts/_lib/es/municipio_mergers.json create mode 100644 scripts/_lib/es/parroquia.py create mode 100644 scripts/_lib/es/parroquia_overrides.json create mode 100644 scripts/_lib/es/sigpac_fega_urls.json create mode 100644 scripts/_lib/fr/siqo.py create mode 100644 scripts/_lib/fr/siqo_supplements.json create mode 100644 scripts/_lib/gr/commune_overrides.json create mode 100644 scripts/_lib/gr/landmarks.json create mode 100644 scripts/_lib/parcellaire_carry_forward.json create mode 100644 scripts/_lib/parcellaire_carry_forward/languedoc-montpeyroux-2026-05-11.geojson.gz create mode 100644 scripts/_lib/parcellaire_carry_forward/saint-sardos-2026-05-11.geojson.gz create mode 100644 scripts/_lib/parcellaire_gap_fills.json create mode 100644 scripts/_lib/parcellaire_gaps.py create mode 100644 scripts/_lib/promoted_gis.json create mode 100644 scripts/_lib/region_exonyms.json create mode 100644 scripts/_lib/region_search_terms.json create mode 100644 scripts/_lib/region_search_terms.py create mode 100644 scripts/_lib/romanise.py create mode 100644 scripts/audit_parcellaire_gaps.py create mode 100644 scripts/extract_parcellaire_carry_forward.py create mode 100644 scripts/refresh_region_search_terms.py create mode 100644 tests/fixtures/it_masaf_allegato_register_table.txt create mode 100644 tests/fixtures/it_masaf_allegato_sassicaia.txt create mode 100644 tests/test_aires_lookup.py create mode 100644 tests/test_aires_supplements.py create mode 100644 tests/test_es_commune_matching.py create mode 100644 tests/test_es_parroquias.py create mode 100644 tests/test_es_pliego_parcels.py create mode 100644 tests/test_fr_siqo_supplements.py create mode 100644 tests/test_fr_wine_region.py create mode 100644 tests/test_geometry_outliers.py create mode 100644 tests/test_geometry_overlaps.py create mode 100644 tests/test_gr_area_units.py create mode 100644 tests/test_grape_translate_sources.py create mode 100644 tests/test_parcellaire_carry_forward.py create mode 100644 tests/test_parcellaire_gap_fills.py create mode 100644 tests/test_promoted_gis.py create mode 100644 tests/test_region_scan_wiring.py create mode 100644 tests/test_region_search_terms.py create mode 100644 tests/test_romanise.py create mode 100644 tests/test_search_forms_wiring.py diff --git a/CLAUDE.md b/CLAUDE.md index aeebc82..c1e5217 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -289,7 +289,10 @@ details and "Hard rules" for invariants that apply to every country. (`cahier_source_sha` prefixed `stale:`, written through the backup) and then chains `02d --batch` per country (parallel) → `02d_verify --batch` → `02e --batch` per country → `02e_verify --batch` → the audit, logs - under `/tmp/owm-/`. + under `/tmp/owm-/`. A scoped slug with no cache yet (a new + appellation) is placed by its extracted record's `country` so + `--scoped-02d` still reaches it (before 2026-10-04 it was logged and + dropped from the pass-down). - **The claim-support gate is part of the pipeline (review 2026-09-12, R2 / R3 / R7 / R8).** Stage 02d's coverage test only proves the *quote* exists; [scripts/02d_verify_terroir_facts.py](scripts/02d_verify_terroir_facts.py) @@ -446,6 +449,119 @@ otherwise protects working resolutions from INAO outages. The audit's FR name guard (`audit_terroir_facts.py`, strict) flags a lien that never names its appellation, so a recurrence cannot go unnoticed. +## Stage 02: homologation dates, the unnumbered 2024 layout, the aire table, the 2026 eau-de-vie template + +Stage-02 rules added 2026-10-04 — the first two while binding the +Montpeyroux cahier, the rest while closing the Saumur / Côtes du Lot / Marc +d'Alsace items of the same day: + +- **`homologation_date(segment, before)`** reads "homologué par arrêté du …" + (no article — 150 cahiers), INAO's own typo "homologué pa l'arrêté" + (Montpeyroux), "du 1er septembre", and, when the segment's head carries + no date, the 600 characters *before* the segment — the BO Agri cover line + "Cahier des charges de l'AOC « X » homologué par … du DATE" sits above the + title the bundle splitter cuts at. 583 → 1,477 dated records; the same + function dates the rescue index, so a newer cahier of the same name now + wins the rescue (Saint-Joseph 2011 → 2024). The record's `source. + homologated_at` feeds the panel's "homologué le" line. +- **`extract_unnumbered_sections`**: the 2024 Saumur cahier prints the XII + section titles as bare centred lines (pdftotext never sees the "I. -" the + PDF draws as list numbering), so the Roman splitter found nothing and the + record was silently served the superseded 2019 cahier through the rescue. + When the Roman read yields < 3 sections and the canonical titles match ≥ 6 + whole lines, the titles are numbered by their canonical position; routing + is unchanged. A normal cahier matches one such line at most, so the gate + never fires on it. +- **The aire table** (`strip_table_label_column`): some 266 cahiers print + section IV as a table — a label column ("Vins tranquilles blancs et / + rosés", "Dénomination géographique complémentaire « Puy-Notre-Dame »") + beside the commune column — and pdftotext -layout interleaves the label + words with the commune lines of the same row, so Saumur read + "Doué-en-Anjou (… de Doué-la-Vins tranquilles blancs et Fontaine …)" and + listed every commune once per wine type (9,213 duplicate tokens over the + corpus). When the section carries the table header ("COULEUR DES VINS" / + "TYPE DE VIN" / "DÉNOMINATION(S) GÉOGRAPHIQUE(S)" … "COMMUNES"), each row + block is cut at the commune column — where the block's "Département de + X :" lines start, else the indent most of its indented lines share — a + margin line keeping only what it has at that column past a space, and + `by_dept` lists a commune once per département. A block qualifies as a + row when it is mostly indented cell lines, or when its indented + "Département" lines all carry the colon of a cell ("Département de + l'Hérault : Cabrières.") beside a line that has both a label and cell + text; a centred "Département de l'Aude" header over a list at the margin + (Saint-Chinian's proximity zone) is neither and is left alone. Nothing is + cut in a cahier without the header. Hyphenated line breaks are joined + after the cut ("Saône-et-\nLoire" had been a département "Saône-et"). +- **The proximity sentence** (`_PROX_SENTENCE_RE`): where section IV has no + numbered sub-blocks (the IGP template) the text from "La zone de + proximité immédiate définie par dérogation …" on is the proximity zone, + never the aire — Côtes du Lot's six out-of-département *cantons* + (Fumel, Tournon-d'Agenais; Lauzerte, Molières, Montaigu-de-Quercy, + Montpezat-de-Quercy) were its aire until then, and some forty IGPs + (Gard, Aude, Pays d'Hérault, Cité de Carcassonne, Haute-Marne, + Île-de-France …) carried their neighbours' communes as their own. The + CSV row already drew the right polygon; the record's aire and the wiki did + not say so. +- **A whole département** (`whole_departements` → `aire_departements`): + "La récolte des raisins, la vinification et l'élaboration des vins … sont + réalisées dans le département du Lot" / "sur le territoire du département + de la Haute-Marne" / "sur la totalité du territoire du département de la + Drôme" / "dans toutes les communes du département des Bouches-du-Rhône" / + "l'ensemble des communes des départements de l'Aude, de l'Hérault, du Gard + et des Pyrénées-Orientales" name no commune. The sentence is anchored, the + window after it is read item by item (article dropped, list ended by the + first item that is not a département — "à l'exclusion des communes de …", + "ainsi que sur les communes suivantes …", "correspondant aux communes") and + every item must be a whole name of the département table (so + "Lot-et-Garonne", "Territoire de Belfort" and an article-less "Hautes-Alpes, + Alpes-Maritimes, Var" come out right). A sentence-form commune the same + text names for such a département is a DGC's zone (Rocamadour) and is + dropped; a list-form one (an exclusion list, a DGC's) stays on the record + and the wiki shows the département whole in its place. A storage rule + ("Le stockage … doivent être réalisés dans les départements du Haut-Rhin et + du Bas-Rhin") is not the récolte and never matches. +- **A list cut by the page**: the BO Agri running header after a comma + ("Houssen,\n\fPublié au BO Agri …\n\nHunawihr") folds into the list + (Marc d'Alsace's Haut-Rhin read 17 of 53 communes; the 51 Alsace grands + crus' proximity zone 45 of 79), and a "Département de X :" header whose + list starts after a blank line ("de la Meuse :\n\nBilly-sous-les-Côtes, …"; + "de l'Ardèche : 96 communes\n\nAlboussière, …") takes the next paragraph + when it reads as a list — mostly capitalised, comma-separated tokens — the + count token dropped. +- **The 2026 eau-de-vie template** (`is_spiritueux_2026_template`, + `extract_spiritueux_2026_sections`): Marc d'Alsace Gewurztraminer's cahier + of the arrêté du 2 septembre 2026 opens "Chapitre Ier : Conditions de + production et lien à l'origine" with Arabic "N. - Title" sections — Nom, + Description de la boisson spiritueuse, Définition de la zone géographique + concernée, Description de la méthode d'obtention, Lien à l'origine + géographique, Règles de présentation — then Chapitre II (declarations) and + Chapitre III, whose control-plan rows "A. - RÈGLES STRUCTURELLES / B. - + RÈGLES ANNUELLES / C. – PRODUIT" are letter headers: the EU documento-único + branch took them (A/B/C present) and the record came out with no aire, no + lien and no grape. The branch is tested after the Partie-I spirits + template and before the Roman splitter; it slices Chapitre Ier only, on + headers *with* the dash ("1. Description des facteurs du lien au terroir" + inside section 5 is a sub-item), routes by title keyword (the Partie-I + table plus `encepagement` → the méthode d'obtention, narrowed to its + "Matière première" sub-block), and the record is `kind: EDV`. A grape- + derived spirit (`is_grape_spirit`: marc, eau-de-vie de vin) with that + block gets its grapes parsed — gewurztraminer Rs — where every other EDV + record keeps none; styles stay empty. +- **`DEPT_HEADER_RE` is linear now**: its "up to four lines before the + colon" group ended each line on an optional newline, so a header with no + colon (Saint-Chinian's centred "Département de l'Aude" over a margin list) + was tried every way four lines can be split — 40 s on a 4 KB section, and + the minutes-long JO-issue case of the register shadow report. Each line + now ends on its newline. A département name no longer spans a line + ("Département de l'Hérault\nAutignac, …" had read "Hérault Autignac"), a + sub-item "b) Pour la dénomination …" ends a list, and a list token that + runs into a sentence keeps its first sentence. +- **The record name follows the referentiel, not the fetch cache.** Stage + 02 overlays each manifest entry's `name` with the `appellation` the + referentiel gives through `siqo.siqo_rows()` (`[name]` log line per + change), so a supplement, a pinned name or a renamed row does not wait for + a stage-01 re-run — see "The FR corpus follows INAO's catalogue". + ## eAmbrosia register — second source for the FR cahier BO Agri is the FR pipeline's primary cahier source, and its long tail is @@ -459,6 +575,20 @@ It is wired as a **self-service fallback tier**, not a replacement: BO Agri and the curator overrides still resolve first, so every currently-canonical resolution stays canonical. +**BO Agri answers French IPs only** (since at least 2026-09-20; verified +2026-10-01 — `info.agriculture.gouv.fr`, 185.24.186.182, connects from Paris +and times out from BE / DE / NL / CH / ES, VPN or not). Stage 01 from outside +France therefore stalls on every download and, because of the +has-usable-cahier guard, changes nothing; run it through a French VPN +endpoint. The resolve-only part (INAO product pages, `www2.inao.gouv.fr`) and +the register tier are not fenced. INAO's product page can also lag INAO's own +*Textes JO* index by months (Crémant de Bordeaux, Mâcon, Côte de +Nuits-Villages, L'Etoile on 2026-10-01: the index listed a 2026 arrêté the +page did not link, or the page was in PNO state with no cahier link at all), +so a drift check reads the index +(`www2.inao.gouv.fr/Textes-officiels/Textes-JO?textjo_type[keyword]=…`) and +pins the newer BO Agri URL in `manual_overrides.json` when the page lags. + The register attachment is the same document already in the build, not a different vintage: for Bâtard-Montrachet (`PDO-FR-A0571`) and IGP Puy-de-Dôme (`PGI-FR-A1211`) the extracted `lien_au_terroir` is byte-identical to the @@ -600,9 +730,10 @@ Two things the shadow report exists to surface, both live findings: instead of a standalone `CDC_*.pdf`. `extract_aire`'s department-header regex backtracks pathologically on that layout — minutes on a 2 KB section. The audit bounds it (`--extract-timeout`, default 20 s → verdict - `register-extract-timeout`); the regex itself is tracked in - [CURATOR_TODO.md](CURATOR_TODO.md) and must be fixed, with its own - corpus-wide diff, before the register can be promoted. + `register-extract-timeout`); the regex was fixed on 2026-10-04 with the + corpus-wide diff that entry asked for (the "up to four lines before the + colon" group now ends each line on its newline — see the stage-02 + section), and the Pouilly-Fumé reproducer runs in half a second. Licence / attribution: the register is the European Commission's own publication of the member state's product specification; the served PDF is the @@ -640,6 +771,182 @@ keyed by `id_denomination_geo` → `{commune_insee, lieu_dit_names: [...]}`. Run [scripts/audit_climats.py](scripts/audit_climats.py) after rerunning stage 04 to surface accept / review / reject buckets per cluster DGC. +## INAO parcellaire gaps (curator fills) + +The INAO parcel layer is not complete. A commune of an appellation's aire +géographique (the aires-communes CSV; the cahier's section IV) can carry no +parcel row at all — for any AOC, its delimitation not digitised +(Vaison-la-Romaine, Saint-Marcellin-lès-Vaison, Saint-Romain-en-Viennois, +Mollans-sur-Ouvèze in the 2026-05-11 release) — or rows for other +appellations only (Visan, Sérignan-du-Comtat and Sorgues hold Côtes du +Rhône parcels and no Villages row). The dissolved `parcellaire` polygon then +has a hole where the appellation's best-known villages are: a visitor +flagged the Côtes du Rhône Villages boundary at z8.8 on 2026-09-29, with +Vaison-la-Romaine on bare basemap at the eastern edge of the footprint. + +The layer does not say which of the two it means, and for a regional +appellation a vineless aire commune is genuinely not on the wine map +(Bourgogne: 45 of 315; the Alsace grand cru CSV rows list all 47 wine +communes for every climat; the Corton and Givry climat rows list three +communes for a one-commune cru), so **nothing is filled automatically and no +corpus-wide card line is rendered**. Two mechanisms instead +([scripts/_lib/parcellaire_gaps.py](scripts/_lib/parcellaire_gaps.py)): + +- [scripts/audit_parcellaire_gaps.py](scripts/audit_parcellaire_gaps.py) + lists, per parcellaire record (parents by appellation name, DGCs by + `id_denomination_geo`), the aire communes without a parcel row, each + classed `not-digitised` (no row for any AOC) / `other-aoc` (rows for other + appellations only), marked `dgc-named` when a DGC aire of the same + appellation lists it, with the pre-merger CSV codes IGN no longer carries + reported apart (Coteaux-sur-Loire, Valloire-sur-Cisse — not gaps). The + coverage index is the shapefile's attribute table (patched like the + polygons), cached under `raw/cache/parcellaire-coverage/.json`. + 167 records carried a gap on the first run; the list is a curator queue, + not a defect count. +- [scripts/_lib/parcellaire_gap_fills.json](scripts/_lib/parcellaire_gap_fills.json) + (checked in) pins the communes a curator verified against the cahier: + `communes` {INSEE: name}, `donors` (appellation slugs tried in order), + `reason`, `source`, `verified`. Stage 04 fills each pinned commune with a + donor's parcels clipped to the commune **only where the coverage index + says the donor has a row there** — a geometric intersection alone cuts a + boundary sliver from the neighbouring commune's parcels and calls it the + vineyard (Vaison against Villedieu's Côtes du Rhône parcels) — else with + the whole commune polygon (IGN AdminExpress, as `aires-csv` records draw). + A donor's parcels are a superset of the missing delimitation (the Villages + parcels lie inside the Côtes du Rhône ones) but the vineyard's shape + rather than the commune's. The fill runs after resolution and before the + outlier clip and the footprint, so the footprint and the villages layer + carry it; `geom_source` stays `parcellaire` / `parcellaire-dgc` (the + footprint, the audits and the paint key on it). The card discloses it — + `geom_parcel_fill` (which communes, drawn whole or from which donor) and + `geom_parcel_gaps` (a pinned record's gaps still open) in the panel + payload, one line per kind (`geom_parcel_fill_commune` / + `geom_parcel_fill_donor` / `geom_parcel_gaps` labels; `_parcel_fill_line` + in content_block.py, `parcelFillLine` in app.js). A pinned commune that + carries a parcel row again is **STALE**: nothing is filled for it, stage 04 + says so on its `[geo] parcellaire gap fill` line, and the audit's + `--strict` fails — re-verify the pin against the new release. + `tests/test_parcellaire_gap_fills.py` checks the table and that every + pinned commune is in the record's aire. + +v1 pins (2026-09-29, the Rhône cluster of the flag): Côtes du Rhône +Villages (7 communes: 426 → 538 km²), Côtes du Rhône (5, whole: 1,323 → +1,377 km²), the Vaison-la-Romaine DGC (3: 2.2 → 35 km²), Massif d'Uchaux +(2), Puyméras (2), Sainte-Cécile (1). Visan's own DGC stays `aires-csv-dgc` +(the whole commune, already disclosed). Candidates the audit surfaces for a +later pass are in [CURATOR_TODO.md](CURATOR_TODO.md). + +### Rows carried forward from an earlier release + +A release can also drop rows that nothing retires. The 2026-09-28 release +(adopted 2026-10-04: 11,773 → 11,751 rows, 357 → 356 appellations) has no +row for AOC Saint-Sardos — 23 communes, 36.4 km² in the 2026-05-11 release — +while no act in the JORF, INAO's Textes JO index or eAmbrosia touches the +appellation, and no row for the DGC Languedoc Montpeyroux (id_denom 1313), +promoted to AOC Montpeyroux before INAO shipped the new appellation's +parcels. The same rule as for the catalogue applies: a record is never +redrawn from a commune union because an export stopped listing it. The +checked-in [scripts/_lib/parcellaire_carry_forward.json](scripts/_lib/parcellaire_carry_forward.json) +names each case — `match` (`{"app": …}` for a whole appellation, +`{"id_denom": n}` for one denomination), the `release` the rows come from, +the extract `file`, the row count, the public state that makes the absence +a data state rather than a withdrawal, a verification date — and the rows +themselves are a checked-in extract under +`scripts/_lib/parcellaire_carry_forward/` (gzipped GeoJSON in EPSG:2154, +attributes and coordinates exactly as INAO published them: a 1 cm rounding +already made one Saint-Sardos polygon invalid), because data.gouv.fr serves +the latest resource only and the earlier zip can no longer be fetched. +`parcellaire.carry_forward_frames` appends them to the current release's +table in `build_aoc_polygons` (geometry) and `parcellaire_gaps.load_coverage` +(attributes) alike, so the polygon, the gap audit and the gap fills agree; +an entry whose rows are back in the current release is **STALE** — nothing +is appended, stage 04 and `audit_parcellaire_gaps.py` say so, `--strict` +fails, and `tests/test_parcellaire_carry_forward.py` checks that the current +release still lacks the rows, that each extract is exactly its entry's rows +and, with the earlier release on disk, a faithful copy of it. The card says +where the polygon comes from (`geom_parcel_carry` in the panel payload — +"Parcel delimitation from the INAO release of 2026-05-11; the release of +2026-09-28 carries no rows for this denomination"; `_parcel_fill_line` / +`parcelFillLine`). `wiki/map-data/aoc-parcels.meta.json` records the +release and the entries the two parcel caches were built from, so a cache +built from another release or before an entry was added is rebuilt rather +than silently served. `scripts/extract_parcellaire_carry_forward.py +--release 2026-05-11 --app "Saint-Sardos"` (or `--id-denom 1313`) writes an +extract from a release on disk. The same release moved Côtes de Provence la +Londe (id_denom 1839) from `aires-csv-dgc` to `parcellaire-dgc` with a null +`insee` on its four rows (`insee2011` filled): the coverage index reads a +code only from a string field, so a float NaN never becomes a commune +"nan", and a field carrying two codes ("49078,49115", a merged commune) +counts both. + +## The FR corpus follows INAO's catalogue, not the export's vintage + +The SIQO referentiel on data.gouv.fr is republished about once a year +(2025-12-31 was still the live file on 2026-10-01) while INAO recognises, +re-labels and retires products all year. Two rules, both enforced in code: + +- **A GI the export predates is added by a supplement row.** Every FR reader + of the referentiel — stage 01, 01d, 02, the coverage audit — reads it + through `siqo.siqo_rows()` ([scripts/_lib/fr/siqo.py](scripts/_lib/fr/siqo.py)), + which appends the rows of the checked-in + [scripts/_lib/fr/siqo_supplements.json](scripts/_lib/fr/siqo_supplements.json) + in the CSV's own 22 columns, so downstream nothing knows they are + supplements. A row is `added` (a recognised GI with no export row yet — + Montpeyroux, arrêté du 11 août 2026, INAO product 23354; its + `id_appellation` / `id_denomination_geo` are the provisional `p` + until the export publishes INAO's numbers, and the real `idproduit` lets + stage 01 walk the real product page) or `retained` (a row the export + dropped that the corpus keeps; skipped again when the export carries the + `idproduit` once more). Each row cites the public act and a verification + date; a row colliding with a CSV row of another appellation raises. + A provisional id is also accepted by the register-override table + (`register_overrides.json`, `file_number: ""` = verified absence until + the Commission publishes the application) and by `_SLUG_BUCKET` in + `fr_wine_region.py` when the aires CSV has no row to place the record by. +- **A record is never removed because an export stopped listing it.** A + full stage-02 run compares the previous `_index.json` with the keys this + run can emit (`dropped_denominations`) *before* it deletes anything and + refuses (exit 2, every record listed) when a previously emitted + denomination would vanish; `--allow-drop` is the explicit decision. The + denomination stays by a `retained` supplement row, and what became of it + is said by a registry with a cited act: `cancelled_gis.json` below for a + cancelled registration, `promoted_gis.json` for a name that moved up a + tier. Visible, dated, linked — never silently gone. +- **A record carries the GI's registered name when the export's catalogue + label is shorter.** `name_overrides` in the same `siqo_supplements.json` + (id_appellation → `appellation`, `_reason`, `_source`, `_verified`) renames + the row — and the parent denomination row with it — inside `siqo_rows()`, + so stage 01, 01d, 02 and the audits agree. v1 pin: 1091, whose + `appellation` column reads "Marc d'Alsace" while its own `produit` column, + the INAO product page, the EU register (PGI-FR-01836) and the cahier + ("enregistrée sous la dénomination « Marc d'Alsace Gewurztraminer » au + registre des Indications Géographiques") say *Marc d'Alsace + Gewurztraminer* — the record, slug and URL every pin and the live site + key on. The 2026-10-01 manifest refresh had re-read the column and + silently renamed the record `marc-d-alsace`; stage 02 now takes the name + from the referentiel, not the manifest. A pin whose row already reads the + pinned name (or has no row) is STALE: `siqo_rows()` says so on stderr and + `tests/test_fr_siqo_supplements.py` fails — drop it. + +## Promoted denominations stay in the corpus, marked + +A DGC that becomes an appellation of its own is not cancelled: the name is +still on older labels and in teaching material, and the record should say +where it went. [scripts/_lib/promoted_gis.json](scripts/_lib/promoted_gis.json) +is keyed by the former record's slug — `name`, `country`, `promoted_on` (the +national act's date), `successor_slug` + `successor_name` (the new +appellation's record, which must exist — via the export or a supplement +row), `national_act` + `national_url` (Légifrance ELI / INAO text page), +optional `cahier_url`. Stage 04 attaches it as `promoted` (a startup field); +the panel renders a green *Promoted* badge after the classification and a +dated line under the header linking the successor's panel, the act and the +new cahier (`promotedBadge` / `promotedLine` in app.js, +`promoted_badge_html` / `promoted_line_html` in content_block.py; the +sidebar tree and search suggestions carry the compact badge). +`tests/test_promoted_gis.py` checks the table and that no slug is in both +registries. v1 entry: Languedoc Montpeyroux → Montpeyroux (arrêté du 11 août +2026; the Languedoc cahier of the same day lists nine DGCs without it). + ## Cancelled GIs stay in the corpus, marked A GI whose registration the Commission cancels is **not** removed from the @@ -685,9 +992,13 @@ two-pass). [scripts/audit_geometry_outliers.py](scripts/audit_geometry_outliers.py) streams `wiki/map-data/appellations.geojson` (country-agnostic; tens of -MB of RAM), flags every detached part (gap > 25 km from the main body, -< 20 % of total area), reports which other appellation each part sits -inside (the triage signal — a part inside an unrelated DOC is plainly +MB of RAM), flags every detached part — parts no more than 25 km apart +form one cluster, transitively; the main body is the cluster holding the +most area, and a part outside it is an outlier when its *cluster* holds +< 20 % of the total (a larger detached cluster is a second lobe, however +many parcels it is drawn in; since 2026-09-24 — the earlier area-rank body +absorbed large detached parts and never tested them) — reports which +other appellation each part sits inside (the triage signal — a part inside an unrelated DOC is plainly mis-attributed), and buckets each finding as **CONFIRMED** / **PENDING-REBUILD** / **STALE** / **ACCEPTED** / **UNREVIEWED**. @@ -705,10 +1016,98 @@ drops a clipped part only when the override matches **exactly one** part of the resolved geometry; zero or several matches leaves the geometry untouched and logs a loud `STALE` warning — an override that no longer matches means the upstream data drifted (or was fixed) and must be -re-verified. The audit re-derives every override against the source -`EU_PDO.gpkg` on each run, so a working clip stays visible as -`CONFIRMED` and is never silently hidden. Re-run stages 04 → audit -after editing the override file. +re-verified. The audit re-derives every override against the source on +each run — the Bétard polygon for a `figshare-pdo` clip, and for a clip +whose `geom_source` is `gisco-nuts-region` (a Greek PGI drawn from NUTS +units: Gavdos off Κρήτη, the Strofades off Ζάκυνθος) the union of the +NUTS polygons stage 04 wrote on the record as `geom_nuts_ids` (panel +payload `wiki/data/d/en/.json`; a build that predates the field +reports the clip STALE until rebuilt) — so a working clip stays visible +as `CONFIRMED` and is never silently hidden. A whitelist entry cites the +same evidence: Skyros stays for ΠΓΕ Εύβοια ("Περιφερειακής Ενότητας +Ευβοίας", of which Skyros is part), Samothrace for ΠΓΕ Θράκη and ΠΓΕ +Έβρος. Ρετσίνα Ευβοίας and Ρετσίνα Χαλκίδας are drawn from LAU unions +that never held Skyros, and Ρετσίνα Καρύστου's union (the former +επαρχία Καρυστίας, which held Skyros) chains the island to the Euboean +body under the 25 km rule — none of the three needs an override. The +2026-09-25 review cleared the 57 unreviewed findings the same way, each +entry citing what makes the part legitimate: for a `parcellaire` record +the INAO delimitation *is* the source, so a lobe is whitelisted when the +cahier's aire names the département (the Yonne lobe of the six Bourgogne +regionals, the northern Rhône of Côtes du Rhône, Ripaille, the +Wissembourg outpost, Sierck, Villars-sur-Var, Bossay-sur-Claise); for an +`aires-csv` record the CSV row is quoted (the Pays de Bray communes of +Calvados and Pommeau, Les Fourgs for Marc du Jura, La Roche-Chalais for +the Cognac Bois ordinaires, the Eure-et-Loir communes of IGP +Île-de-France); NUTS-drawn Greek PGIs follow the ΥΠΑΑΤ wording — +"νομού Χανίων" (the prefecture) keeps Gavdos for Χανιά where "νήσου +Κρήτης" clips it for Κρήτη — and a national spec settles a Bétard part +(Barabás is a Bükk település per the termékleírás; Kojetice is in the +Znojemská list of Vyhláška 254/2010). Four findings were defects, fixed +upstream instead: Calvados Domfrontais drawn as the whole Calvados aire +(the aires-CSV name lookup, see below), Cinco Villas' "Los Corrales" +bound to Sevilla, Valle Belice and Costa Toscana collapsed to whole +provinces (the IT comune parser), and Πλαγιές Αίνου's mainland parts +turned out to be the Echinades islets of Δ.Κ. Αγίας Ευφημίας — GISCO's +own geometry, whitelisted. Re-run stages 04 → audit after editing the +override file. + +**INAO aires-communes binding** ([scripts/_lib/aires.py](scripts/_lib/aires.py)): +a commune-union record finds its CSV rows by normalised name — exact, +then *near-exact* (rapidfuzz ratio ≥ 95 against names of similar +length, one candidate only), then — for a SIQO "X ou Y" register name — +the exact row of one of its aliases ("Moulis ou Moulis-en-Médoc" → +"Moulis"; 2026-09-26, 22 Burgundy and Layon AOCs bound their row for the +first time), then the substring fallback that serves "Champagne grand cru" +→ "Champagne", in which a CSV name shorter than six characters binds only +as a whole word of the record name ("anjou" in "Rosé d'Anjou", never +"gard" in "Euskal Sagardoa", which drew the Basque cider over the Gard), +and in which the other direction — the record name inside a *longer* CSV +label — binds only when the record (or each of its own " ou " parts) is an +alias part of that label ("Pouilly-sur-Loire" and "Pouilly-Fumé ou Blanc +Fumé de Pouilly" in the combined CSV label "Pouilly-Fumé ou Blanc Fumé de +Pouilly et Pouilly-sur-Loire"; `_CSV_ALIAS_KEYS`, filled by `load_aires`), +never when it is a trailing word of another appellation — the new AOC +"Montpeyroux" took the DGC aire "Languedoc Montpeyroux" (2 communes against +the cahier's 4) until 2026-10-04. The same sweep bound IGP Côtes du Lot to +its IGP CSV aire "Lot" (340 communes of the Lot) where the cahier-text +parser had drawn only the 6 named communes outside the département. +The same ladder (`_resolve_key`) serves `lookup_departements`, the +département profile behind the FR wine region. The near-exact step exists because +SIQO spells "Calvados Domfontais" where the CSV, the cahier and the EU +register say "Domfrontais": the substring rule handed that record the +1,573 communes of "Calvados" (2026-09-25) for a 112-commune appellation. +`tests/test_aires_lookup.py` pins the order. + +**INAO aires-communes supplements** ([scripts/_lib/aires_supplements.json](scripts/_lib/aires_supplements.json), +checked in): the CSV rows of an aire can be a decade older than the cahier +in force, and nothing in the CSV says so. A visitor flagged the IGP Cévennes +boundary on 2026-09-29 (fr, z7.2): its 236 rows date from 14 September 2012 +and are Gard only, while the cahier published in the BO on 8 August 2024 +lists 40 Lozère communes beside them ("Le vignoble des Cévennes est situé +dans les départements du Gard et de la Lozère") — the map drew a Cévennes +without the Cévennes lozériennes. A pin per record (`aire` + `ida` — the CSV +label and bucket — `communes` {INSEE: name} the homologated cahier lists +and the CSV lacks, `not_drawn` for a commune included only in part, `reason`, +`source`, `verified`) is folded into the index by `aires.apply_supplements` +inside `load_aires()`, so stage 04, the parcellaire-gap audit and every +other consumer read the same aire; the record stays `aires-csv` and the +card's commune-union line counts the added communes. Only a cahier +published in the Bulletin officiel is pinned, never a PNOCDC draft (Thézac- +Perricard's three Lot communes wait for that reason). A pinned commune the +CSV carries again is **STALE**: it is not added twice, stage 04 says so on +its `[geo] aires supplement` line and `tests/test_aires_supplements.py` +fails (it also checks that every pinned name is a current IGN commune the +cahier's zone text names). Nothing is parsed automatically — a cahier lists +the vinification zone, exclusions and the proximity zone in the same prose +(Alpilles names 29 communes for vinification and 19 for the grapes). v1 +pins: Cévennes (+39 Lozère communes, 3,482 → 4,952 km²; Massegros Causses +Gorges is in the aire for the commune déléguée Les Vignes only and is not +drawn, which leaves Le Rozier and Saint-Pierre-des-Tripiers as a detached +lobe), Côtes de Thau (+9 communes around the Étang de Thau, cahier of 24 +August 2023 against rows of 2015; 181 → 422 km²), Maures (+3, cahier of 11 +December 2025; 2,193 → 2,313 km²). Candidates for a later pass are in +[CURATOR_TODO.md](CURATOR_TODO.md). ## Geometry-overlap audit @@ -732,19 +1131,46 @@ of one appellation) and classifies the rest by `share` = overlap area / appellation area: **NESTED** (one near-contains the other), **PARTIAL** / **WIDE** (a genuinely large or wide mutual overlap) — all normal — versus **SLIVER**: a small overlap (1–50 km², < 10 % of -*both* appellations) between otherwise-disjoint appellations. Slivers -are the suspicious bucket; cross-country slivers are listed first -(appellations of different countries should share no ground). One +*both* appellations) between otherwise-disjoint appellations. One appellation overlapping a parent and all its sub-denominations collapses to a single finding. +The first corpus-wide review (2026-09-25, 520 slivers) showed that the +double-assigned-commune premise holds for one class of pair only, so +every sliver is now **classified** (`classify()`; every class is listed +in full, nothing is hidden) and only the last class is SUSPICIOUS: +**BORDER** — two countries, a band under `--thin-km` (0.3 km) wide: the +two national layers digitise the state border differently (77 pairs, +all ≤ 0.24 km wide); **TIER** — a PGI / IGP or spirit-drink GI over a +PDO / AOC of the same country: the broader tier shares ground with the +narrower one by design (226); **GENERALISATION** — same tier, a thin +band, the two records drawn from different geometry sources (Bétard vs +a GISCO union, a geoportal zone vs a commune list — 107); +**SOURCE-DRAWN** — same tier, both polygons from an official zone layer +(regional geoportal, MAPA, INAO parcellaire) or Bétard: the overlap is +the publisher's own statement (Tuscany's DOCs share ground; Bétard pads +municipalities) and no commune list of ours can fix it (86); +**SUSPICIOUS** — same tier, at least one side drawn from a commune list +of ours, and either wide or from the same source (24 on that review). +A sub-denomination that inherits its polygon is classified through its +parent's provenance (`root_source`). `--json PATH` writes every row with +its class. + Reviewed-legitimate pairs go in [scripts/_lib/geometry_overlap_overrides.json](scripts/_lib/geometry_overlap_overrides.json) -(`whitelist` of slug pairs) and report as ACCEPTED. The audit is a +(`whitelist` of slug pairs) and report as ACCEPTED — the 24 suspicious +pairs of the 2026-09-25 review were all the regulator's own doing and are +whitelisted with the evidence in the reason: for the French pairs the +shared communes are named and sit in BOTH appellations' rows of the INAO +aires-communes CSV (Beaujolais and the Bourgogne regionals share the +eight Mâconnais–Beaujolais communes; Cheverny and Touraine share +Monthou-sur-Bièvre; Agenais and Côtes du Lot both list Fumel), the +Blaye / Bourg band is INAO parcels against an INAO commune list, and the +three Greek pairs are a LAU-vs-NUTS generalisation band. The audit is a detector only — it changes no geometry; fixing a real artifact means correcting the upstream commune list / resolver. Thresholds are -CLI-configurable (`--sliver-max`, `--max-sliver-km2`, …); `--strict` -exits non-zero on unreviewed slivers. +CLI-configurable (`--sliver-max`, `--max-sliver-km2`, `--thin-km`, …); +`--strict` exits non-zero on unreviewed SUSPICIOUS slivers only. ## Bétard-snapshot delta audit @@ -869,9 +1295,9 @@ touches many records still wants a full run. | Script | Reads | Writes | |---|---|---| | 00_fetch_data.py | (network) | raw/inao/siqo-referentiel.csv, raw/ign/communes.geojson, raw/inao/parcellaire/*.shp, raw/cadastre/lieux-dits/*.json.gz | -| 01_scrape_cahiers.py | raw/inao/siqo-referentiel.csv + raw/inao/cahiers/manual_overrides.json + raw/inao/register/resolved.json | raw/inao/cahiers/*.pdf, raw/inao/cahiers/manifest.json, raw/inao/register/{cahiers/*.pdf,manifest.json} | -| 01d_resolve_register.py | raw/inao/cahiers/manifest.json (or raw/inao/siqo-referentiel.csv) + raw/eambrosia-register/gi-index.json + scripts/_lib/fr/register_overrides.json | raw/inao/register/{resolved,unresolved}.json | -| 02_extract_cahiers.py | raw/inao/cahiers/*.pdf | raw/inao/cahier-extracted/*.json + _index.json | +| 01_scrape_cahiers.py | raw/inao/siqo-referentiel.csv + scripts/_lib/fr/siqo_supplements.json + raw/inao/cahiers/manual_overrides.json + raw/inao/register/resolved.json | raw/inao/cahiers/*.pdf, raw/inao/cahiers/manifest.json, raw/inao/register/{cahiers/*.pdf,manifest.json} | +| 01d_resolve_register.py | raw/inao/cahiers/manifest.json (or raw/inao/siqo-referentiel.csv + scripts/_lib/fr/siqo_supplements.json) + raw/eambrosia-register/gi-index.json + scripts/_lib/fr/register_overrides.json | raw/inao/register/{resolved,unresolved}.json | +| 02_extract_cahiers.py | raw/inao/cahiers/*.pdf + raw/inao/siqo-referentiel.csv + scripts/_lib/fr/siqo_supplements.json (+ the previous _index.json for the drop guard) | raw/inao/cahier-extracted/*.json + _index.json | | audit_fr_register_shadow.py | raw/inao/cahiers/manifest.json + raw/inao/cahier-extracted/ + raw/inao/register/resolved.json | raw/inao/register/{shadow-report.json,shadow-report.md,cahiers/*.pdf,manifest.json} | | 02b_fetch_grape_lexicon.py | raw/inao/cahier-extracted/*.json + raw/vivc/by-slug/ | raw/wikipedia/grapes//*.json + manifest.json | | 02b_fetch_aoc_lexicon.py | raw/inao/cahier-extracted/*.json | raw/wikipedia/aocs/fr/*.json + manifest.json | @@ -895,6 +1321,8 @@ touches many records still wants a full run. | 02i_fetch_wikidata_qids.py | raw/*/*-extracted/*.json (slug + id_eambrosia) + raw/wikipedia/aocs// + raw/wikidata/slug_overrides.json | raw/wikidata/qids-by-slug.json + p9854.json + manifest.json + slug_overrides.example.json | | 03_generate_wiki.py | raw/inao/cahier-extracted/*.json + raw/terroir-facts/ | wiki/*.md, wiki/_index.json | | audit_empty_grapes.py | wiki/data/aocs.en.*.js + wiki/data/d/en/*.json + scripts/_lib/empty_grapes_overrides.json | (stdout — FLAGGED / INHERIT / REVIEWED / STUB / NON-WINE buckets; `--strict`, `--json PATH`) | +| audit_parcellaire_gaps.py | raw/inao/parcellaire/*.shp (attribute table, cached to raw/cache/parcellaire-coverage/) + scripts/_lib/parcellaire_carry_forward.json (+ its extracts) + raw/inao/aoc-aop-aires-communes.csv + raw/ign/communes.geojson + raw/inao/cahier-extracted/*.json + scripts/_lib/parcellaire_gap_fills.json | (stdout — aire communes without a parcel row per parcellaire record, classed; gap-fill pins and carried-forward rows APPLIED / STALE; `--strict`, `--json PATH`) | +| extract_parcellaire_carry_forward.py | raw/inao/parcellaire/_delim-parcellaire-aoc-shp.shp | scripts/_lib/parcellaire_carry_forward/