From d989df0bdcfd49ebc107fe6b83097a841bcf8b8c Mon Sep 17 00:00:00 2001
From: Emmanuel Raviart <emmanuel@raviart.com>
Date: Thu, 24 Nov 2022 19:01:21 +0100
Subject: [PATCH] Add date & plural support to units

---
 package-lock.json                             |  73 +++++----
 package.json                                  |   7 +-
 plugin-yaml-patched.ts                        |  72 +++++++++
 src/lib/components/parameters/NodeEdit.svelte |   5 +-
 .../parameters/ParameterPane.svelte           |  17 --
 .../parameters/ParameterView.svelte           |  20 ++-
 .../parameters/ReferencesEdit.svelte          |   2 +-
 .../parameters/ScaleAtInstantEdit.svelte      |  17 +-
 .../components/parameters/ScaleEdit.svelte    |  10 +-
 .../parameters/ValueAtInstantEdit.svelte      |   8 +-
 .../components/parameters/ValueEdit.svelte    |   6 +-
 .../VariableReferredScaleAtInstant.svelte     |  18 ++-
 .../VariableReferredScaleParameter.svelte     |   1 +
 .../VariableReferredValueParameter.svelte     |   4 +-
 src/lib/units.ts                              |  55 ++++---
 .../parameters/[parameter]/+page.server.ts    |   3 +-
 .../parameters/[parameter]/+page.svelte       |   7 +-
 .../parameters/[parameter]/edit/+page.svelte  |  26 +++-
 .../variables/[variable]/xlsx/+page.svelte    | 146 ++++++++++++------
 vite.config.ts                                |   9 +-
 20 files changed, 341 insertions(+), 165 deletions(-)
 create mode 100644 plugin-yaml-patched.ts
 delete mode 100644 src/lib/components/parameters/ParameterPane.svelte

diff --git a/package-lock.json b/package-lock.json
index b7ff436fa..666db6018 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,9 +16,9 @@
         "@fontsource/lato": "^4.3.0",
         "@fontsource/lora": "^4.3.0",
         "@iconify/svelte": "^3.0.0",
-        "@leximpact/socio-fiscal-openfisca-json": "^0.0.83",
-        "@openfisca/json-model": "^2.0.1",
-        "@playwright/test": "^1.22.2",
+        "@leximpact/socio-fiscal-openfisca-json": "^0.1.0",
+        "@openfisca/json-model": "^3.0.0",
+        "@playwright/test": "^1.28.1",
         "@rgossiaux/svelte-headlessui": "^1.0.0-beta.12",
         "@rollup/plugin-yaml": "^4.0.1",
         "@sveltejs/adapter-node": "^1.0.0-next.101",
@@ -28,6 +28,7 @@
         "@tricoteuses/legal-explorer": "^0.0.1",
         "@types/cookie": "^0.5.0",
         "@types/fs-extra": "^9.0.11",
+        "@types/js-yaml": "^4.0.5",
         "@types/uuid": "^8.3.4",
         "@typescript-eslint/eslint-plugin": "^5.0.0",
         "@typescript-eslint/parser": "^5.0.0",
@@ -2059,12 +2060,12 @@
       }
     },
     "node_modules/@leximpact/socio-fiscal-openfisca-json": {
-      "version": "0.0.83",
-      "resolved": "https://registry.npmjs.org/@leximpact/socio-fiscal-openfisca-json/-/socio-fiscal-openfisca-json-0.0.83.tgz",
-      "integrity": "sha512-pwa2kfnVqBCARC/F8EJzBeYSJJCTyX2TaQdplcbIIUC+srJnid+KA0YYpDFsYIih9m0a1nmM+Vzl58G2GkIkpA==",
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@leximpact/socio-fiscal-openfisca-json/-/socio-fiscal-openfisca-json-0.1.0.tgz",
+      "integrity": "sha512-2q03f4k/1w1+GiEGL0BftLkgSBeSMhhJCx0GV5VLJXvRQoDv7JK9d2JDi/8cINcw6OIKiJ8IRz2B2KsKpohqDg==",
       "dev": true,
       "peerDependencies": {
-        "@openfisca/json-model": "^2.0.1"
+        "@openfisca/json-model": "^3.0.0"
       },
       "peerDependenciesMeta": {
         "@openfisca/json-model": {
@@ -2108,9 +2109,9 @@
       }
     },
     "node_modules/@openfisca/json-model": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@openfisca/json-model/-/json-model-2.0.1.tgz",
-      "integrity": "sha512-tpPNFww71HW/xI31raU9+QeFU1UuWgDWLkizy0N4kgv3bp7lUVKGngtp+qT4BocOwobPdFZgQUo7jYKGuFOunA==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@openfisca/json-model/-/json-model-3.0.0.tgz",
+      "integrity": "sha512-Cix5anqn0APIIF0CGq7WmfxKrwMGqmibaTfMQNq3KX6+vAtiTcyiwZcIT0n3oKPQn44DNBPHD0e5qV9LkhX+yw==",
       "dev": true,
       "dependencies": {
         "@auditors/core": "^0.3.0",
@@ -2123,13 +2124,13 @@
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.28.0",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
-      "integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
+      "version": "1.28.1",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.1.tgz",
+      "integrity": "sha512-xN6spdqrNlwSn9KabIhqfZR7IWjPpFK1835tFNgjrlysaSezuX8PYUwaz38V/yI8TJLG9PkAMEXoHRXYXlpTPQ==",
       "dev": true,
       "dependencies": {
         "@types/node": "*",
-        "playwright-core": "1.28.0"
+        "playwright-core": "1.28.1"
       },
       "bin": {
         "playwright": "cli.js"
@@ -2386,6 +2387,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/js-yaml": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
+      "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
+      "dev": true
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.11",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -5550,9 +5557,9 @@
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.28.0",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
-      "integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
+      "version": "1.28.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.1.tgz",
+      "integrity": "sha512-3PixLnGPno0E8rSBJjtwqTwJe3Yw72QwBBBxNoukIj3lEeBNXwbNiKrNuB1oyQgTBw5QHUhNO3SteEtHaMK6ag==",
       "dev": true,
       "bin": {
         "playwright": "cli.js"
@@ -8451,9 +8458,9 @@
       }
     },
     "@leximpact/socio-fiscal-openfisca-json": {
-      "version": "0.0.83",
-      "resolved": "https://registry.npmjs.org/@leximpact/socio-fiscal-openfisca-json/-/socio-fiscal-openfisca-json-0.0.83.tgz",
-      "integrity": "sha512-pwa2kfnVqBCARC/F8EJzBeYSJJCTyX2TaQdplcbIIUC+srJnid+KA0YYpDFsYIih9m0a1nmM+Vzl58G2GkIkpA==",
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@leximpact/socio-fiscal-openfisca-json/-/socio-fiscal-openfisca-json-0.1.0.tgz",
+      "integrity": "sha512-2q03f4k/1w1+GiEGL0BftLkgSBeSMhhJCx0GV5VLJXvRQoDv7JK9d2JDi/8cINcw6OIKiJ8IRz2B2KsKpohqDg==",
       "dev": true,
       "requires": {}
     },
@@ -8484,9 +8491,9 @@
       }
     },
     "@openfisca/json-model": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@openfisca/json-model/-/json-model-2.0.1.tgz",
-      "integrity": "sha512-tpPNFww71HW/xI31raU9+QeFU1UuWgDWLkizy0N4kgv3bp7lUVKGngtp+qT4BocOwobPdFZgQUo7jYKGuFOunA==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@openfisca/json-model/-/json-model-3.0.0.tgz",
+      "integrity": "sha512-Cix5anqn0APIIF0CGq7WmfxKrwMGqmibaTfMQNq3KX6+vAtiTcyiwZcIT0n3oKPQn44DNBPHD0e5qV9LkhX+yw==",
       "dev": true,
       "requires": {
         "@auditors/core": "^0.3.0",
@@ -8496,13 +8503,13 @@
       }
     },
     "@playwright/test": {
-      "version": "1.28.0",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
-      "integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
+      "version": "1.28.1",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.1.tgz",
+      "integrity": "sha512-xN6spdqrNlwSn9KabIhqfZR7IWjPpFK1835tFNgjrlysaSezuX8PYUwaz38V/yI8TJLG9PkAMEXoHRXYXlpTPQ==",
       "dev": true,
       "requires": {
         "@types/node": "*",
-        "playwright-core": "1.28.0"
+        "playwright-core": "1.28.1"
       }
     },
     "@polka/url": {
@@ -8669,6 +8676,12 @@
         "@types/node": "*"
       }
     },
+    "@types/js-yaml": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
+      "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
+      "dev": true
+    },
     "@types/json-schema": {
       "version": "7.0.11",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -10912,9 +10925,9 @@
       }
     },
     "playwright-core": {
-      "version": "1.28.0",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
-      "integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
+      "version": "1.28.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.1.tgz",
+      "integrity": "sha512-3PixLnGPno0E8rSBJjtwqTwJe3Yw72QwBBBxNoukIj3lEeBNXwbNiKrNuB1oyQgTBw5QHUhNO3SteEtHaMK6ag==",
       "dev": true
     },
     "postcss": {
diff --git a/package.json b/package.json
index c55eca35e..568728088 100644
--- a/package.json
+++ b/package.json
@@ -20,9 +20,9 @@
     "@fontsource/lato": "^4.3.0",
     "@fontsource/lora": "^4.3.0",
     "@iconify/svelte": "^3.0.0",
-    "@leximpact/socio-fiscal-openfisca-json": "^0.0.83",
-    "@openfisca/json-model": "^2.0.1",
-    "@playwright/test": "^1.22.2",
+    "@leximpact/socio-fiscal-openfisca-json": "^0.1.0",
+    "@openfisca/json-model": "^3.0.0",
+    "@playwright/test": "^1.28.1",
     "@rgossiaux/svelte-headlessui": "^1.0.0-beta.12",
     "@rollup/plugin-yaml": "^4.0.1",
     "@sveltejs/adapter-node": "^1.0.0-next.101",
@@ -32,6 +32,7 @@
     "@tricoteuses/legal-explorer": "^0.0.1",
     "@types/cookie": "^0.5.0",
     "@types/fs-extra": "^9.0.11",
+    "@types/js-yaml": "^4.0.5",
     "@types/uuid": "^8.3.4",
     "@typescript-eslint/eslint-plugin": "^5.0.0",
     "@typescript-eslint/parser": "^5.0.0",
diff --git a/plugin-yaml-patched.ts b/plugin-yaml-patched.ts
new file mode 100644
index 000000000..97d13837b
--- /dev/null
+++ b/plugin-yaml-patched.ts
@@ -0,0 +1,72 @@
+/// Code taken from https://github.com/rollup/plugins/blob/master/packages/yaml/src/index.js
+
+import YAML, { type LoadOptions } from "js-yaml"
+import toSource from "tosource"
+import {
+  createFilter,
+  makeLegalIdentifier,
+  type FilterPattern,
+} from "@rollup/pluginutils"
+import type { PluginOption } from "vite"
+
+interface Options extends LoadOptions {
+  documentMode?: "multi" | "single"
+  exclude?: FilterPattern | undefined
+  include?: FilterPattern | undefined
+  transform?: ((data: unknown, id: string) => unknown) | null
+}
+const defaults: Options = {
+  documentMode: "single",
+  transform: null,
+}
+const ext = /\.ya?ml$/
+
+export default function yaml(opts: Options = {}): PluginOption {
+  const options = Object.assign({}, defaults, opts)
+  const { documentMode } = options
+  const filter = createFilter(options.include, options.exclude)
+  let loadMethod: (str: string, opts?: LoadOptions) => unknown
+
+  if (documentMode === "single") {
+    loadMethod = YAML.load
+  } else if (documentMode === "multi") {
+    loadMethod = YAML.loadAll as (str: string, opts?: LoadOptions) => unknown
+  } else {
+    this.error(
+      `plugin-yaml → documentMode: '${documentMode}' is not a valid value. Please choose 'single' or 'multi'`,
+    )
+  }
+
+  return {
+    name: "yaml",
+
+    transform(content: string, id: string) {
+      if (!ext.test(id)) return null
+      if (!filter(id)) return null
+
+      // The pach is here: it adds `options` argument.
+      let data = loadMethod(content, options)
+
+      if (typeof options.transform === "function") {
+        const result = options.transform(data, id)
+        // eslint-disable-next-line no-undefined
+        if (result !== undefined) {
+          data = result
+        }
+      }
+
+      const keys = Object.keys(data).filter(
+        (key) => key === makeLegalIdentifier(key),
+      )
+      const code = `var data = ${toSource(data)};\n\n`
+      const exports = ["export default data;"]
+        .concat(keys.map((key) => `export var ${key} = data.${key};`))
+        .join("\n")
+
+      return {
+        code: code + exports,
+        map: { mappings: "" },
+      }
+    },
+  }
+}
diff --git a/src/lib/components/parameters/NodeEdit.svelte b/src/lib/components/parameters/NodeEdit.svelte
index e4232c378..3dae483bf 100644
--- a/src/lib/components/parameters/NodeEdit.svelte
+++ b/src/lib/components/parameters/NodeEdit.svelte
@@ -4,8 +4,9 @@
 
   import ReferencesEdit from "$lib/components/parameters/ReferencesEdit.svelte"
   import { errorAsKeyValueDictionary, iterArrayWithErrors } from "$lib/errors"
-  import { labelFromUnitName, units } from "$lib/units"
+  import { getUnitLabel, units } from "$lib/units"
 
+  export let date: string
   let globalErrors: { [key: string]: unknown }
   export { globalErrors as errors }
   export let parameter: NodeParameter
@@ -228,7 +229,7 @@
           <option selected value={undefined}>Non précisée</option>
         {/if}
         {#each units as unit}
-          <option value={unit.name}>{labelFromUnitName(unit.name)}</option>
+          <option value={unit.name}>{getUnitLabel(unit.name, date)}</option>
         {/each}
       </select>
       {#if showErrors && errors.unit !== undefined}
diff --git a/src/lib/components/parameters/ParameterPane.svelte b/src/lib/components/parameters/ParameterPane.svelte
deleted file mode 100644
index 9edf42392..000000000
--- a/src/lib/components/parameters/ParameterPane.svelte
+++ /dev/null
@@ -1,17 +0,0 @@
-<script lang="ts">
-  import ParameterView from "$lib/components/parameters/ParameterView.svelte"
-  import { getParameter, rootParameter } from "$lib/parameters"
-
-  export let name: string
-  // export let pane: string
-
-  $: parameter = getParameter(rootParameter, name)
-
-  $: if (parameter === undefined) {
-    console.error(`Parameter "${name}" not found`)
-  }
-</script>
-
-{#if parameter !== undefined}
-  <ParameterView {parameter} />
-{/if}
diff --git a/src/lib/components/parameters/ParameterView.svelte b/src/lib/components/parameters/ParameterView.svelte
index bd9746267..2070a5f9c 100644
--- a/src/lib/components/parameters/ParameterView.svelte
+++ b/src/lib/components/parameters/ParameterView.svelte
@@ -18,9 +18,10 @@
     labelFromScaleType,
     labelFromValueType,
   } from "$lib/parameters"
-  import { shortLabelFromUnitName } from "$lib/units"
+  import { getUnitShortLabel } from "$lib/units"
   import type { SelfTargetAProps } from "$lib/urls"
 
+  export let date: string
   export let parameter: Parameter
 
   const dateFormatter = new Intl.DateTimeFormat("fr-FR", { dateStyle: "full" })
@@ -191,9 +192,10 @@
                           : valueAtInstant.value ?? ""}</td
                       >
                       <td class="border p-1 text-center"
-                        >{shortLabelFromUnitName(
+                        >{getUnitShortLabel(
                           valueAtInstant.unit ?? parameter.unit,
-                        ) ?? ""}</td
+                          date,
+                        )}</td
                       >
                     {/if}
                     {#if valueAtInstant !== "expected"}
@@ -232,7 +234,7 @@
               <div class="font-base my-1 flex border-b py-1 ">
                 <p class="mr-1">
                   Unité du paramètre&nbsp;:: <span class="font-bold">
-                    {shortLabelFromUnitName(parameter.unit)}</span
+                    {getUnitShortLabel(parameter.unit, date)}</span
                   >
                 </p>
               </div>
@@ -247,7 +249,7 @@
               {#if parameter.threshold_unit !== undefined}
                 <p class="font-base my-1 mr-1 flex py-1">
                   Unité de seuil&nbsp;:&nbsp;<span class="font-bold">
-                    {shortLabelFromUnitName(parameter.threshold_unit)}</span
+                    {getUnitShortLabel(parameter.threshold_unit, date)}</span
                   >
                 </p>
               {/if}
@@ -255,8 +257,9 @@
                 {#if asAmountScaleParameter(parameter).amount_unit !== undefined}
                   <p class="font-base my-1 mr-1 flex py-1">
                     Unité de montant&nbsp;:&nbsp; <span class="font-bold">
-                      {shortLabelFromUnitName(
+                      {getUnitShortLabel(
                         asAmountScaleParameter(parameter).amount_unit,
+                        date,
                       )}</span
                     >
                   </p>
@@ -264,8 +267,9 @@
               {:else if asRateScaleParameter(parameter).rate_unit !== undefined}
                 <p class="font-base my-1 mr-1 flex py-1">
                   Unité de taux&nbsp;:&nbsp;<span class="font-bold">
-                    {shortLabelFromUnitName(
+                    {getUnitShortLabel(
                       asRateScaleParameter(parameter).rate_unit,
+                      date,
                     )}</span
                   >
                 </p>
@@ -281,7 +285,7 @@
               <div class="font-base my-1 flex py-1">
                 <p class="mr-1">
                   Unité de la valeur&nbsp;:&nbsp;<span class="font-bold"
-                    >{shortLabelFromUnitName(parameter.unit)}</span
+                    >{getUnitShortLabel(parameter.unit, date)}</span
                   >
                 </p>
               </div>
diff --git a/src/lib/components/parameters/ReferencesEdit.svelte b/src/lib/components/parameters/ReferencesEdit.svelte
index 3c066e52a..f3a76c3cb 100644
--- a/src/lib/components/parameters/ReferencesEdit.svelte
+++ b/src/lib/components/parameters/ReferencesEdit.svelte
@@ -18,7 +18,7 @@
     dispatch("change", references)
   }
 
-  function changeReference(index, { detail }: CustomEvent) {
+  function changeReference(index: number, { detail }: CustomEvent) {
     const reference = detail as Reference
     references = [...references]
     references[index] = reference
diff --git a/src/lib/components/parameters/ScaleAtInstantEdit.svelte b/src/lib/components/parameters/ScaleAtInstantEdit.svelte
index 463c6a6b1..8bc9b6945 100644
--- a/src/lib/components/parameters/ScaleAtInstantEdit.svelte
+++ b/src/lib/components/parameters/ScaleAtInstantEdit.svelte
@@ -21,8 +21,9 @@
     asRateBracketAtInstant,
     asRateScaleParameter,
   } from "$lib/parameters"
-  import { shortLabelFromUnitName } from "$lib/units"
+  import { getUnitShortLabel } from "$lib/units"
 
+  export let date: string
   let globalErrors: { [key: string]: unknown }
   export { globalErrors as errors }
   export let parameter: ScaleParameter
@@ -181,7 +182,7 @@
                   )}
                 />
                 <span class="font-serif text-sm text-black">
-                  {shortLabelFromUnitName(parameter.threshold_unit) ?? ""}
+                  {getUnitShortLabel(parameter.threshold_unit, date)}
                 </span>
                 {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).threshold).value !== undefined}
                   <p>
@@ -212,9 +213,10 @@
                     )}
                   />
                   <span class="font-serif text-base">
-                    {shortLabelFromUnitName(
+                    {getUnitShortLabel(
                       asAmountScaleParameter(parameter).amount_unit,
-                    ) ?? ""}
+                      date,
+                    )}
                   </span>
                   {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).amount).value !== undefined}
                     <p>
@@ -249,7 +251,7 @@
                     />
                     <span class="font-serif text-base">
                       <!-- TODO: Should be parameter.base_unit. -->
-                      {shortLabelFromUnitName(parameter.threshold_unit) ?? ""}
+                      {getUnitShortLabel(parameter.threshold_unit, date)}
                     </span>
                     {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).base).value !== undefined}
                       <p>
@@ -280,9 +282,10 @@
                     )}
                   />
                   <span class="font-serif text-base">
-                    {shortLabelFromUnitName(
+                    {getUnitShortLabel(
                       asRateScaleParameter(parameter).rate_unit,
-                    ) ?? ""}
+                      date,
+                    )}
                   </span>
                   {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).rate).value !== undefined}
                     <p>
diff --git a/src/lib/components/parameters/ScaleEdit.svelte b/src/lib/components/parameters/ScaleEdit.svelte
index 07896e686..b7c39d6ab 100644
--- a/src/lib/components/parameters/ScaleEdit.svelte
+++ b/src/lib/components/parameters/ScaleEdit.svelte
@@ -21,8 +21,9 @@
     buildInstantReferencesAndScaleArray,
     labelFromScaleType,
   } from "$lib/parameters"
-  import { labelFromUnitName, units } from "$lib/units"
+  import { getUnitLabel, units } from "$lib/units"
 
+  export let date: string
   let globalErrors: { [key: string]: unknown }
   export { globalErrors as errors }
   export let parameter: ScaleParameter
@@ -193,6 +194,7 @@
               <div class="mb-4 p-2 text-xs text-gray-500">
                 {#if scaleAtInstant !== undefined}
                   <ScaleAtInstantEdit
+                    {date}
                     errors={errorAsKeyValueDictionary(error?.[1])}
                     on:change={(event) => changeScaleAtInstant(index, event)}
                     {parameter}
@@ -301,7 +303,7 @@
             <option selected value={undefined}>Non précisée</option>
           {/if}
           {#each units as unit}
-            <option value={unit.name}>{labelFromUnitName(unit.name)}</option>
+            <option value={unit.name}>{getUnitLabel(unit.name, date)}</option>
           {/each}
         </select>
         {#if showErrors && errors.amount_unit !== undefined}
@@ -321,7 +323,7 @@
           <option selected value={undefined}>Non précisée</option>
         {/if}
         {#each units as unit}
-          <option value={unit.name}>{labelFromUnitName(unit.name)}</option>
+          <option value={unit.name}>{getUnitLabel(unit.name, date)}</option>
         {/each}
       </select>
       {#if showErrors && errors.rate_unit !== undefined}
@@ -378,7 +380,7 @@
           <option selected value={undefined}>Non précisée</option>
         {/if}
         {#each units as unit}
-          <option value={unit.name}>{labelFromUnitName(unit.name)}</option>
+          <option value={unit.name}>{getUnitLabel(unit.name, date)}</option>
         {/each}
       </select>
       {#if showErrors && errors.threshold_unit !== undefined}
diff --git a/src/lib/components/parameters/ValueAtInstantEdit.svelte b/src/lib/components/parameters/ValueAtInstantEdit.svelte
index da89dcfac..e3a341ce2 100644
--- a/src/lib/components/parameters/ValueAtInstantEdit.svelte
+++ b/src/lib/components/parameters/ValueAtInstantEdit.svelte
@@ -19,8 +19,9 @@
   import { createEventDispatcher } from "svelte"
 
   import { auditEditedAttribute } from "$lib/errors"
-  import { labelFromUnitName, shortLabelFromUnitName, units } from "$lib/units"
+  import { getUnitLabel, units } from "$lib/units"
 
+  export let date: string
   let globalErrors: { [key: string]: unknown }
   export { globalErrors as errors }
   export let parameter: ValueParameter
@@ -156,14 +157,15 @@
         value={asMaybeNumberValue(valueAtInstant).unit}
       >
         <option value={undefined}>
-          {#if parameter.unit == null}Non précisée{:else}{shortLabelFromUnitName(
+          {#if parameter.unit == null}Non précisée{:else}{getUnitLabel(
               parameter.unit,
+              date,
             )}
           {/if}</option
         >
         {#each units as unit}
           {#if unit.name !== parameter.unit}
-            <option value={unit.name}>{labelFromUnitName(unit.name)}</option>
+            <option value={unit.name}>{getUnitLabel(unit.name, date)}</option>
           {/if}
         {/each}
       </select>
diff --git a/src/lib/components/parameters/ValueEdit.svelte b/src/lib/components/parameters/ValueEdit.svelte
index a5e1f3ae5..bbe0fbc07 100644
--- a/src/lib/components/parameters/ValueEdit.svelte
+++ b/src/lib/components/parameters/ValueEdit.svelte
@@ -15,8 +15,9 @@
     buildInstantReferencesAndValueArray,
     labelFromValueType,
   } from "$lib/parameters"
-  import { labelFromUnitName, units } from "$lib/units"
+  import { getUnitLabel, units } from "$lib/units"
 
+  export let date: string
   let globalErrors: { [key: string]: unknown }
   export { globalErrors as errors }
   export let parameter: ValueParameter
@@ -172,6 +173,7 @@
               <div class="mb-4 p-2 text-xs text-gray-500">
                 {#if valueAtInstant !== undefined}
                   <ValueAtInstantEdit
+                    {date}
                     errors={errorAsKeyValueDictionary(error?.[1])}
                     on:change={(event) => changeValueAtInstant(index, event)}
                     {parameter}
@@ -298,7 +300,7 @@
           <option selected value={undefined}>Non précisée</option>
         {/if}
         {#each units as unit}
-          <option value={unit.name}>{labelFromUnitName(unit.name)}</option>
+          <option value={unit.name}>{getUnitLabel(unit.name, date)}</option>
         {/each}
       </select>
       {#if showErrors && errors.unit !== undefined}
diff --git a/src/lib/components/variables/VariableReferredScaleAtInstant.svelte b/src/lib/components/variables/VariableReferredScaleAtInstant.svelte
index 7249b159e..17ac04779 100644
--- a/src/lib/components/variables/VariableReferredScaleAtInstant.svelte
+++ b/src/lib/components/variables/VariableReferredScaleAtInstant.svelte
@@ -23,12 +23,13 @@
     asRateBracketAtInstantOrNullable,
     asRateScaleParameter,
   } from "$lib/parameters"
-  import { shortLabelFromUnitName } from "$lib/units"
+  import { getUnitShortLabel } from "$lib/units"
 
   import VariableReferredValueEdit from "./VariableReferredValueEdit.svelte"
 
   export let billParameter: ScaleParameter
   export let billScaleAtInstant: ScaleAtInstant | null
+  export let date: string
   let globalErrors: { [key: string]: unknown }
   export { globalErrors as errors }
   export let lawScaleAtInstant: ScaleAtInstant | undefined | null
@@ -171,7 +172,7 @@
                     : bracketAtInstant.threshold?.value ?? null}
                 />
                 <span class="ml-1 text-base">
-                  {shortLabelFromUnitName(billParameter.threshold_unit) ?? ""}
+                  {getUnitShortLabel(billParameter.threshold_unit, date)}
                 </span>
                 {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).threshold).value !== undefined}
                   <p>
@@ -203,9 +204,10 @@
                         ).value ?? null}
                   />
                   <span class="text-base">
-                    {shortLabelFromUnitName(
+                    {getUnitShortLabel(
                       asAmountScaleParameter(billParameter).amount_unit,
-                    ) ?? ""}
+                      date,
+                    )}
                   </span>
                   {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).amount).value !== undefined}
                     <p>
@@ -240,8 +242,7 @@
                     />
                     <span class="ml-1 text-base">
                       <!-- TODO: Should be parameter.base_unit. -->
-                      {shortLabelFromUnitName(billParameter.threshold_unit) ??
-                        ""}
+                      {getUnitShortLabel(billParameter.threshold_unit, date)}
                     </span>
                     {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).base).value !== undefined}
                       <p>
@@ -274,9 +275,10 @@
                   />
                   <span class="ml-1 text-base">
                     <!-- TODO: Should be parameter.base_unit. -->
-                    {shortLabelFromUnitName(
+                    {getUnitShortLabel(
                       asRateScaleParameter(billParameter).rate_unit,
-                    ) ?? ""}
+                      date,
+                    )}
                   </span>
                   {#if showErrors && errorAsKeyValueDictionary(errorAsKeyValueDictionary(errorsAtIndex).rate).value !== undefined}
                     <p>
diff --git a/src/lib/components/variables/VariableReferredScaleParameter.svelte b/src/lib/components/variables/VariableReferredScaleParameter.svelte
index 0b7e07efa..230494d9c 100644
--- a/src/lib/components/variables/VariableReferredScaleParameter.svelte
+++ b/src/lib/components/variables/VariableReferredScaleParameter.svelte
@@ -123,6 +123,7 @@
     <VariableReferredScaleAtInstant
       {billParameter}
       {billScaleAtInstant}
+      {date}
       errors={{}}
       {lawScaleAtInstant}
       on:change={changeScale}
diff --git a/src/lib/components/variables/VariableReferredValueParameter.svelte b/src/lib/components/variables/VariableReferredValueParameter.svelte
index 5e52ca09d..320a3835a 100644
--- a/src/lib/components/variables/VariableReferredValueParameter.svelte
+++ b/src/lib/components/variables/VariableReferredValueParameter.svelte
@@ -15,7 +15,7 @@
   import ArticleModal from "$lib/components/parameters/ArticleModal.svelte"
   import type { ParametricReform, ValueParameterReform } from "$lib/reforms"
   import { ParameterReformChangeType } from "$lib/reforms"
-  import { shortLabelFromUnitName } from "$lib/units"
+  import { getUnitShortLabel } from "$lib/units"
 
   import VariableReferredParameterHeader from "./VariableReferredParameterHeader.svelte"
   import VariableReferredValueEdit from "./VariableReferredValueEdit.svelte"
@@ -140,7 +140,7 @@
         />
       </div>
       <span class="ml-1 text-base">
-        {shortLabelFromUnitName(billParameter.unit) ?? ""}
+        {getUnitShortLabel(billParameter.unit, date)}
       </span>
 
       {#if valueError !== null}<p class="text-red-500">{valueError}</p>{/if}
diff --git a/src/lib/units.ts b/src/lib/units.ts
index 1998dd755..ec3a87fc4 100644
--- a/src/lib/units.ts
+++ b/src/lib/units.ts
@@ -1,33 +1,38 @@
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 // @ts-ignore
 import unitsUnknown from "@leximpact/socio-fiscal-openfisca-json/units.yaml"
-import type { UnitsMetadata } from "@openfisca/json-model"
+import {
+  getUnitLabel as getUnitLabelOriginal,
+  getUnitShortLabel as getUnitShortLabelOriginal,
+  type Unit,
+} from "@openfisca/json-model"
 
-export function labelFromUnitName(unitName: string | undefined | null): string {
-  if (unitName == null) {
-    return ""
-  }
-  const unit = unitByName[unitName]
-  if (unit === undefined) {
-    return unitName
-  }
-  return unit.label ?? unitName
-}
+const frenchPluralRules = new Intl.PluralRules(["fr-FR"])
+export const units = unitsUnknown as Unit[]
+const unitByName = Object.fromEntries(units.map((unit) => [unit.name, unit]))
 
-export function shortLabelFromUnitName(
-  unitName: string | undefined | null,
+export function getUnitLabel(
+  name: string | undefined | null,
+  date: string,
+  value = 123,
 ): string {
-  if (unitName == null) {
-    return ""
-  }
-  const unit = unitByName[unitName]
-  if (unit === undefined) {
-    return unitName
-  }
-  return unit.short_label ?? unit.label ?? unitName
+  return getUnitLabelOriginal(
+    unitByName,
+    name,
+    date,
+    frenchPluralRules.select(value ?? 0),
+  )
 }
 
-export const units = unitsUnknown as UnitsMetadata
-export const unitByName = Object.fromEntries(
-  units.map((unit) => [unit.name, unit]),
-)
+export function getUnitShortLabel(
+  name: string | undefined | null,
+  date: string,
+  value = 123,
+): string {
+  return getUnitShortLabelOriginal(
+    unitByName,
+    name,
+    date,
+    frenchPluralRules.select(value ?? 0),
+  )
+}
diff --git a/src/routes/parameters/[parameter]/+page.server.ts b/src/routes/parameters/[parameter]/+page.server.ts
index f4b37282b..5124df099 100644
--- a/src/routes/parameters/[parameter]/+page.server.ts
+++ b/src/routes/parameters/[parameter]/+page.server.ts
@@ -11,6 +11,7 @@ import { randomBytes } from "crypto"
 
 import { getParameter, rootParameter } from "$lib/parameters"
 import config from "$lib/server/config"
+import { units } from "$lib/units"
 
 import type { Action } from "./$types"
 
@@ -29,7 +30,7 @@ export const PUT: Action = async ({ request, params }) => {
   if (parameter === undefined) {
     throw error(404, `Paramètre ${name} non trouvé`)
   }
-  const [validParameter, parameterError] = auditEditableParameter(
+  const [validParameter, parameterError] = auditEditableParameter(units)(
     strictAudit,
     await request.json(),
   )
diff --git a/src/routes/parameters/[parameter]/+page.svelte b/src/routes/parameters/[parameter]/+page.svelte
index 219faef14..5726f239b 100644
--- a/src/routes/parameters/[parameter]/+page.svelte
+++ b/src/routes/parameters/[parameter]/+page.svelte
@@ -1,5 +1,6 @@
 <script lang="ts">
-  import { setContext } from "svelte"
+  import { getContext, setContext } from "svelte"
+  import type { Writable } from "svelte/store"
 
   import ParameterView from "$lib/components/parameters/ParameterView.svelte"
   import { newSelfTargetAProps } from "$lib/urls"
@@ -8,6 +9,8 @@
 
   export let data: PageData
 
+  const date = getContext("date") as Writable<string>
+
   $: ({ parameter } = data)
 
   setContext("newSelfTargetAProps", newSelfTargetAProps)
@@ -17,4 +20,4 @@
   <title>{parameter.name} | Paramètres | {data.title}</title>
 </svelte:head>
 
-<ParameterView {parameter} />
+<ParameterView date={$date} {parameter} />
diff --git a/src/routes/parameters/[parameter]/edit/+page.svelte b/src/routes/parameters/[parameter]/edit/+page.svelte
index da99cd0e2..910bd5f3d 100644
--- a/src/routes/parameters/[parameter]/edit/+page.svelte
+++ b/src/routes/parameters/[parameter]/edit/+page.svelte
@@ -10,18 +10,22 @@
     convertEditableParameterToRaw,
     yamlFromRawParameter,
   } from "@openfisca/json-model"
+  import { getContext } from "svelte"
+  import type { Writable } from "svelte/store"
 
   import { goto } from "$app/navigation"
   import NodeEdit from "$lib/components/parameters/NodeEdit.svelte"
   import ScaleEdit from "$lib/components/parameters/ScaleEdit.svelte"
   import ValueEdit from "$lib/components/parameters/ValueEdit.svelte"
   import { labelFromParameterClass } from "$lib/parameters"
+  import { units } from "$lib/units"
   import { newSelfTargetAProps } from "$lib/urls"
 
   import type { PageData } from "./$types"
 
   export let data: PageData
 
+  const date = getContext("date") as Writable<string>
   let { parameter, processedParameter } = data
 
   let errors: { [key: string]: unknown } = {}
@@ -54,7 +58,7 @@
   }
 
   $: ((parameter) => {
-    const [validParameter, parameterError] = auditEditableParameter(
+    const [validParameter, parameterError] = auditEditableParameter(units)(
       laxAudit,
       parameter,
     )
@@ -64,7 +68,7 @@
   })(parameter)
 
   async function save() {
-    const [validParameter, parameterError] = auditEditableParameter(
+    const [validParameter, parameterError] = auditEditableParameter(units)(
       laxAudit,
       parameter,
     )
@@ -241,11 +245,23 @@
           </div>
 
           {#if parameter.class === ParameterClass.Node}
-            <NodeEdit {errors} bind:parameter {showErrors} />
+            <NodeEdit date={$date} {errors} bind:parameter {showErrors} />
           {:else if parameter.class === ParameterClass.Scale}
-            <ScaleEdit {errors} bind:parameter bind:reviewed {showErrors} />
+            <ScaleEdit
+              date={$date}
+              {errors}
+              bind:parameter
+              bind:reviewed
+              {showErrors}
+            />
           {:else if parameter.class === ParameterClass.Value}
-            <ValueEdit {errors} bind:parameter bind:reviewed {showErrors} />
+            <ValueEdit
+              date={$date}
+              {errors}
+              bind:parameter
+              bind:reviewed
+              {showErrors}
+            />
           {/if}
 
           {#if parameter.referring_variables !== undefined}
diff --git a/src/routes/variables/[variable]/xlsx/+page.svelte b/src/routes/variables/[variable]/xlsx/+page.svelte
index 4f91b36ca..ef0220a59 100644
--- a/src/routes/variables/[variable]/xlsx/+page.svelte
+++ b/src/routes/variables/[variable]/xlsx/+page.svelte
@@ -163,7 +163,7 @@
       // const token = crypto.randomUUID()
       const token = uuidV4()
       calculationByName.law = { running: true, token }
-      webSocketByName.law.send(
+      webSocketByName?.law.send(
         JSON.stringify({
           ...message,
           situation: aggregatedSituation,
@@ -242,6 +242,7 @@
       WBProps: { date1904: true },
     }
 
+    // Collect infos on persons & groups.
     const columnIndexByEntityKey: { [key: string]: number } = {}
     const groupInfosByIdByEntityKeyBySituationIndex: {
       [situationIndex: number]: {
@@ -250,15 +251,20 @@
         }
       }
     } = {}
+    const maxPersonsCountByGroupEntityKey: {
+      [groupEntityKey: string]: number
+    } = {}
     const personIndexByIdBySituationIndex: {
       [situationIndex: number]: {
         [personId: string]: number
       }
     } = {}
-    const refByName: { [name: string]: string } = {}
-    const roleByGroupEntityKeyByPersonIdBySituationIndex: {
+    // const refByName: { [name: string]: string } = {}
+    const personInfosByGroupEntityKeyByPersonIdBySituationIndex: {
       [situationIndex: number]: {
-        [personId: string]: { [groupEntityKey: string]: string }
+        [personId: string]: {
+          [groupEntityKey: string]: { groupId: string; role: string }
+        }
       }
     } = {}
     const rowIndexByEntityKey: { [entityKey: string]: number } = {}
@@ -273,11 +279,13 @@
         groupInfosByIdByEntityKey
       const personIndexById: { [personId: string]: number } = {}
       personIndexByIdBySituationIndex[situationIndex] = personIndexById
-      const roleByGroupEntityKeyByPersonId: {
-        [personId: string]: { [groupEntityKey: string]: string }
+      const personInfosByGroupEntityKeyByPersonId: {
+        [personId: string]: {
+          [groupEntityKey: string]: { groupId: string; role: string }
+        }
       } = {}
-      roleByGroupEntityKeyByPersonIdBySituationIndex[situationIndex] =
-        roleByGroupEntityKeyByPersonId
+      personInfosByGroupEntityKeyByPersonIdBySituationIndex[situationIndex] =
+        personInfosByGroupEntityKeyByPersonId
       for (const [entityKey, entity] of Object.entries(entityByKey)) {
         const groupInfosById: {
           [groupId: string]: { groupIndex: number; personsId: string[] }
@@ -306,25 +314,34 @@
 
                 const flattenedRole = flattenedRolesGenerator.next()
                   .value as RoleBase
-                let roleByGroupEntityKey =
-                  roleByGroupEntityKeyByPersonId[personId]
-                if (roleByGroupEntityKey === undefined) {
-                  roleByGroupEntityKey = roleByGroupEntityKeyByPersonId[
-                    personId
-                  ] = {}
+                let personInfosByGroupEntityKey =
+                  personInfosByGroupEntityKeyByPersonId[personId]
+                if (personInfosByGroupEntityKey === undefined) {
+                  personInfosByGroupEntityKey =
+                    personInfosByGroupEntityKeyByPersonId[personId] = {}
+                }
+                personInfosByGroupEntityKey[entityKey] = {
+                  groupId: populationId,
+                  role: flattenedRole.key,
                 }
-                roleByGroupEntityKey[entityKey] = flattenedRole.key
               }
             }
             groupInfosById[populationId] = {
               groupIndex: rowIndex,
               personsId,
             }
+            const personsCount = personsId.length
+            if (
+              personsCount > (maxPersonsCountByGroupEntityKey[entityKey] ?? 0)
+            ) {
+              maxPersonsCountByGroupEntityKey[entityKey] = personsCount
+            }
           }
         }
       }
     }
 
+    // Generate tableByEntityKey.
     const variableByName = Object.fromEntries(
       variables.map((variable) => [variable.name, variable]),
     )
@@ -347,9 +364,17 @@
             entityByKey,
           ).sort(([key1], [key2]) => key1.localeCompare(key2))) {
             if (!groupEntity.is_person) {
-              header.push(`role_dans_${groupEntityKey}`)
+              header.push(groupEntityKey, `role_dans_${groupEntityKey}`)
             }
           }
+        } else {
+          for (
+            let i = 1;
+            i <= (maxPersonsCountByGroupEntityKey[entityKey] ?? 0);
+            i++
+          ) {
+            header.push(`membre_${i}_${entityKey}`)
+          }
         }
         table = tableByEntityKey[entityKey] = [header]
         for (const [situationIndex, situation] of $testCases.entries()) {
@@ -361,8 +386,10 @@
             groupInfosByIdByEntityKeyBySituationIndex[situationIndex]
           const personIndexById =
             personIndexByIdBySituationIndex[situationIndex]
-          const roleByGroupEntityKeyByPersonId =
-            roleByGroupEntityKeyByPersonIdBySituationIndex[situationIndex]
+          const personInfosByGroupEntityKeyByPersonId =
+            personInfosByGroupEntityKeyByPersonIdBySituationIndex[
+              situationIndex
+            ]
           for (const [populationIndex, populationId] of Object.keys(
             entitySituation,
           )
@@ -380,35 +407,68 @@
               populationId,
             ]
             if (entity.is_person) {
-              const personIndex = personIndexById[populationId]
-              refByName[uid] = `${entityKey}!$${personIndex + 1}:$${
-                personIndex + 1
-              }`
+              // const personIndex = personIndexById[populationId]
+              const personInfosByGroupEntityKey =
+                personInfosByGroupEntityKeyByPersonId[populationId]
+              // refByName[uid] = `${entityKey}!$${personIndex + 1}:$${
+              //   personIndex + 1
+              // }`
               for (const [groupEntityKey, groupEntity] of Object.entries(
                 entityByKey,
               ).sort(([key1], [key2]) => key1.localeCompare(key2))) {
                 if (!groupEntity.is_person) {
+                  const personInfos =
+                    personInfosByGroupEntityKey[groupEntityKey]
+                  const groupInfos =
+                    groupInfosByIdByEntityKey[groupEntityKey][
+                      personInfos.groupId
+                    ]
                   row.push(
-                    roleByGroupEntityKeyByPersonId[populationId][
-                      groupEntityKey
-                    ],
+                    {
+                      f: `ROW(${groupEntityKey}!$${
+                        groupInfos.groupIndex + 1
+                      }:$${groupInfos.groupIndex + 1})`,
+                      t: "n",
+                      v: groupInfos.groupIndex + 1,
+                    },
+                    personInfos.role,
                   )
                 }
               }
             } else {
               const groupInfos =
                 groupInfosByIdByEntityKey[entityKey][populationId]
-              refByName[uid] = `${entityKey}!$${groupInfos.groupIndex + 1}:$${
-                groupInfos.groupIndex + 1
-              }`
-              refByName[`${uid}_membres`] = groupInfos.personsId
-                .map((personId) => {
+              for (
+                let i = 0;
+                i < (maxPersonsCountByGroupEntityKey[entityKey] ?? 0);
+                i++
+              ) {
+                const personId = groupInfos.personsId[i]
+                if (personId === undefined) {
+                  row.push(undefined)
+                } else {
                   const personIndex = personIndexById[personId]
-                  return `${personEntityKey}!$${personIndex + 1}:$${
-                    personIndex + 1
-                  }`
-                })
-                .join(":")
+                  // Note: A formula is used to allow value update when rows are inserted or deleted.
+                  row.push({
+                    f: `ROW(${personEntityKey}!$${personIndex + 1}:$${
+                      personIndex + 1
+                    })`,
+                    t: "n",
+                    v: personIndex + 1,
+                  })
+                }
+              }
+              // refByName[uid] = `${entityKey}!$${groupInfos.groupIndex + 1}:$${
+              //   groupInfos.groupIndex + 1
+              // }`
+              // refByName[`${uid}_membres`] = groupInfos.personsId
+              //   .map((personId) => {
+              //     const personIndex = personIndexById[personId]
+              //     return `${personEntityKey}!$${personIndex + 1}:$${
+              //       personIndex + 1
+              //     }`
+              //   })
+              //   .join(":")
             }
             table.push(row)
           }
@@ -560,14 +620,14 @@
     }
 
     // Add rows-related named ranges.
-    for (const [name, ref] of Object.entries(refByName)) {
-      workbook.Workbook.Names.push({
-        // Comment:
-        Name: name,
-        Ref: ref,
-        Sheet: null,
-      })
-    }
+    // for (const [name, ref] of Object.entries(refByName)) {
+    //   workbook.Workbook.Names.push({
+    //     // Comment:
+    //     Name: name,
+    //     Ref: ref,
+    //     Sheet: null,
+    //   })
+    // }
 
     XLSX.writeFile(
       workbook,
diff --git a/vite.config.ts b/vite.config.ts
index ca0833bae..387d5b333 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,6 +1,9 @@
 import { sveltekit } from "@sveltejs/kit/vite"
 import type { UserConfig } from "vite"
-import yaml from "@rollup/plugin-yaml"
+import yaml from "js-yaml"
+// import yamlPlugin from "@rollup/plugin-yaml"
+
+import yamlPlugin from "./plugin-yaml-patched"
 
 const config: UserConfig = {
   build: {
@@ -14,7 +17,9 @@ const config: UserConfig = {
     exclude: ["svelte-modals"],
   },
   plugins: [
-    yaml(), // To import YAML files
+    yamlPlugin({
+      schema: yaml.JSON_SCHEMA, // Keep dates as strings.
+    }), // To import YAML files
     sveltekit(),
   ],
   ssr: {
-- 
GitLab