fix(api): les uids du catalogue sont en anglais, pas des slugs français

reference-seed-data.ts reste rédigé en français (c'est juste le libellé
d'autoring, jamais stocké/exposé), mais la clé stable (`Diet.key`/
`Category.key`/`Ingredient.key`) qu'on en dérive doit elle-même être un
identifiant anglais, indépendant de la langue d'autoring — pas juste le
même texte français passé à slugify().

- apps/api/src/db/catalog-en-keys.ts : dictionnaire écrit à la main
  (label français -> clé anglaise) pour les 5 régimes, 14 allergènes et
  437 ingrédients ; getEnglishKey() lève une erreur explicite si un
  nouvel élément n'a pas encore d'entrée plutôt que de retomber sur un
  slug français silencieux.
- scripts/validate-catalog-en-keys.ts : vérifie que chaque diet/allergène/
  ingrédient de reference-seed-data.ts a une entrée, et que les clés
  anglaises résultantes sont uniques (437/437, 14/14, 5/5 — zéro manquant,
  zéro collision).
- reference-seed-data.ts et scripts/generate-catalog-i18n.ts utilisent
  désormais getEnglishKey() au lieu de slugify(nom français).
- Nouvelle migration (20260818193000_catalog_keys_to_english) qui
  remappe les lignes déjà seedées avec un slug français (par la migration
  précédente) vers leur clé anglaise définitive.
- apps/web/src/locales/fr/translation.json régénéré : catalog.* est
  maintenant indexé par clé anglaise ("vegetarian", "eggs",
  "ground_beef"...), toujours avec le libellé français en valeur.
- Tests/step-definitions mis à jour (getEnglishKey() au lieu de
  slugify()) ; 102 tests Mocha + 32 scénarios Cucumber passent contre la
  base migrée. Vérifié aussi en direct via GET /reference/diets et
  /reference/allergies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Nicolas 2026-08-18 20:03:55 +02:00
parent 1d9bb6d112
commit 4b85531601
13 changed files with 1522 additions and 501 deletions

2
.gitignore vendored
View file

@ -150,5 +150,3 @@ tmp-mockups/
# IA
.claude/
# Scratch output of apps/api/scripts/generate-catalog-i18n.ts — regenerate on demand.
apps/api/scripts/backfill.sql

View file

@ -1,7 +1,7 @@
import assert from "node:assert/strict";
import { Then, When } from "@cucumber/cucumber";
import { prisma } from "../../src/db/prisma.js";
import { slugify } from "../../src/utils/slugify.js";
import { getEnglishKey } from "../../src/db/catalog-en-keys.js";
import type { CustomWorld } from "../support/world.js";
/** Splits a comma-separated list of names from a `.feature` string into trimmed, non-empty parts. */
@ -22,7 +22,7 @@ function splitNames(names: string): string[] {
async function allergyIdsFor(names: string[]): Promise<number[]> {
const allergies = await prisma.allergy.findMany({ include: { category: true } });
return names.map((name) => {
const key = slugify(name);
const key = getEnglishKey(name);
const match = allergies.find((allergy) => allergy.category.key === key);
if (!match) throw new Error(`No seeded allergen named "${name}"`);
return match.id;
@ -30,14 +30,14 @@ async function allergyIdsFor(names: string[]): Promise<number[]> {
}
When("I set my regime to {string}", async function (this: CustomWorld, dietName: string) {
const diet = await prisma.diet.findFirstOrThrow({ where: { key: slugify(dietName) } });
const diet = await prisma.diet.findFirstOrThrow({ where: { key: getEnglishKey(dietName) } });
this.response = await this.agent.patch("/profile/diet").send({ dietId: diet.id });
});
Then(
"my profile's regime should be {string}",
async function (this: CustomWorld, dietName: string) {
const diet = await prisma.diet.findFirstOrThrow({ where: { key: slugify(dietName) } });
const diet = await prisma.diet.findFirstOrThrow({ where: { key: getEnglishKey(dietName) } });
assert.equal(this.response.body.dietId, diet.id);
},
);

View file

@ -1,7 +1,7 @@
import assert from "node:assert/strict";
import { Given, Then, When } from "@cucumber/cucumber";
import { prisma } from "../../src/db/prisma.js";
import { slugify } from "../../src/utils/slugify.js";
import { getEnglishKey } from "../../src/db/catalog-en-keys.js";
import { TEST_REFERENCE_DATE } from "../../test-support/reference-date.js";
import type { CustomWorld } from "../support/world.js";
@ -12,7 +12,7 @@ import type { CustomWorld } from "../support/world.js";
* matching.
*/
async function findIngredientId(name: string): Promise<number> {
const ingredient = await prisma.ingredient.findFirstOrThrow({ where: { key: slugify(name) } });
const ingredient = await prisma.ingredient.findFirstOrThrow({ where: { key: getEnglishKey(name) } });
return ingredient.id;
}
@ -64,7 +64,7 @@ Then(
ingredients: Array<{ ingredient: { key: string } }>;
steps: Array<{ description: string }>;
};
const expectedKey = slugify(ingredientName);
const expectedKey = getEnglishKey(ingredientName);
assert.ok(body.ingredients.some((line) => line.ingredient.key === expectedKey));
assert.ok(body.steps.some((s) => s.description === step));
},

View file

@ -1,6 +1,6 @@
import assert from "node:assert/strict";
import { Then } from "@cucumber/cucumber";
import { slugify } from "../../src/utils/slugify.js";
import { getEnglishKey } from "../../src/db/catalog-en-keys.js";
import type { CustomWorld } from "../support/world.js";
// The feature file still names an item by its French label, for
@ -11,7 +11,7 @@ Then(
"the reference list response should include {string}",
function (this: CustomWorld, name: string) {
const keys = (this.response.body as Array<{ key: string }>).map((item) => item.key);
const expectedKey = slugify(name);
const expectedKey = getEnglishKey(name);
assert.ok(keys.includes(expectedKey), `expected ${JSON.stringify(keys)} to include "${expectedKey}"`);
},
);

View file

@ -0,0 +1,465 @@
-- Auto-generated once by scripts/gen-english-remap-sql.ts — do not re-run,
-- do not hand-edit. Remaps every Diet/Category(allergen)/Ingredient row's
-- "key" from the French slug the previous migration
-- (20260818_catalog_labels_to_keys) produced to the hand-assigned English
-- key in catalog-en-keys.ts.
UPDATE "diet" SET "key" = 'omnivore' WHERE "key" = 'omnivore';
UPDATE "diet" SET "key" = 'vegetarian' WHERE "key" = 'vegetarien';
UPDATE "diet" SET "key" = 'vegan' WHERE "key" = 'vegan';
UPDATE "diet" SET "key" = 'pescatarian' WHERE "key" = 'pescetarien';
UPDATE "diet" SET "key" = 'gluten_free' WHERE "key" = 'sans_gluten';
UPDATE "category" SET "key" = 'gluten' WHERE "key" = 'gluten';
UPDATE "category" SET "key" = 'crustaceans' WHERE "key" = 'crustaces';
UPDATE "category" SET "key" = 'eggs' WHERE "key" = 'oeufs';
UPDATE "category" SET "key" = 'fish' WHERE "key" = 'poissons';
UPDATE "category" SET "key" = 'peanuts' WHERE "key" = 'arachides';
UPDATE "category" SET "key" = 'soy' WHERE "key" = 'soja';
UPDATE "category" SET "key" = 'milk' WHERE "key" = 'lait';
UPDATE "category" SET "key" = 'tree_nuts' WHERE "key" = 'fruits_a_coque';
UPDATE "category" SET "key" = 'celery' WHERE "key" = 'celeri';
UPDATE "category" SET "key" = 'mustard' WHERE "key" = 'moutarde';
UPDATE "category" SET "key" = 'sesame_seeds' WHERE "key" = 'graines_de_sesame';
UPDATE "category" SET "key" = 'sulfites' WHERE "key" = 'sulfites';
UPDATE "category" SET "key" = 'lupin' WHERE "key" = 'lupin';
UPDATE "category" SET "key" = 'molluscs' WHERE "key" = 'mollusques';
UPDATE "ingredients" SET "key" = 'tomato' WHERE "key" = 'tomate';
UPDATE "ingredients" SET "key" = 'onion' WHERE "key" = 'oignon';
UPDATE "ingredients" SET "key" = 'shallot' WHERE "key" = 'echalote';
UPDATE "ingredients" SET "key" = 'garlic' WHERE "key" = 'ail';
UPDATE "ingredients" SET "key" = 'carrot' WHERE "key" = 'carotte';
UPDATE "ingredients" SET "key" = 'zucchini' WHERE "key" = 'courgette';
UPDATE "ingredients" SET "key" = 'cucumber' WHERE "key" = 'concombre';
UPDATE "ingredients" SET "key" = 'gherkins' WHERE "key" = 'cornichons';
UPDATE "ingredients" SET "key" = 'bell_pepper' WHERE "key" = 'poivron';
UPDATE "ingredients" SET "key" = 'mushroom' WHERE "key" = 'champignon';
UPDATE "ingredients" SET "key" = 'porcini' WHERE "key" = 'cepes';
UPDATE "ingredients" SET "key" = 'eggplant' WHERE "key" = 'aubergine';
UPDATE "ingredients" SET "key" = 'broccoli' WHERE "key" = 'brocoli';
UPDATE "ingredients" SET "key" = 'cauliflower' WHERE "key" = 'chou_fleur';
UPDATE "ingredients" SET "key" = 'white_cabbage' WHERE "key" = 'chou_blanc';
UPDATE "ingredients" SET "key" = 'red_cabbage' WHERE "key" = 'chou_rouge';
UPDATE "ingredients" SET "key" = 'brussels_sprouts' WHERE "key" = 'chou_de_bruxelles';
UPDATE "ingredients" SET "key" = 'spinach' WHERE "key" = 'epinard';
UPDATE "ingredients" SET "key" = 'swiss_chard' WHERE "key" = 'blette';
UPDATE "ingredients" SET "key" = 'lettuce' WHERE "key" = 'salade';
UPDATE "ingredients" SET "key" = 'arugula' WHERE "key" = 'roquette';
UPDATE "ingredients" SET "key" = 'watercress' WHERE "key" = 'cresson';
UPDATE "ingredients" SET "key" = 'leek' WHERE "key" = 'poireau';
UPDATE "ingredients" SET "key" = 'celery' WHERE "key" = 'celeri';
UPDATE "ingredients" SET "key" = 'radish' WHERE "key" = 'radis';
UPDATE "ingredients" SET "key" = 'beetroot' WHERE "key" = 'betterave';
UPDATE "ingredients" SET "key" = 'turnip' WHERE "key" = 'navet';
UPDATE "ingredients" SET "key" = 'parsnip' WHERE "key" = 'panais';
UPDATE "ingredients" SET "key" = 'green_bean' WHERE "key" = 'haricot_vert';
UPDATE "ingredients" SET "key" = 'pea' WHERE "key" = 'petit_pois';
UPDATE "ingredients" SET "key" = 'corn' WHERE "key" = 'mais';
UPDATE "ingredients" SET "key" = 'artichoke' WHERE "key" = 'artichaut';
UPDATE "ingredients" SET "key" = 'fennel' WHERE "key" = 'fenouil';
UPDATE "ingredients" SET "key" = 'endive' WHERE "key" = 'endive';
UPDATE "ingredients" SET "key" = 'pumpkin' WHERE "key" = 'potiron';
UPDATE "ingredients" SET "key" = 'butternut_squash' WHERE "key" = 'butternut';
UPDATE "ingredients" SET "key" = 'asparagus' WHERE "key" = 'asperge';
UPDATE "ingredients" SET "key" = 'avocado' WHERE "key" = 'avocat';
UPDATE "ingredients" SET "key" = 'potato' WHERE "key" = 'pomme_de_terre';
UPDATE "ingredients" SET "key" = 'sweet_potato' WHERE "key" = 'patate_douce';
UPDATE "ingredients" SET "key" = 'cherry_tomato' WHERE "key" = 'tomates_cerises';
UPDATE "ingredients" SET "key" = 'bok_choy' WHERE "key" = 'pak_choi';
UPDATE "ingredients" SET "key" = 'soybean_sprouts' WHERE "key" = 'germes_de_soja';
UPDATE "ingredients" SET "key" = 'shiitake' WHERE "key" = 'shiitake';
UPDATE "ingredients" SET "key" = 'daikon' WHERE "key" = 'daikon';
UPDATE "ingredients" SET "key" = 'fresh_green_chili' WHERE "key" = 'piment_vert_frais';
UPDATE "ingredients" SET "key" = 'lemon' WHERE "key" = 'citron';
UPDATE "ingredients" SET "key" = 'lime' WHERE "key" = 'citron_vert';
UPDATE "ingredients" SET "key" = 'apple' WHERE "key" = 'pomme';
UPDATE "ingredients" SET "key" = 'pear' WHERE "key" = 'poire';
UPDATE "ingredients" SET "key" = 'banana' WHERE "key" = 'banane';
UPDATE "ingredients" SET "key" = 'orange' WHERE "key" = 'orange';
UPDATE "ingredients" SET "key" = 'clementine' WHERE "key" = 'clementine';
UPDATE "ingredients" SET "key" = 'grapefruit' WHERE "key" = 'pamplemousse';
UPDATE "ingredients" SET "key" = 'strawberry' WHERE "key" = 'fraise';
UPDATE "ingredients" SET "key" = 'raspberry' WHERE "key" = 'framboise';
UPDATE "ingredients" SET "key" = 'blueberry' WHERE "key" = 'myrtille';
UPDATE "ingredients" SET "key" = 'blackberry' WHERE "key" = 'mure';
UPDATE "ingredients" SET "key" = 'cherry' WHERE "key" = 'cerise';
UPDATE "ingredients" SET "key" = 'apricot' WHERE "key" = 'abricot';
UPDATE "ingredients" SET "key" = 'peach' WHERE "key" = 'peche';
UPDATE "ingredients" SET "key" = 'plum' WHERE "key" = 'prune';
UPDATE "ingredients" SET "key" = 'grape' WHERE "key" = 'raisin';
UPDATE "ingredients" SET "key" = 'melon' WHERE "key" = 'melon';
UPDATE "ingredients" SET "key" = 'watermelon' WHERE "key" = 'pasteque';
UPDATE "ingredients" SET "key" = 'pineapple' WHERE "key" = 'ananas';
UPDATE "ingredients" SET "key" = 'mango' WHERE "key" = 'mangue';
UPDATE "ingredients" SET "key" = 'kiwi' WHERE "key" = 'kiwi';
UPDATE "ingredients" SET "key" = 'fig' WHERE "key" = 'figue';
UPDATE "ingredients" SET "key" = 'date' WHERE "key" = 'datte';
UPDATE "ingredients" SET "key" = 'lychee' WHERE "key" = 'litchi';
UPDATE "ingredients" SET "key" = 'pomegranate' WHERE "key" = 'grenade';
UPDATE "ingredients" SET "key" = 'rhubarb' WHERE "key" = 'rhubarbe';
UPDATE "ingredients" SET "key" = 'quince' WHERE "key" = 'coing';
UPDATE "ingredients" SET "key" = 'basil' WHERE "key" = 'basilic';
UPDATE "ingredients" SET "key" = 'parsley' WHERE "key" = 'persil';
UPDATE "ingredients" SET "key" = 'thyme' WHERE "key" = 'thym';
UPDATE "ingredients" SET "key" = 'rosemary' WHERE "key" = 'romarin';
UPDATE "ingredients" SET "key" = 'bay_leaf' WHERE "key" = 'laurier';
UPDATE "ingredients" SET "key" = 'chives' WHERE "key" = 'ciboulette';
UPDATE "ingredients" SET "key" = 'fresh_cilantro' WHERE "key" = 'coriandre_fraiche';
UPDATE "ingredients" SET "key" = 'mint' WHERE "key" = 'menthe';
UPDATE "ingredients" SET "key" = 'oregano' WHERE "key" = 'origan';
UPDATE "ingredients" SET "key" = 'dill' WHERE "key" = 'aneth';
UPDATE "ingredients" SET "key" = 'tarragon' WHERE "key" = 'estragon';
UPDATE "ingredients" SET "key" = 'savory' WHERE "key" = 'sarriette';
UPDATE "ingredients" SET "key" = 'marjoram' WHERE "key" = 'marjolaine';
UPDATE "ingredients" SET "key" = 'sage' WHERE "key" = 'sauge';
UPDATE "ingredients" SET "key" = 'chervil' WHERE "key" = 'cerfeuil';
UPDATE "ingredients" SET "key" = 'ginger' WHERE "key" = 'gingembre';
UPDATE "ingredients" SET "key" = 'lemongrass' WHERE "key" = 'citronnelle';
UPDATE "ingredients" SET "key" = 'kaffir_lime' WHERE "key" = 'combava';
UPDATE "ingredients" SET "key" = 'rabbit' WHERE "key" = 'lapin';
UPDATE "ingredients" SET "key" = 'ground_beef' WHERE "key" = 'boeuf_hache';
UPDATE "ingredients" SET "key" = 'beef_steak' WHERE "key" = 'steak_de_boeuf';
UPDATE "ingredients" SET "key" = 'beef_roast' WHERE "key" = 'roti_de_boeuf';
UPDATE "ingredients" SET "key" = 'veal_cutlet' WHERE "key" = 'escalope_de_veau';
UPDATE "ingredients" SET "key" = 'pork_tenderloin' WHERE "key" = 'filet_mignon_de_porc';
UPDATE "ingredients" SET "key" = 'pork_chop' WHERE "key" = 'cote_de_porc';
UPDATE "ingredients" SET "key" = 'lamb' WHERE "key" = 'agneau';
UPDATE "ingredients" SET "key" = 'leg_of_lamb' WHERE "key" = 'gigot_d_agneau';
UPDATE "ingredients" SET "key" = 'bacon_lardons' WHERE "key" = 'lardons';
UPDATE "ingredients" SET "key" = 'bacon' WHERE "key" = 'bacon';
UPDATE "ingredients" SET "key" = 'ham' WHERE "key" = 'jambon_blanc';
UPDATE "ingredients" SET "key" = 'cured_ham' WHERE "key" = 'jambon_cru';
UPDATE "ingredients" SET "key" = 'sausage' WHERE "key" = 'saucisse';
UPDATE "ingredients" SET "key" = 'chorizo' WHERE "key" = 'chorizo';
UPDATE "ingredients" SET "key" = 'merguez' WHERE "key" = 'merguez';
UPDATE "ingredients" SET "key" = 'prosciutto' WHERE "key" = 'prosciutto';
UPDATE "ingredients" SET "key" = 'pancetta' WHERE "key" = 'pancetta';
UPDATE "ingredients" SET "key" = 'mortadella' WHERE "key" = 'mortadelle';
UPDATE "ingredients" SET "key" = 'salami' WHERE "key" = 'salami';
UPDATE "ingredients" SET "key" = 'chicken' WHERE "key" = 'poulet';
UPDATE "ingredients" SET "key" = 'turkey' WHERE "key" = 'dinde';
UPDATE "ingredients" SET "key" = 'duck' WHERE "key" = 'canard';
UPDATE "ingredients" SET "key" = 'duck_breast' WHERE "key" = 'magret_de_canard';
UPDATE "ingredients" SET "key" = 'salmon' WHERE "key" = 'saumon';
UPDATE "ingredients" SET "key" = 'tuna' WHERE "key" = 'thon';
UPDATE "ingredients" SET "key" = 'cod' WHERE "key" = 'cabillaud';
UPDATE "ingredients" SET "key" = 'trout' WHERE "key" = 'truite';
UPDATE "ingredients" SET "key" = 'sardine' WHERE "key" = 'sardine';
UPDATE "ingredients" SET "key" = 'anchovy' WHERE "key" = 'anchois';
UPDATE "ingredients" SET "key" = 'whiting' WHERE "key" = 'merlan';
UPDATE "ingredients" SET "key" = 'surimi' WHERE "key" = 'surimi';
UPDATE "ingredients" SET "key" = 'sea_bass' WHERE "key" = 'bar_loup_de_mer';
UPDATE "ingredients" SET "key" = 'sea_bream' WHERE "key" = 'dorade';
UPDATE "ingredients" SET "key" = 'sole' WHERE "key" = 'sole';
UPDATE "ingredients" SET "key" = 'turbot' WHERE "key" = 'turbot';
UPDATE "ingredients" SET "key" = 'hake' WHERE "key" = 'merlu';
UPDATE "ingredients" SET "key" = 'pollock' WHERE "key" = 'colin';
UPDATE "ingredients" SET "key" = 'saithe' WHERE "key" = 'lieu_noir';
UPDATE "ingredients" SET "key" = 'haddock' WHERE "key" = 'eglefin';
UPDATE "ingredients" SET "key" = 'mackerel' WHERE "key" = 'maquereau';
UPDATE "ingredients" SET "key" = 'herring' WHERE "key" = 'hareng';
UPDATE "ingredients" SET "key" = 'red_mullet' WHERE "key" = 'rouget';
UPDATE "ingredients" SET "key" = 'skate' WHERE "key" = 'raie';
UPDATE "ingredients" SET "key" = 'monkfish' WHERE "key" = 'lotte';
UPDATE "ingredients" SET "key" = 'halibut' WHERE "key" = 'fletan';
UPDATE "ingredients" SET "key" = 'swordfish' WHERE "key" = 'espadon';
UPDATE "ingredients" SET "key" = 'carp' WHERE "key" = 'carpe';
UPDATE "ingredients" SET "key" = 'pike' WHERE "key" = 'brochet';
UPDATE "ingredients" SET "key" = 'perch' WHERE "key" = 'perche';
UPDATE "ingredients" SET "key" = 'tilapia' WHERE "key" = 'tilapia';
UPDATE "ingredients" SET "key" = 'pangasius' WHERE "key" = 'panga';
UPDATE "ingredients" SET "key" = 'smoked_salmon' WHERE "key" = 'saumon_fume';
UPDATE "ingredients" SET "key" = 'dried_fish' WHERE "key" = 'poisson_seche';
UPDATE "ingredients" SET "key" = 'shrimp' WHERE "key" = 'crevettes';
UPDATE "ingredients" SET "key" = 'langoustine' WHERE "key" = 'langoustines';
UPDATE "ingredients" SET "key" = 'lobster' WHERE "key" = 'homard';
UPDATE "ingredients" SET "key" = 'crab' WHERE "key" = 'crabe';
UPDATE "ingredients" SET "key" = 'spiny_lobster' WHERE "key" = 'langouste';
UPDATE "ingredients" SET "key" = 'mussels' WHERE "key" = 'moules';
UPDATE "ingredients" SET "key" = 'oysters' WHERE "key" = 'huitres';
UPDATE "ingredients" SET "key" = 'scallops' WHERE "key" = 'saint_jacques';
UPDATE "ingredients" SET "key" = 'squid' WHERE "key" = 'calamar';
UPDATE "ingredients" SET "key" = 'octopus' WHERE "key" = 'poulpe';
UPDATE "ingredients" SET "key" = 'clams' WHERE "key" = 'palourdes';
UPDATE "ingredients" SET "key" = 'whelks' WHERE "key" = 'bulots';
UPDATE "ingredients" SET "key" = 'semolina' WHERE "key" = 'semoule';
UPDATE "ingredients" SET "key" = 'couscous' WHERE "key" = 'couscous';
UPDATE "ingredients" SET "key" = 'bulgur' WHERE "key" = 'boulgour';
UPDATE "ingredients" SET "key" = 'polenta' WHERE "key" = 'polenta';
UPDATE "ingredients" SET "key" = 'quinoa' WHERE "key" = 'quinoa';
UPDATE "ingredients" SET "key" = 'pasta' WHERE "key" = 'pates';
UPDATE "ingredients" SET "key" = 'whole_wheat_pasta' WHERE "key" = 'pates_completes';
UPDATE "ingredients" SET "key" = 'rice' WHERE "key" = 'riz';
UPDATE "ingredients" SET "key" = 'basmati_rice' WHERE "key" = 'riz_basmati';
UPDATE "ingredients" SET "key" = 'brown_rice' WHERE "key" = 'riz_complet';
UPDATE "ingredients" SET "key" = 'oats' WHERE "key" = 'flocons_d_avoine';
UPDATE "ingredients" SET "key" = 'spaghetti' WHERE "key" = 'spaghetti';
UPDATE "ingredients" SET "key" = 'penne' WHERE "key" = 'penne';
UPDATE "ingredients" SET "key" = 'tagliatelle' WHERE "key" = 'tagliatelles';
UPDATE "ingredients" SET "key" = 'lasagna_sheets' WHERE "key" = 'lasagnes_feuilles';
UPDATE "ingredients" SET "key" = 'gnocchi' WHERE "key" = 'gnocchi';
UPDATE "ingredients" SET "key" = 'arborio_rice' WHERE "key" = 'riz_arborio';
UPDATE "ingredients" SET "key" = 'rice_noodles' WHERE "key" = 'nouilles_de_riz';
UPDATE "ingredients" SET "key" = 'udon_noodles' WHERE "key" = 'nouilles_udon';
UPDATE "ingredients" SET "key" = 'soba_noodles' WHERE "key" = 'nouilles_soba';
UPDATE "ingredients" SET "key" = 'chinese_noodles' WHERE "key" = 'nouilles_chinoises';
UPDATE "ingredients" SET "key" = 'rice_vermicelli' WHERE "key" = 'vermicelles_de_riz';
UPDATE "ingredients" SET "key" = 'soy_vermicelli' WHERE "key" = 'vermicelles_de_soja';
UPDATE "ingredients" SET "key" = 'sticky_rice' WHERE "key" = 'riz_gluant';
UPDATE "ingredients" SET "key" = 'sushi_rice' WHERE "key" = 'riz_a_sushi';
UPDATE "ingredients" SET "key" = 'jasmine_rice' WHERE "key" = 'riz_jasmin';
UPDATE "ingredients" SET "key" = 'green_lentils' WHERE "key" = 'lentilles_vertes';
UPDATE "ingredients" SET "key" = 'red_lentils' WHERE "key" = 'lentilles_corail';
UPDATE "ingredients" SET "key" = 'chickpeas' WHERE "key" = 'pois_chiches';
UPDATE "ingredients" SET "key" = 'white_beans' WHERE "key" = 'haricots_blancs';
UPDATE "ingredients" SET "key" = 'kidney_beans' WHERE "key" = 'haricots_rouges';
UPDATE "ingredients" SET "key" = 'black_beans' WHERE "key" = 'haricots_noirs';
UPDATE "ingredients" SET "key" = 'split_peas' WHERE "key" = 'pois_casses';
UPDATE "ingredients" SET "key" = 'fava_beans' WHERE "key" = 'feves';
UPDATE "ingredients" SET "key" = 'edamame' WHERE "key" = 'edamame';
UPDATE "ingredients" SET "key" = 'pinto_beans' WHERE "key" = 'haricots_pinto';
UPDATE "ingredients" SET "key" = 'peanuts_shelled' WHERE "key" = 'cacahuetes';
UPDATE "ingredients" SET "key" = 'almonds' WHERE "key" = 'amandes';
UPDATE "ingredients" SET "key" = 'walnuts' WHERE "key" = 'noix';
UPDATE "ingredients" SET "key" = 'hazelnuts' WHERE "key" = 'noisettes';
UPDATE "ingredients" SET "key" = 'cashews' WHERE "key" = 'noix_de_cajou';
UPDATE "ingredients" SET "key" = 'pistachios' WHERE "key" = 'pistaches';
UPDATE "ingredients" SET "key" = 'pecans' WHERE "key" = 'noix_de_pecan';
UPDATE "ingredients" SET "key" = 'almond_powder' WHERE "key" = 'poudre_d_amande';
UPDATE "ingredients" SET "key" = 'pine_nuts' WHERE "key" = 'pignons_de_pin';
UPDATE "ingredients" SET "key" = 'sunflower_seeds' WHERE "key" = 'graines_de_tournesol';
UPDATE "ingredients" SET "key" = 'pumpkin_seeds' WHERE "key" = 'graines_de_courge';
UPDATE "ingredients" SET "key" = 'shredded_coconut' WHERE "key" = 'noix_de_coco_rapee';
UPDATE "ingredients" SET "key" = 'raisins' WHERE "key" = 'raisins_secs';
UPDATE "ingredients" SET "key" = 'prunes' WHERE "key" = 'pruneaux';
UPDATE "ingredients" SET "key" = 'dried_apricots' WHERE "key" = 'abricots_secs';
UPDATE "ingredients" SET "key" = 'sesame_seeds' WHERE "key" = 'graines_de_sesame';
UPDATE "ingredients" SET "key" = 'black_mushrooms' WHERE "key" = 'champignons_noirs';
UPDATE "ingredients" SET "key" = 'nori_seaweed' WHERE "key" = 'algue_nori';
UPDATE "ingredients" SET "key" = 'wakame_seaweed' WHERE "key" = 'algue_wakame';
UPDATE "ingredients" SET "key" = 'kombu_seaweed' WHERE "key" = 'algue_kombu';
UPDATE "ingredients" SET "key" = 'bamboo_shoots' WHERE "key" = 'pousses_de_bambou';
UPDATE "ingredients" SET "key" = 'water_chestnuts' WHERE "key" = 'chataignes_d_eau';
UPDATE "ingredients" SET "key" = 'bread' WHERE "key" = 'pain';
UPDATE "ingredients" SET "key" = 'sandwich_bread' WHERE "key" = 'pain_de_mie';
UPDATE "ingredients" SET "key" = 'whole_wheat_bread' WHERE "key" = 'pain_complet';
UPDATE "ingredients" SET "key" = 'baguette' WHERE "key" = 'baguette';
UPDATE "ingredients" SET "key" = 'rye_bread' WHERE "key" = 'pain_de_seigle';
UPDATE "ingredients" SET "key" = 'breadcrumbs' WHERE "key" = 'chapelure';
UPDATE "ingredients" SET "key" = 'burger_bun' WHERE "key" = 'pain_a_burger';
UPDATE "ingredients" SET "key" = 'brioche_bun' WHERE "key" = 'pain_brioche';
UPDATE "ingredients" SET "key" = 'hot_dog_bun' WHERE "key" = 'pain_a_hot_dog';
UPDATE "ingredients" SET "key" = 'pita_bread' WHERE "key" = 'pain_pita';
UPDATE "ingredients" SET "key" = 'bagel' WHERE "key" = 'pain_bagel';
UPDATE "ingredients" SET "key" = 'naan' WHERE "key" = 'naan';
UPDATE "ingredients" SET "key" = 'wrap_bread' WHERE "key" = 'pain_wrap';
UPDATE "ingredients" SET "key" = 'viennese_bread' WHERE "key" = 'pain_viennois';
UPDATE "ingredients" SET "key" = 'country_bread' WHERE "key" = 'pain_de_campagne';
UPDATE "ingredients" SET "key" = 'multigrain_bread' WHERE "key" = 'pain_aux_cereales';
UPDATE "ingredients" SET "key" = 'bread_roll' WHERE "key" = 'petit_pain';
UPDATE "ingredients" SET "key" = 'swedish_bread' WHERE "key" = 'pain_suedois';
UPDATE "ingredients" SET "key" = 'gluten_free_bread' WHERE "key" = 'pain_sans_gluten';
UPDATE "ingredients" SET "key" = 'rusk' WHERE "key" = 'biscotte';
UPDATE "ingredients" SET "key" = 'croutons' WHERE "key" = 'croutons';
UPDATE "ingredients" SET "key" = 'focaccia' WHERE "key" = 'focaccia';
UPDATE "ingredients" SET "key" = 'ciabatta' WHERE "key" = 'ciabatta';
UPDATE "ingredients" SET "key" = 'corn_tortilla' WHERE "key" = 'tortilla_de_mais';
UPDATE "ingredients" SET "key" = 'wheat_tortilla' WHERE "key" = 'tortilla_de_ble';
UPDATE "ingredients" SET "key" = 'puff_pastry' WHERE "key" = 'pate_feuilletee';
UPDATE "ingredients" SET "key" = 'shortcrust_pastry' WHERE "key" = 'pate_brisee';
UPDATE "ingredients" SET "key" = 'pizza_dough' WHERE "key" = 'pate_a_pizza';
UPDATE "ingredients" SET "key" = 'sweet_shortcrust_pastry' WHERE "key" = 'pate_a_tarte_sablee';
UPDATE "ingredients" SET "key" = 'milk' WHERE "key" = 'lait';
UPDATE "ingredients" SET "key" = 'butter' WHERE "key" = 'beurre';
UPDATE "ingredients" SET "key" = 'creme_fraiche' WHERE "key" = 'creme_fraiche';
UPDATE "ingredients" SET "key" = 'liquid_cream' WHERE "key" = 'creme_liquide';
UPDATE "ingredients" SET "key" = 'cheese' WHERE "key" = 'fromage';
UPDATE "ingredients" SET "key" = 'emmental' WHERE "key" = 'emmental';
UPDATE "ingredients" SET "key" = 'gruyere' WHERE "key" = 'gruyere';
UPDATE "ingredients" SET "key" = 'parmesan' WHERE "key" = 'parmesan';
UPDATE "ingredients" SET "key" = 'mozzarella' WHERE "key" = 'mozzarella';
UPDATE "ingredients" SET "key" = 'goat_cheese' WHERE "key" = 'chevre_fromage';
UPDATE "ingredients" SET "key" = 'feta' WHERE "key" = 'feta';
UPDATE "ingredients" SET "key" = 'comte' WHERE "key" = 'comte';
UPDATE "ingredients" SET "key" = 'fromage_blanc' WHERE "key" = 'fromage_blanc';
UPDATE "ingredients" SET "key" = 'mascarpone' WHERE "key" = 'mascarpone';
UPDATE "ingredients" SET "key" = 'yogurt' WHERE "key" = 'yaourt';
UPDATE "ingredients" SET "key" = 'burrata' WHERE "key" = 'burrata';
UPDATE "ingredients" SET "key" = 'ricotta' WHERE "key" = 'ricotta';
UPDATE "ingredients" SET "key" = 'pecorino' WHERE "key" = 'pecorino';
UPDATE "ingredients" SET "key" = 'gorgonzola' WHERE "key" = 'gorgonzola';
UPDATE "ingredients" SET "key" = 'cheddar' WHERE "key" = 'cheddar';
UPDATE "ingredients" SET "key" = 'egg' WHERE "key" = 'oeuf';
UPDATE "ingredients" SET "key" = 'coconut_milk' WHERE "key" = 'lait_de_coco';
UPDATE "ingredients" SET "key" = 'coconut_cream' WHERE "key" = 'creme_de_coco';
UPDATE "ingredients" SET "key" = 'almond_milk' WHERE "key" = 'lait_d_amande';
UPDATE "ingredients" SET "key" = 'oat_milk' WHERE "key" = 'lait_d_avoine';
UPDATE "ingredients" SET "key" = 'tofu' WHERE "key" = 'tofu';
UPDATE "ingredients" SET "key" = 'silken_tofu' WHERE "key" = 'tofu_soyeux';
UPDATE "ingredients" SET "key" = 'herbes_de_provence' WHERE "key" = 'herbes_de_provence';
UPDATE "ingredients" SET "key" = 'black_pepper' WHERE "key" = 'poivre_noir';
UPDATE "ingredients" SET "key" = 'paprika' WHERE "key" = 'paprika';
UPDATE "ingredients" SET "key" = 'espelette_pepper' WHERE "key" = 'piment_d_espelette';
UPDATE "ingredients" SET "key" = 'cayenne_pepper' WHERE "key" = 'piment_de_cayenne';
UPDATE "ingredients" SET "key" = 'cumin' WHERE "key" = 'cumin';
UPDATE "ingredients" SET "key" = 'curry_powder' WHERE "key" = 'curry_poudre';
UPDATE "ingredients" SET "key" = 'turmeric' WHERE "key" = 'curcuma';
UPDATE "ingredients" SET "key" = 'cinnamon' WHERE "key" = 'cannelle';
UPDATE "ingredients" SET "key" = 'nutmeg' WHERE "key" = 'muscade';
UPDATE "ingredients" SET "key" = 'saffron' WHERE "key" = 'safran';
UPDATE "ingredients" SET "key" = 'clove' WHERE "key" = 'clou_de_girofle';
UPDATE "ingredients" SET "key" = 'vanilla_bean' WHERE "key" = 'vanille_gousse';
UPDATE "ingredients" SET "key" = 'white_pepper' WHERE "key" = 'poivre_blanc';
UPDATE "ingredients" SET "key" = 'pink_pepper' WHERE "key" = 'poivre_rose';
UPDATE "ingredients" SET "key" = 'sichuan_pepper' WHERE "key" = 'poivre_du_sichuan';
UPDATE "ingredients" SET "key" = 'smoked_paprika' WHERE "key" = 'paprika_fume';
UPDATE "ingredients" SET "key" = 'bird_eye_chili' WHERE "key" = 'piment_oiseau';
UPDATE "ingredients" SET "key" = 'juniper_berries' WHERE "key" = 'baies_de_genievre';
UPDATE "ingredients" SET "key" = 'star_anise' WHERE "key" = 'anis_etoile_badiane';
UPDATE "ingredients" SET "key" = 'green_anise' WHERE "key" = 'anis_vert';
UPDATE "ingredients" SET "key" = 'fennel_seeds' WHERE "key" = 'graines_de_fenouil';
UPDATE "ingredients" SET "key" = 'sumac' WHERE "key" = 'sumac';
UPDATE "ingredients" SET "key" = 'nigella' WHERE "key" = 'nigelle';
UPDATE "ingredients" SET "key" = 'allspice' WHERE "key" = 'quatre_epices';
UPDATE "ingredients" SET "key" = 'colombo_powder' WHERE "key" = 'colombo_poudre';
UPDATE "ingredients" SET "key" = 'baharat' WHERE "key" = 'baharat';
UPDATE "ingredients" SET "key" = 'horseradish' WHERE "key" = 'raifort';
UPDATE "ingredients" SET "key" = 'herb_salt' WHERE "key" = 'sel_aux_herbes';
UPDATE "ingredients" SET "key" = 'celery_salt' WHERE "key" = 'sel_de_celeri';
UPDATE "ingredients" SET "key" = 'fleur_de_sel' WHERE "key" = 'fleur_de_sel';
UPDATE "ingredients" SET "key" = 'salt' WHERE "key" = 'sel';
UPDATE "ingredients" SET "key" = 'five_spice' WHERE "key" = 'cinq_epices';
UPDATE "ingredients" SET "key" = 'garam_masala' WHERE "key" = 'garam_masala';
UPDATE "ingredients" SET "key" = 'coriander_seeds' WHERE "key" = 'graines_de_coriandre';
UPDATE "ingredients" SET "key" = 'cardamom' WHERE "key" = 'cardamome';
UPDATE "ingredients" SET "key" = 'fenugreek' WHERE "key" = 'fenugrec';
UPDATE "ingredients" SET "key" = 'jalapeno' WHERE "key" = 'piment_jalapeno';
UPDATE "ingredients" SET "key" = 'chipotle' WHERE "key" = 'piment_chipotle';
UPDATE "ingredients" SET "key" = 'poblano_pepper' WHERE "key" = 'piment_poblano';
UPDATE "ingredients" SET "key" = 'habanero' WHERE "key" = 'piment_habanero';
UPDATE "ingredients" SET "key" = 'ras_el_hanout' WHERE "key" = 'ras_el_hanout';
UPDATE "ingredients" SET "key" = 'zaatar' WHERE "key" = 'za_atar';
UPDATE "ingredients" SET "key" = 'soy_sauce' WHERE "key" = 'sauce_soja';
UPDATE "ingredients" SET "key" = 'mustard' WHERE "key" = 'moutarde';
UPDATE "ingredients" SET "key" = 'mayonnaise' WHERE "key" = 'mayonnaise';
UPDATE "ingredients" SET "key" = 'ketchup' WHERE "key" = 'ketchup';
UPDATE "ingredients" SET "key" = 'tabasco' WHERE "key" = 'tabasco';
UPDATE "ingredients" SET "key" = 'worcestershire_sauce' WHERE "key" = 'sauce_worcestershire';
UPDATE "ingredients" SET "key" = 'fish_sauce' WHERE "key" = 'sauce_nuoc_mam';
UPDATE "ingredients" SET "key" = 'wasabi' WHERE "key" = 'wasabi';
UPDATE "ingredients" SET "key" = 'harissa' WHERE "key" = 'harissa';
UPDATE "ingredients" SET "key" = 'curry_paste' WHERE "key" = 'pate_de_curry';
UPDATE "ingredients" SET "key" = 'peanut_butter' WHERE "key" = 'beurre_de_cacahuete';
UPDATE "ingredients" SET "key" = 'dijon_mustard' WHERE "key" = 'moutarde_de_dijon';
UPDATE "ingredients" SET "key" = 'wholegrain_mustard' WHERE "key" = 'moutarde_a_l_ancienne';
UPDATE "ingredients" SET "key" = 'barbecue_sauce' WHERE "key" = 'sauce_barbecue';
UPDATE "ingredients" SET "key" = 'tartar_sauce' WHERE "key" = 'sauce_tartare';
UPDATE "ingredients" SET "key" = 'cocktail_sauce' WHERE "key" = 'sauce_cocktail';
UPDATE "ingredients" SET "key" = 'bearnaise_sauce' WHERE "key" = 'sauce_bearnaise';
UPDATE "ingredients" SET "key" = 'hollandaise_sauce' WHERE "key" = 'sauce_hollandaise';
UPDATE "ingredients" SET "key" = 'bechamel_sauce' WHERE "key" = 'sauce_bechamel';
UPDATE "ingredients" SET "key" = 'teriyaki_sauce' WHERE "key" = 'sauce_teriyaki';
UPDATE "ingredients" SET "key" = 'ponzu_sauce' WHERE "key" = 'sauce_ponzu';
UPDATE "ingredients" SET "key" = 'chimichurri' WHERE "key" = 'chimichurri';
UPDATE "ingredients" SET "key" = 'red_pesto' WHERE "key" = 'pesto_rouge_tomates_sechees';
UPDATE "ingredients" SET "key" = 'pesto' WHERE "key" = 'pesto';
UPDATE "ingredients" SET "key" = 'oyster_sauce' WHERE "key" = 'sauce_huitre';
UPDATE "ingredients" SET "key" = 'hoisin_sauce' WHERE "key" = 'sauce_hoisin';
UPDATE "ingredients" SET "key" = 'sriracha' WHERE "key" = 'sauce_sriracha';
UPDATE "ingredients" SET "key" = 'sweet_chili_sauce' WHERE "key" = 'sauce_sweet_chili';
UPDATE "ingredients" SET "key" = 'miso' WHERE "key" = 'miso';
UPDATE "ingredients" SET "key" = 'shrimp_paste' WHERE "key" = 'pate_de_crevettes';
UPDATE "ingredients" SET "key" = 'red_curry_paste' WHERE "key" = 'pate_de_curry_rouge_thai';
UPDATE "ingredients" SET "key" = 'green_curry_paste' WHERE "key" = 'pate_de_curry_vert_thai';
UPDATE "ingredients" SET "key" = 'tahini' WHERE "key" = 'tahini';
UPDATE "ingredients" SET "key" = 'olive_oil' WHERE "key" = 'huile_d_olive';
UPDATE "ingredients" SET "key" = 'sunflower_oil' WHERE "key" = 'huile_de_tournesol';
UPDATE "ingredients" SET "key" = 'rapeseed_oil' WHERE "key" = 'huile_de_colza';
UPDATE "ingredients" SET "key" = 'coconut_oil' WHERE "key" = 'huile_de_coco';
UPDATE "ingredients" SET "key" = 'sesame_oil' WHERE "key" = 'huile_de_sesame';
UPDATE "ingredients" SET "key" = 'cider_vinegar' WHERE "key" = 'vinaigre_de_cidre';
UPDATE "ingredients" SET "key" = 'white_vinegar' WHERE "key" = 'vinaigre_blanc';
UPDATE "ingredients" SET "key" = 'balsamic_vinegar' WHERE "key" = 'vinaigre_balsamique';
UPDATE "ingredients" SET "key" = 'capers' WHERE "key" = 'capres';
UPDATE "ingredients" SET "key" = 'olives' WHERE "key" = 'olives';
UPDATE "ingredients" SET "key" = 'white_wine' WHERE "key" = 'vin_blanc_cuisine';
UPDATE "ingredients" SET "key" = 'red_wine' WHERE "key" = 'vin_rouge_cuisine';
UPDATE "ingredients" SET "key" = 'red_wine_vinegar' WHERE "key" = 'vinaigre_de_vin_rouge';
UPDATE "ingredients" SET "key" = 'white_wine_vinegar' WHERE "key" = 'vinaigre_de_vin_blanc';
UPDATE "ingredients" SET "key" = 'sherry_vinegar' WHERE "key" = 'vinaigre_de_xeres';
UPDATE "ingredients" SET "key" = 'walnut_oil' WHERE "key" = 'huile_de_noix';
UPDATE "ingredients" SET "key" = 'hazelnut_oil' WHERE "key" = 'huile_de_noisette';
UPDATE "ingredients" SET "key" = 'peanut_oil' WHERE "key" = 'huile_d_arachide';
UPDATE "ingredients" SET "key" = 'chili_oil' WHERE "key" = 'huile_pimentee';
UPDATE "ingredients" SET "key" = 'rice_vinegar' WHERE "key" = 'vinaigre_de_riz';
UPDATE "ingredients" SET "key" = 'mirin' WHERE "key" = 'mirin';
UPDATE "ingredients" SET "key" = 'sake' WHERE "key" = 'sake_cuisine';
UPDATE "ingredients" SET "key" = 'lemon_juice' WHERE "key" = 'jus_de_citron';
UPDATE "ingredients" SET "key" = 'lime_juice' WHERE "key" = 'jus_de_citron_vert';
UPDATE "ingredients" SET "key" = 'orange_juice' WHERE "key" = 'jus_d_orange';
UPDATE "ingredients" SET "key" = 'apple_juice' WHERE "key" = 'jus_de_pomme';
UPDATE "ingredients" SET "key" = 'grape_juice' WHERE "key" = 'jus_de_raisin';
UPDATE "ingredients" SET "key" = 'tomato_juice' WHERE "key" = 'jus_de_tomate';
UPDATE "ingredients" SET "key" = 'cranberry_juice' WHERE "key" = 'jus_de_cranberry';
UPDATE "ingredients" SET "key" = 'coffee' WHERE "key" = 'cafe';
UPDATE "ingredients" SET "key" = 'tea' WHERE "key" = 'the';
UPDATE "ingredients" SET "key" = 'beer' WHERE "key" = 'biere_cuisine';
UPDATE "ingredients" SET "key" = 'cider' WHERE "key" = 'cidre_cuisine';
UPDATE "ingredients" SET "key" = 'champagne' WHERE "key" = 'champagne_vin_petillant_cuisine';
UPDATE "ingredients" SET "key" = 'port_wine' WHERE "key" = 'porto_cuisine';
UPDATE "ingredients" SET "key" = 'vin_jaune' WHERE "key" = 'vin_jaune_cuisine';
UPDATE "ingredients" SET "key" = 'cognac' WHERE "key" = 'cognac';
UPDATE "ingredients" SET "key" = 'rum' WHERE "key" = 'rhum';
UPDATE "ingredients" SET "key" = 'whisky' WHERE "key" = 'whisky';
UPDATE "ingredients" SET "key" = 'vodka' WHERE "key" = 'vodka';
UPDATE "ingredients" SET "key" = 'wheat_flour' WHERE "key" = 'farine_de_ble';
UPDATE "ingredients" SET "key" = 'whole_wheat_flour' WHERE "key" = 'farine_complete';
UPDATE "ingredients" SET "key" = 'corn_flour' WHERE "key" = 'farine_de_mais';
UPDATE "ingredients" SET "key" = 'buckwheat_flour' WHERE "key" = 'farine_de_sarrasin';
UPDATE "ingredients" SET "key" = 'rice_flour' WHERE "key" = 'farine_de_riz';
UPDATE "ingredients" SET "key" = 'vegetable_stock_cube' WHERE "key" = 'bouillon_cube_legumes';
UPDATE "ingredients" SET "key" = 'chicken_stock_cube' WHERE "key" = 'bouillon_cube_volaille';
UPDATE "ingredients" SET "key" = 'tomato_paste' WHERE "key" = 'concentre_de_tomate';
UPDATE "ingredients" SET "key" = 'tomato_coulis' WHERE "key" = 'coulis_de_tomate';
UPDATE "ingredients" SET "key" = 'canned_peeled_tomatoes' WHERE "key" = 'tomates_pelees_conserve';
UPDATE "ingredients" SET "key" = 'sun_dried_tomatoes' WHERE "key" = 'tomates_sechees';
UPDATE "ingredients" SET "key" = 'veal_stock' WHERE "key" = 'fond_de_veau';
UPDATE "ingredients" SET "key" = 'chicken_stock' WHERE "key" = 'fond_de_volaille';
UPDATE "ingredients" SET "key" = 'beef_stock_cube' WHERE "key" = 'bouillon_cube_boeuf';
UPDATE "ingredients" SET "key" = 'fish_stock_cube' WHERE "key" = 'bouillon_cube_poisson';
UPDATE "ingredients" SET "key" = 'vegetable_broth' WHERE "key" = 'bouillon_de_legumes';
UPDATE "ingredients" SET "key" = 'chicken_broth' WHERE "key" = 'bouillon_de_volaille';
UPDATE "ingredients" SET "key" = 'beef_broth' WHERE "key" = 'bouillon_de_boeuf';
UPDATE "ingredients" SET "key" = 'court_bouillon' WHERE "key" = 'court_bouillon';
UPDATE "ingredients" SET "key" = 'dashi' WHERE "key" = 'dashi_bouillon_japonais';
UPDATE "ingredients" SET "key" = 'shellfish_bisque' WHERE "key" = 'bisque_de_crustaces';
UPDATE "ingredients" SET "key" = 'tapioca_flour' WHERE "key" = 'farine_de_tapioca';
UPDATE "ingredients" SET "key" = 'masa_harina' WHERE "key" = 'masa_harina';
UPDATE "ingredients" SET "key" = 'water' WHERE "key" = 'eau';
UPDATE "ingredients" SET "key" = 'sparkling_water' WHERE "key" = 'eau_gazeuse';
UPDATE "ingredients" SET "key" = 'orange_blossom_water' WHERE "key" = 'eau_de_fleur_d_oranger';
UPDATE "ingredients" SET "key" = 'rose_water' WHERE "key" = 'eau_de_rose';
UPDATE "ingredients" SET "key" = 'fish_fumet' WHERE "key" = 'fumet_de_poisson';
UPDATE "ingredients" SET "key" = 'bakers_yeast' WHERE "key" = 'levure_boulangere';
UPDATE "ingredients" SET "key" = 'baking_powder' WHERE "key" = 'levure_chimique';
UPDATE "ingredients" SET "key" = 'cornstarch' WHERE "key" = 'maizena';
UPDATE "ingredients" SET "key" = 'lupin_flour' WHERE "key" = 'farine_de_lupin';
UPDATE "ingredients" SET "key" = 'gelatin' WHERE "key" = 'gelatine';
UPDATE "ingredients" SET "key" = 'baking_soda' WHERE "key" = 'bicarbonate_de_soude';
UPDATE "ingredients" SET "key" = 'potato_starch' WHERE "key" = 'fecule_de_pomme_de_terre';
UPDATE "ingredients" SET "key" = 'sugar' WHERE "key" = 'sucre';
UPDATE "ingredients" SET "key" = 'honey' WHERE "key" = 'miel';
UPDATE "ingredients" SET "key" = 'maple_syrup' WHERE "key" = 'sirop_d_erable';
UPDATE "ingredients" SET "key" = 'brown_sugar' WHERE "key" = 'sucre_roux';
UPDATE "ingredients" SET "key" = 'powdered_sugar' WHERE "key" = 'sucre_glace';
UPDATE "ingredients" SET "key" = 'demerara_sugar' WHERE "key" = 'cassonade';
UPDATE "ingredients" SET "key" = 'dark_chocolate' WHERE "key" = 'chocolat_noir';
UPDATE "ingredients" SET "key" = 'milk_chocolate' WHERE "key" = 'chocolat_au_lait';
UPDATE "ingredients" SET "key" = 'white_chocolate' WHERE "key" = 'chocolat_blanc';
UPDATE "ingredients" SET "key" = 'chocolate_chips' WHERE "key" = 'pepites_de_chocolat';
UPDATE "ingredients" SET "key" = 'cocoa_powder' WHERE "key" = 'cacao_en_poudre';
UPDATE "ingredients" SET "key" = 'vanilla_extract' WHERE "key" = 'extrait_de_vanille';
UPDATE "ingredients" SET "key" = 'palm_sugar' WHERE "key" = 'sucre_de_palme';
UPDATE "ingredients" SET "key" = 'cane_syrup' WHERE "key" = 'sirop_de_sucre_de_canne';

View file

@ -1,99 +1,49 @@
/**
* One-off generator, run by hand whenever the catalog's reference data
* changes (a new ingredient/diet/allergen added to
* `db/reference-seed-data.ts`): derives every row's slug `key` from its
* French name (see `slugify.ts`), fails loudly on any collision, and
* regenerates `apps/web/src/locales/fr/translation.json`'s
* `catalog.{diets,allergens,ingredients}` sections (key -> French label),
* merged in without touching the rest of the file.
* `db/reference-seed-data.ts`, or an English key corrected in
* `catalog-en-keys.ts`): regenerates
* `apps/web/src/locales/fr/translation.json`'s
* `catalog.{diets,allergens,ingredients}` sections (English key -> French
* label), merged in without touching the rest of the file.
*
* Also (re-)writes `backfill.sql` alongside itself the `UPDATE ... SET
* key = ...` statements a migration adding a brand new item needs to carry
* forward, in case that ever happens again; the one for this refactor's own
* migration (`prisma/migrations/20260818190000_catalog_labels_to_keys/`)
* was generated once and copied in by hand, and `backfill.sql` itself is
* gitignored scratch output, not the source of truth.
* Doesn't touch the database a brand new diet/allergen/ingredient is
* created fresh by `seedReferenceData`'s normal create path (see
* `reference-seed-data.ts`), no backfill needed. Renaming an *existing*
* item's English key in `catalog-en-keys.ts` does need a one-off migration
* (`UPDATE ... SET key = ...`, keyed by the *old* key value) written by
* hand for that occasion see
* `prisma/migrations/20260818193000_catalog_keys_to_english/` for the shape
* one looks like.
*
* Never imported by the app itself a dev-time tool, run via
* `tsx scripts/generate-catalog-i18n.ts`.
*/
import { readFileSync, writeFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
import { ALLERGENS, DIETS, INGREDIENT_GROUPS } from "../src/db/reference-seed-data.js";
import { slugify } from "../src/utils/slugify.js";
const here = fileURLToPath(new URL(".", import.meta.url));
function toKeyLabelMap(labels: string[]): Record<string, string> {
const map: Record<string, string> = {};
const seenKeys = new Map<string, string>();
for (const label of labels) {
const key = slugify(label);
const clashingLabel = seenKeys.get(key);
if (clashingLabel !== undefined && clashingLabel !== label) {
throw new Error(`Slug collision: "${clashingLabel}" and "${label}" both slugify to "${key}"`);
}
seenKeys.set(key, label);
map[key] = label;
map[getEnglishKey(label)] = label;
}
return map;
}
const dietLabels = DIETS;
const allergenLabels = ALLERGENS.map((a) => a.name);
const ingredientLabels = INGREDIENT_GROUPS.flatMap((g) => g.items.map((i) => i.name));
const diets = toKeyLabelMap(dietLabels);
const allergens = toKeyLabelMap(allergenLabels);
const ingredients = toKeyLabelMap(ingredientLabels);
const diets = toKeyLabelMap(DIETS);
const allergens = toKeyLabelMap(ALLERGENS.map((a) => a.name));
const ingredients = toKeyLabelMap(INGREDIENT_GROUPS.flatMap((g) => g.items.map((i) => i.name)));
console.log(
`diets: ${Object.keys(diets).length}, allergens: ${Object.keys(allergens).length}, ingredients: ${Object.keys(ingredients).length}`,
);
// --- Merge into the fr locale file -----------------------------------------
const localePath = here + "../../web/src/locales/fr/translation.json";
const locale = JSON.parse(readFileSync(localePath, "utf8"));
locale.catalog = { diets, allergens, ingredients };
writeFileSync(localePath, JSON.stringify(locale, null, 2) + "\n");
console.log(`wrote ${localePath}`);
// --- Emit the migration's backfill SQL --------------------------------------
function escapeSql(value: string): string {
return value.replace(/'/g, "''");
}
// The migration this feeds renames each table's "name" column to "key"
// *before* running this backfill — so by the time these UPDATEs run, the
// "key" column still holds the old French label value (just under its new
// column name), which is exactly what the WHERE clause below matches on.
const dietUpdates = Object.entries(diets)
.map(([key, label]) => `UPDATE "diet" SET "key" = '${escapeSql(key)}' WHERE "key" = '${escapeSql(label)}';`)
.join("\n");
const categoryUpdates = Object.entries(allergens)
.map(
([key, label]) => `UPDATE "category" SET "key" = '${escapeSql(key)}' WHERE "key" = '${escapeSql(label)}';`,
)
.join("\n");
const ingredientUpdates = Object.entries(ingredients)
.map(
([key, label]) =>
`UPDATE "ingredients" SET "key" = '${escapeSql(key)}' WHERE "key" = '${escapeSql(label)}';`,
)
.join("\n");
const sql = `-- Auto-generated by scripts/generate-catalog-i18n.ts — do not hand-edit.
-- Backfills the "key" column (just renamed from "name" by this migration's
-- preceding statement, so it still holds the old French label) to its slug
-- value, for every row already seeded in a database this migration runs
-- against. A fresh database has none of these rows yet (the seed script
-- inserts by "key" from the start), so this is a no-op there.
${dietUpdates}
${categoryUpdates}
${ingredientUpdates}
`;
writeFileSync(here + "backfill.sql", sql);
console.log(`wrote ${here}backfill.sql`);

View file

@ -0,0 +1,43 @@
/**
* One-off validation, run by hand: checks that `catalog-en-keys.ts` has an
* entry for every diet/allergen/ingredient currently in
* `reference-seed-data.ts`, and that the resulting English keys are unique
* within each table. Not part of the app or the seed itself just a
* pre-flight check while building/editing the dictionary by hand.
*/
import { ALLERGENS, DIETS, INGREDIENT_GROUPS } from "../src/db/reference-seed-data.js";
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
function check(label: string, names: string[]) {
const keys = new Map<string, string>();
const missing: string[] = [];
const duplicates: string[] = [];
for (const name of names) {
let key: string;
try {
key = getEnglishKey(name);
} catch {
missing.push(name);
continue;
}
const existing = keys.get(key);
if (existing !== undefined && existing !== name) {
duplicates.push(`"${existing}" and "${name}" both map to "${key}"`);
}
keys.set(key, name);
}
console.log(`${label}: ${names.length} names, ${keys.size} unique keys`);
if (missing.length > 0) {
console.log(` MISSING (${missing.length}):`, missing);
}
if (duplicates.length > 0) {
console.log(` DUPLICATES (${duplicates.length}):`, duplicates);
}
}
check("Diets", DIETS);
check("Allergens", ALLERGENS.map((a) => a.name));
check(
"Ingredients",
INGREDIENT_GROUPS.flatMap((g) => g.items.map((i) => i.name)),
);

View file

@ -0,0 +1,557 @@
/**
* English key for every catalog reference label `Diet`/`Category`
* (allergens)/`Ingredient` rows must carry a stable, storage-safe `key`
* that is itself English, independent of whatever language the *seed's*
* authoring label (`DIETS`/`ALLERGENS`/`INGREDIENT_GROUPS` in
* `reference-seed-data.ts`, currently French) happens to be in a French
* `key` would tie the identifier to the one language it's meant to be
* decoupled from (see `utils/slugify.ts`'s doc comment and `apps/web`'s
* `locales/fr/translation.json` `catalog.*` namespace, which resolves the
* *display* label from this same key).
*
* Hand-assigned (not machine-translated) an English label is chosen once
* and never changes, exactly like the key it produces (via {@link
* slugify}). Keyed by the exact French authoring label so
* `reference-seed-data.ts` and `scripts/generate-catalog-i18n.ts` can look
* a row's key up by the same string they already have in hand.
*
* `getEnglishKey` throws on a missing entry rather than falling back to
* slugifying the French label a silently-French key defeats the point,
* so a newly-added diet/allergen/ingredient must get an entry here before
* it can seed.
*/
import { slugify } from "../utils/slugify.js";
const DIET_KEYS: Record<string, string> = {
Omnivore: "omnivore",
Végétarien: "vegetarian",
Végan: "vegan",
Pescétarien: "pescatarian",
"Sans gluten": "gluten_free",
};
const ALLERGEN_KEYS: Record<string, string> = {
Gluten: "gluten",
Crustacés: "crustaceans",
Œufs: "eggs",
Poissons: "fish",
Arachides: "peanuts",
Soja: "soy",
Lait: "milk",
"Fruits à coque": "tree_nuts",
Céleri: "celery",
Moutarde: "mustard",
"Graines de sésame": "sesame_seeds",
Sulfites: "sulfites",
Lupin: "lupin",
Mollusques: "molluscs",
};
// Grouped by (category, subcategory), mirroring `INGREDIENT_GROUPS` in
// reference-seed-data.ts, purely so a translator can find/check an entry
// against its source group — this is one flat lookup table at runtime.
const INGREDIENT_KEYS: Record<string, string> = {
// --- Produits frais / Légumes ---------------------------------------
Tomate: "tomato",
Oignon: "onion",
Échalote: "shallot",
Ail: "garlic",
Carotte: "carrot",
Courgette: "zucchini",
Concombre: "cucumber",
Cornichons: "gherkins",
Poivron: "bell_pepper",
Champignon: "mushroom",
Cèpes: "porcini",
Aubergine: "eggplant",
Brocoli: "broccoli",
"Chou-fleur": "cauliflower",
"Chou blanc": "white_cabbage",
"Chou rouge": "red_cabbage",
"Chou de Bruxelles": "brussels_sprouts",
Épinard: "spinach",
Blette: "swiss_chard",
Salade: "lettuce",
Roquette: "arugula",
Cresson: "watercress",
Poireau: "leek",
Radis: "radish",
Betterave: "beetroot",
Navet: "turnip",
Panais: "parsnip",
"Haricot vert": "green_bean",
"Petit pois": "pea",
Maïs: "corn",
Artichaut: "artichoke",
Fenouil: "fennel",
Endive: "endive",
Potiron: "pumpkin",
Butternut: "butternut_squash",
Asperge: "asparagus",
Avocat: "avocado",
"Pomme de terre": "potato",
"Patate douce": "sweet_potato",
"Tomates cerises": "cherry_tomato",
"Pak-choï": "bok_choy",
"Germes de soja": "soybean_sprouts",
Shiitake: "shiitake",
Daikon: "daikon",
"Piment vert frais": "fresh_green_chili",
// --- Produits frais / Fruits -----------------------------------------
Citron: "lemon",
"Citron vert": "lime",
Pomme: "apple",
Poire: "pear",
Banane: "banana",
Orange: "orange",
Clémentine: "clementine",
Pamplemousse: "grapefruit",
Fraise: "strawberry",
Framboise: "raspberry",
Myrtille: "blueberry",
Mûre: "blackberry",
Cerise: "cherry",
Abricot: "apricot",
Pêche: "peach",
Prune: "plum",
Raisin: "grape",
Melon: "melon",
Pastèque: "watermelon",
Ananas: "pineapple",
Mangue: "mango",
Kiwi: "kiwi",
Figue: "fig",
Datte: "date",
Litchi: "lychee",
Grenade: "pomegranate",
Rhubarbe: "rhubarb",
Coing: "quince",
// --- Produits frais / Herbes fraîches ---------------------------------
Basilic: "basil",
Persil: "parsley",
Thym: "thyme",
Romarin: "rosemary",
Laurier: "bay_leaf",
Ciboulette: "chives",
"Coriandre fraîche": "fresh_cilantro",
Menthe: "mint",
Origan: "oregano",
Aneth: "dill",
Estragon: "tarragon",
Sarriette: "savory",
Marjolaine: "marjoram",
Sauge: "sage",
Cerfeuil: "chervil",
Gingembre: "ginger",
Citronnelle: "lemongrass",
Combava: "kaffir_lime",
// --- Boucherie & poissonnerie / Viandes -------------------------------
Lapin: "rabbit",
"Bœuf haché": "ground_beef",
"Steak de bœuf": "beef_steak",
"Rôti de bœuf": "beef_roast",
"Escalope de veau": "veal_cutlet",
"Filet mignon de porc": "pork_tenderloin",
"Côte de porc": "pork_chop",
Agneau: "lamb",
"Gigot d'agneau": "leg_of_lamb",
Lardons: "bacon_lardons",
Bacon: "bacon",
"Jambon blanc": "ham",
"Jambon cru": "cured_ham",
Saucisse: "sausage",
Chorizo: "chorizo",
Merguez: "merguez",
Prosciutto: "prosciutto",
Pancetta: "pancetta",
Mortadelle: "mortadella",
Salami: "salami",
// --- Boucherie & poissonnerie / Volailles -----------------------------
Poulet: "chicken",
Dinde: "turkey",
Canard: "duck",
"Magret de canard": "duck_breast",
// --- Boucherie & poissonnerie / Poissons -------------------------------
Saumon: "salmon",
Thon: "tuna",
Cabillaud: "cod",
Truite: "trout",
Sardine: "sardine",
Anchois: "anchovy",
Merlan: "whiting",
Surimi: "surimi",
"Bar (loup de mer)": "sea_bass",
Dorade: "sea_bream",
Sole: "sole",
Turbot: "turbot",
Merlu: "hake",
Colin: "pollock",
"Lieu noir": "saithe",
Églefin: "haddock",
Maquereau: "mackerel",
Hareng: "herring",
Rouget: "red_mullet",
Raie: "skate",
Lotte: "monkfish",
Flétan: "halibut",
Espadon: "swordfish",
Carpe: "carp",
Brochet: "pike",
Perche: "perch",
Tilapia: "tilapia",
Panga: "pangasius",
"Saumon fumé": "smoked_salmon",
"Poisson séché": "dried_fish",
// --- Boucherie & poissonnerie / Crustacés & fruits de mer -------------
Crevettes: "shrimp",
Langoustines: "langoustine",
Homard: "lobster",
Crabe: "crab",
Langouste: "spiny_lobster",
Moules: "mussels",
Huîtres: "oysters",
"Saint-Jacques": "scallops",
Calamar: "squid",
Poulpe: "octopus",
Palourdes: "clams",
Bulots: "whelks",
// --- Épicerie sèche / Féculents ----------------------------------------
Semoule: "semolina",
Couscous: "couscous",
Boulgour: "bulgur",
Polenta: "polenta",
Quinoa: "quinoa",
Pâtes: "pasta",
"Pâtes complètes": "whole_wheat_pasta",
Riz: "rice",
"Riz basmati": "basmati_rice",
"Riz complet": "brown_rice",
"Flocons d'avoine": "oats",
Spaghetti: "spaghetti",
Penne: "penne",
Tagliatelles: "tagliatelle",
"Lasagnes (feuilles)": "lasagna_sheets",
Gnocchi: "gnocchi",
"Riz arborio": "arborio_rice",
"Nouilles de riz": "rice_noodles",
"Nouilles udon": "udon_noodles",
"Nouilles soba": "soba_noodles",
"Nouilles chinoises": "chinese_noodles",
"Vermicelles de riz": "rice_vermicelli",
"Vermicelles de soja": "soy_vermicelli",
"Riz gluant": "sticky_rice",
"Riz à sushi": "sushi_rice",
"Riz jasmin": "jasmine_rice",
// --- Épicerie sèche / Légumineuses --------------------------------------
"Lentilles vertes": "green_lentils",
"Lentilles corail": "red_lentils",
"Pois chiches": "chickpeas",
"Haricots blancs": "white_beans",
"Haricots rouges": "kidney_beans",
"Haricots noirs": "black_beans",
"Pois cassés": "split_peas",
Fèves: "fava_beans",
Edamame: "edamame",
"Haricots pinto": "pinto_beans",
// --- Épicerie sèche / Graines & fruits secs ----------------------------
Cacahuètes: "peanuts_shelled",
Amandes: "almonds",
Noix: "walnuts",
Noisettes: "hazelnuts",
"Noix de cajou": "cashews",
Pistaches: "pistachios",
"Noix de pécan": "pecans",
"Poudre d'amande": "almond_powder",
"Pignons de pin": "pine_nuts",
"Graines de tournesol": "sunflower_seeds",
"Graines de courge": "pumpkin_seeds",
"Noix de coco râpée": "shredded_coconut",
"Raisins secs": "raisins",
Pruneaux: "prunes",
"Abricots secs": "dried_apricots",
// --- Épicerie sèche / Autres --------------------------------------------
"Champignons noirs": "black_mushrooms",
"Algue nori": "nori_seaweed",
"Algue wakamé": "wakame_seaweed",
"Algue kombu": "kombu_seaweed",
"Pousses de bambou": "bamboo_shoots",
"Châtaignes d'eau": "water_chestnuts",
// --- Boulangerie / Pains -------------------------------------------------
Pain: "bread",
"Pain de mie": "sandwich_bread",
"Pain complet": "whole_wheat_bread",
Baguette: "baguette",
"Pain de seigle": "rye_bread",
Chapelure: "breadcrumbs",
"Pain à burger": "burger_bun",
"Pain brioché": "brioche_bun",
"Pain à hot-dog": "hot_dog_bun",
"Pain pita": "pita_bread",
"Pain bagel": "bagel",
Naan: "naan",
"Pain wrap": "wrap_bread",
"Pain viennois": "viennese_bread",
"Pain de campagne": "country_bread",
"Pain aux céréales": "multigrain_bread",
"Petit pain": "bread_roll",
"Pain suédois": "swedish_bread",
"Pain sans gluten": "gluten_free_bread",
Biscotte: "rusk",
Croûtons: "croutons",
Focaccia: "focaccia",
Ciabatta: "ciabatta",
"Tortilla de maïs": "corn_tortilla",
"Tortilla de blé": "wheat_tortilla",
// --- Boulangerie / Pâtes à cuire -----------------------------------------
"Pâte feuilletée": "puff_pastry",
"Pâte brisée": "shortcrust_pastry",
"Pâte à pizza": "pizza_dough",
"Pâte à tarte sablée": "sweet_shortcrust_pastry",
// --- Crémerie & fromage / Produits laitiers ------------------------------
Lait: "milk",
Beurre: "butter",
"Crème fraîche": "creme_fraiche",
"Crème liquide": "liquid_cream",
Fromage: "cheese",
Emmental: "emmental",
Gruyère: "gruyere",
Parmesan: "parmesan",
Mozzarella: "mozzarella",
"Chèvre (fromage)": "goat_cheese",
Feta: "feta",
Comté: "comte",
"Fromage blanc": "fromage_blanc",
Mascarpone: "mascarpone",
Yaourt: "yogurt",
Burrata: "burrata",
Ricotta: "ricotta",
Pecorino: "pecorino",
Gorgonzola: "gorgonzola",
Cheddar: "cheddar",
// --- Crémerie & fromage / Œufs -------------------------------------------
Œuf: "egg",
// --- Crémerie & fromage / Alternatives ------------------------------------
"Lait de coco": "coconut_milk",
"Crème de coco": "coconut_cream",
"Lait d'amande": "almond_milk",
"Lait d'avoine": "oat_milk",
Tofu: "tofu",
"Tofu soyeux": "silken_tofu",
// --- Condiments & épices / Épices -----------------------------------------
"Herbes de Provence": "herbes_de_provence",
"Poivre noir": "black_pepper",
Paprika: "paprika",
"Piment d'Espelette": "espelette_pepper",
"Piment de Cayenne": "cayenne_pepper",
Cumin: "cumin",
"Curry (poudre)": "curry_powder",
Curcuma: "turmeric",
Cannelle: "cinnamon",
Muscade: "nutmeg",
Safran: "saffron",
"Clou de girofle": "clove",
"Vanille (gousse)": "vanilla_bean",
"Poivre blanc": "white_pepper",
"Poivre rose": "pink_pepper",
"Poivre du Sichuan": "sichuan_pepper",
"Paprika fumé": "smoked_paprika",
"Piment oiseau": "bird_eye_chili",
"Baies de genièvre": "juniper_berries",
"Anis étoilé (badiane)": "star_anise",
"Anis vert": "green_anise",
"Graines de fenouil": "fennel_seeds",
Sumac: "sumac",
Nigelle: "nigella",
"Quatre épices": "allspice",
"Colombo (poudre)": "colombo_powder",
Baharat: "baharat",
Raifort: "horseradish",
"Sel aux herbes": "herb_salt",
"Sel de céleri": "celery_salt",
"Fleur de sel": "fleur_de_sel",
Sel: "salt",
"Cinq épices": "five_spice",
"Garam masala": "garam_masala",
"Graines de coriandre": "coriander_seeds",
Cardamome: "cardamom",
Fenugrec: "fenugreek",
"Piment jalapeño": "jalapeno",
"Piment chipotle": "chipotle",
"Piment poblano": "poblano_pepper",
"Piment habanero": "habanero",
"Ras el hanout": "ras_el_hanout",
"Za'atar": "zaatar",
// --- Condiments & épices / Sauces -------------------------------------------
"Sauce soja": "soy_sauce",
Moutarde: "mustard",
Mayonnaise: "mayonnaise",
Ketchup: "ketchup",
Tabasco: "tabasco",
"Sauce Worcestershire": "worcestershire_sauce",
"Sauce nuoc-mâm": "fish_sauce",
Wasabi: "wasabi",
Harissa: "harissa",
"Pâte de curry": "curry_paste",
"Beurre de cacahuète": "peanut_butter",
"Moutarde de Dijon": "dijon_mustard",
"Moutarde à l'ancienne": "wholegrain_mustard",
"Sauce barbecue": "barbecue_sauce",
"Sauce tartare": "tartar_sauce",
"Sauce cocktail": "cocktail_sauce",
"Sauce béarnaise": "bearnaise_sauce",
"Sauce hollandaise": "hollandaise_sauce",
"Sauce béchamel": "bechamel_sauce",
"Sauce teriyaki": "teriyaki_sauce",
"Sauce ponzu": "ponzu_sauce",
Chimichurri: "chimichurri",
"Pesto rouge (tomates séchées)": "red_pesto",
Pesto: "pesto",
"Sauce huître": "oyster_sauce",
"Sauce hoisin": "hoisin_sauce",
"Sauce sriracha": "sriracha",
"Sauce sweet chili": "sweet_chili_sauce",
Miso: "miso",
"Pâte de crevettes": "shrimp_paste",
"Pâte de curry rouge (thaï)": "red_curry_paste",
"Pâte de curry vert (thaï)": "green_curry_paste",
Tahini: "tahini",
// --- Condiments & épices / Assaisonnements -----------------------------------
"Huile d'olive": "olive_oil",
"Huile de tournesol": "sunflower_oil",
"Huile de colza": "rapeseed_oil",
"Huile de coco": "coconut_oil",
"Huile de sésame": "sesame_oil",
"Vinaigre de cidre": "cider_vinegar",
"Vinaigre blanc": "white_vinegar",
"Vinaigre balsamique": "balsamic_vinegar",
Câpres: "capers",
Olives: "olives",
"Vin blanc (cuisine)": "white_wine",
"Vin rouge (cuisine)": "red_wine",
"Vinaigre de vin rouge": "red_wine_vinegar",
"Vinaigre de vin blanc": "white_wine_vinegar",
"Vinaigre de xérès": "sherry_vinegar",
"Huile de noix": "walnut_oil",
"Huile de noisette": "hazelnut_oil",
"Huile d'arachide": "peanut_oil",
"Huile pimentée": "chili_oil",
"Vinaigre de riz": "rice_vinegar",
Mirin: "mirin",
"Saké (cuisine)": "sake",
"Jus de citron": "lemon_juice",
"Jus de citron vert": "lime_juice",
"Jus d'orange": "orange_juice",
"Jus de pomme": "apple_juice",
"Jus de raisin": "grape_juice",
"Jus de tomate": "tomato_juice",
"Jus de cranberry": "cranberry_juice",
Café: "coffee",
Thé: "tea",
"Bière (cuisine)": "beer",
"Cidre (cuisine)": "cider",
"Champagne / vin pétillant (cuisine)": "champagne",
"Porto (cuisine)": "port_wine",
"Vin jaune (cuisine)": "vin_jaune",
Cognac: "cognac",
Rhum: "rum",
Whisky: "whisky",
Vodka: "vodka",
// --- Aides culinaires / Bases -------------------------------------------
"Farine de blé": "wheat_flour",
"Farine complète": "whole_wheat_flour",
"Farine de maïs": "corn_flour",
"Farine de sarrasin": "buckwheat_flour",
"Farine de riz": "rice_flour",
"Bouillon cube légumes": "vegetable_stock_cube",
"Bouillon cube volaille": "chicken_stock_cube",
"Concentré de tomate": "tomato_paste",
"Coulis de tomate": "tomato_coulis",
"Tomates pelées (conserve)": "canned_peeled_tomatoes",
"Tomates séchées": "sun_dried_tomatoes",
"Fond de veau": "veal_stock",
"Fond de volaille": "chicken_stock",
"Bouillon cube bœuf": "beef_stock_cube",
"Bouillon cube poisson": "fish_stock_cube",
"Bouillon de légumes": "vegetable_broth",
"Bouillon de volaille": "chicken_broth",
"Bouillon de bœuf": "beef_broth",
"Court-bouillon": "court_bouillon",
"Dashi (bouillon japonais)": "dashi",
"Bisque de crustacés": "shellfish_bisque",
"Farine de tapioca": "tapioca_flour",
"Masa harina": "masa_harina",
Eau: "water",
"Eau gazeuse": "sparkling_water",
"Eau de fleur d'oranger": "orange_blossom_water",
"Eau de rose": "rose_water",
"Fumet de poisson": "fish_fumet",
// --- Aides culinaires / Épaississants -------------------------------------
"Levure boulangère": "bakers_yeast",
"Levure chimique": "baking_powder",
Maïzena: "cornstarch",
"Farine de lupin": "lupin_flour",
Gélatine: "gelatin",
"Bicarbonate de soude": "baking_soda",
"Fécule de pomme de terre": "potato_starch",
// --- Aides culinaires / Sucres ---------------------------------------------
Sucre: "sugar",
Miel: "honey",
"Sirop d'érable": "maple_syrup",
"Sucre roux": "brown_sugar",
"Sucre glace": "powdered_sugar",
Cassonade: "demerara_sugar",
"Chocolat noir": "dark_chocolate",
"Chocolat au lait": "milk_chocolate",
"Chocolat blanc": "white_chocolate",
"Pépites de chocolat": "chocolate_chips",
"Cacao en poudre": "cocoa_powder",
"Extrait de vanille": "vanilla_extract",
"Sucre de palme": "palm_sugar",
"Sirop de sucre de canne": "cane_syrup",
};
const ALL_KEYS: Record<string, string> = {
...DIET_KEYS,
...ALLERGEN_KEYS,
...INGREDIENT_KEYS,
};
/**
* Resolves a seed-time French authoring label to its English `key`
* throws if it's missing an entry above rather than silently falling back
* to a French slug, since a new diet/allergen/ingredient needs a
* deliberately-chosen English key before it can seed at all.
* `slugify` still runs over the result so a stray character/casing slip in
* the table above can't produce a key that doesn't match the
* `[a-z0-9_]`-only shape every other key has.
*/
export function getEnglishKey(frenchLabel: string): string {
const english = ALL_KEYS[frenchLabel];
if (english === undefined) {
throw new Error(
`No English key registered for "${frenchLabel}" — add one to catalog-en-keys.ts`,
);
}
return slugify(english);
}

View file

@ -5,7 +5,7 @@ import type {
IngredientSubcategory,
PrismaClient,
} from "@prisma/client";
import { slugify } from "../utils/slugify.js";
import { getEnglishKey } from "./catalog-en-keys.js";
// Short, optional-to-pick regime list — `UserProfile.dietId` stays
// nullable, this is not meant to be exhaustive. Exported for
@ -890,14 +890,16 @@ const INGREDIENTS: Array<
*
* Every `name` below (`DIETS`, `ALLERGENS`, `INGREDIENT_GROUPS`) is an
* *authoring* label, never written to the database or seen by a client
* {@link slugify} derives each row's real, stable `key` from it once, up
* front. The database only ever stores that slug; the French label itself
* lives in `apps/web`'s `locales/fr/translation.json` (`catalog.*`
* {@link getEnglishKey} resolves each row's real, stable, English `key`
* from it (see `catalog-en-keys.ts` for why the key must be English even
* though this file's labels are French). The database only ever stores
* that key; the French label itself lives in `apps/web`'s
* `locales/fr/translation.json` (`catalog.*`
* namespace, kept in sync by `scripts/generate-catalog-i18n.ts`).
*/
export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
for (const name of DIETS) {
const key = slugify(name);
const key = getEnglishKey(name);
await prisma.diet.upsert({ where: { key }, update: {}, create: { key } });
}
@ -908,7 +910,7 @@ export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
// must correct `kind` on an already-existing category if the
// classification above ever changes, not just skip it.
for (const { name, kind } of ALLERGENS) {
const key = slugify(name);
const key = getEnglishKey(name);
const category = await prisma.category.upsert({
where: { key },
update: { kind },
@ -929,18 +931,18 @@ export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
// test-suite case) that's zero updates, on a real re-deploy it's however
// many rows were edited in code since the last deploy, never the full
// list.
const ingredientKeys = INGREDIENTS.map((i) => slugify(i.name));
const ingredientKeys = INGREDIENTS.map((i) => getEnglishKey(i.name));
const existingIngredients = await prisma.ingredient.findMany({
where: { key: { in: ingredientKeys } },
select: { id: true, key: true, icon: true, category: true, subcategory: true },
});
const existingByKey = new Map(existingIngredients.map((i) => [i.key, i]));
const missingIngredients = INGREDIENTS.filter((i) => !existingByKey.has(slugify(i.name)));
const missingIngredients = INGREDIENTS.filter((i) => !existingByKey.has(getEnglishKey(i.name)));
if (missingIngredients.length > 0) {
await prisma.ingredient.createMany({
data: missingIngredients.map(({ name, icon, category, subcategory }) => ({
key: slugify(name),
key: getEnglishKey(name),
icon,
category,
subcategory,
@ -949,7 +951,7 @@ export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
}
const changed = INGREDIENTS.filter((i) => {
const existing = existingByKey.get(slugify(i.name));
const existing = existingByKey.get(getEnglishKey(i.name));
return (
existing &&
(existing.icon !== i.icon ||
@ -958,7 +960,7 @@ export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
);
});
for (const { name, icon, category, subcategory } of changed) {
await prisma.ingredient.update({ where: { key: slugify(name) }, data: { icon, category, subcategory } });
await prisma.ingredient.update({ where: { key: getEnglishKey(name) }, data: { icon, category, subcategory } });
}
// Re-resolve every ingredient's id (existing + just-created) and every
@ -977,10 +979,10 @@ export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
const links: Array<{ ingredientId: number; allergyId: number }> = [];
for (const { name, allergenNames } of INGREDIENTS) {
const ingredientId = ingredientIdByKey.get(slugify(name));
const ingredientId = ingredientIdByKey.get(getEnglishKey(name));
if (ingredientId === undefined) continue;
for (const allergenName of allergenNames) {
const allergyId = allergyIdByCategoryKey.get(slugify(allergenName));
const allergyId = allergyIdByCategoryKey.get(getEnglishKey(allergenName));
if (allergyId !== undefined) links.push({ ingredientId, allergyId });
}
}
@ -996,10 +998,10 @@ export async function seedReferenceData(prisma: PrismaClient): Promise<void> {
const dietLinks: Array<{ ingredientId: number; dietId: number }> = [];
for (const { name, dietNames } of INGREDIENTS) {
const ingredientId = ingredientIdByKey.get(slugify(name));
const ingredientId = ingredientIdByKey.get(getEnglishKey(name));
if (ingredientId === undefined) continue;
for (const dietName of dietNames) {
const dietId = dietIdByKey.get(slugify(dietName));
const dietId = dietIdByKey.get(getEnglishKey(dietName));
if (dietId !== undefined) dietLinks.push({ ingredientId, dietId });
}
}

View file

@ -4,7 +4,7 @@ import { expect } from "chai";
import request from "supertest";
import { createApp } from "../src/app.js";
import { prisma } from "../src/db/prisma.js";
import { slugify } from "../src/utils/slugify.js";
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
import { resetDatabase } from "../test-support/reset-db.js";
function buildSignupPayload(): SignupInput {
@ -40,7 +40,7 @@ describe("Profile", () => {
it("sets the profile's regime to a valid, seeded diet", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const diet = await prisma.diet.findFirstOrThrow({ where: { key: slugify("Végétarien") } });
const diet = await prisma.diet.findFirstOrThrow({ where: { key: getEnglishKey("Végétarien") } });
const res = await agent.patch("/profile/diet").send({ dietId: diet.id });
@ -51,7 +51,7 @@ describe("Profile", () => {
it("clears the regime when dietId is null", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const diet = await prisma.diet.findFirstOrThrow({ where: { key: slugify("Végan") } });
const diet = await prisma.diet.findFirstOrThrow({ where: { key: getEnglishKey("Végan") } });
await agent.patch("/profile/diet").send({ dietId: diet.id });
const res = await agent.patch("/profile/diet").send({ dietId: null });
@ -84,8 +84,8 @@ describe("Profile", () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const allergies = await prisma.allergy.findMany({ include: { category: true } });
const peanuts = allergies.find((a) => a.category.key === slugify("Arachides"));
const gluten = allergies.find((a) => a.category.key === slugify("Gluten"));
const peanuts = allergies.find((a) => a.category.key === getEnglishKey("Arachides"));
const gluten = allergies.find((a) => a.category.key === getEnglishKey("Gluten"));
if (!peanuts || !gluten) throw new Error("expected seeded allergens missing");
const initial = await agent.get("/profile/allergies");
@ -105,8 +105,8 @@ describe("Profile", () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const allergies = await prisma.allergy.findMany({ include: { category: true } });
const peanuts = allergies.find((a) => a.category.key === slugify("Arachides"));
const gluten = allergies.find((a) => a.category.key === slugify("Gluten"));
const peanuts = allergies.find((a) => a.category.key === getEnglishKey("Arachides"));
const gluten = allergies.find((a) => a.category.key === getEnglishKey("Gluten"));
if (!peanuts || !gluten) throw new Error("expected seeded allergens missing");
await agent.patch("/profile/allergies").send({ allergyIds: [peanuts.id] });
@ -141,8 +141,8 @@ describe("Profile", () => {
it("starts empty, then reflects a saved selection", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const tomate = await prisma.ingredient.findFirstOrThrow({ where: { key: slugify("Tomate") } });
const oignon = await prisma.ingredient.findFirstOrThrow({ where: { key: slugify("Oignon") } });
const tomate = await prisma.ingredient.findFirstOrThrow({ where: { key: getEnglishKey("Tomate") } });
const oignon = await prisma.ingredient.findFirstOrThrow({ where: { key: getEnglishKey("Oignon") } });
const initial = await agent.get("/profile/disliked-ingredients");
expect(initial.body).to.deep.equal([]);
@ -160,8 +160,8 @@ describe("Profile", () => {
it("replaces (not merges) the previous selection", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const tomate = await prisma.ingredient.findFirstOrThrow({ where: { key: slugify("Tomate") } });
const oignon = await prisma.ingredient.findFirstOrThrow({ where: { key: slugify("Oignon") } });
const tomate = await prisma.ingredient.findFirstOrThrow({ where: { key: getEnglishKey("Tomate") } });
const oignon = await prisma.ingredient.findFirstOrThrow({ where: { key: getEnglishKey("Oignon") } });
await agent
.patch("/profile/disliked-ingredients")

View file

@ -5,7 +5,7 @@ import { expect } from "chai";
import request from "supertest";
import { createApp } from "../src/app.js";
import { prisma } from "../src/db/prisma.js";
import { slugify } from "../src/utils/slugify.js";
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
import { resetDatabase } from "../test-support/reset-db.js";
/** See `auth.test.ts` — generated rather than hardcoded, no test fixture looks like a real person's data. */
@ -22,7 +22,7 @@ function buildSignupPayload(): SignupInput {
/** Resolves a reference ingredient's id by its `reference-seed-data.ts` French name (slugified to match its `key`). */
async function ingredientId(name: string): Promise<number> {
const ingredient = await prisma.ingredient.findFirstOrThrow({ where: { key: slugify(name) } });
const ingredient = await prisma.ingredient.findFirstOrThrow({ where: { key: getEnglishKey(name) } });
return ingredient.id;
}
@ -164,7 +164,9 @@ describe("Recipes", () => {
const { agent } = await signup();
const tomate = await ingredientId("Tomate");
const oeuf = await ingredientId("Œuf");
const vegetarien = await prisma.diet.findFirstOrThrow({ where: { key: "vegetarien" } });
const vegetarien = await prisma.diet.findFirstOrThrow({
where: { key: getEnglishKey("Végétarien") },
});
const res = await agent.post("/recipes").send({
name: "Omelette provençale",
@ -184,8 +186,12 @@ describe("Recipes", () => {
res.body.steps.map((s: { description: string; order: number }) => s.order),
).to.deep.equal([0, 1]);
// Allergens aggregated across ingredients — "Œuf" carries "Œufs".
expect(res.body.allergens.map((a: { key: string }) => a.key)).to.include("oeufs");
expect(res.body.diets.map((d: { key: string }) => d.key)).to.deep.equal(["vegetarien"]);
expect(res.body.allergens.map((a: { key: string }) => a.key)).to.include(
getEnglishKey("Œufs"),
);
expect(res.body.diets.map((d: { key: string }) => d.key)).to.deep.equal([
getEnglishKey("Végétarien"),
]);
});
it("defaults to PERSONAL visibility, and stamps the author's current household", async () => {
@ -280,7 +286,7 @@ describe("Recipes", () => {
expect(res.status).to.equal(200);
expect(res.body.name).to.equal("Salade");
expect(res.body.ingredients[0].ingredient.key).to.equal("tomate");
expect(res.body.ingredients[0].ingredient.key).to.equal(getEnglishKey("Tomate"));
expect(res.body.isFavorite).to.equal(false);
});
@ -365,7 +371,7 @@ describe("Recipes", () => {
expect(res.body.name).to.equal("Salade composée");
expect(res.body.visibility).to.equal("PUBLIC");
expect(res.body.ingredients).to.have.length(1);
expect(res.body.ingredients[0].ingredient.key).to.equal("oignon");
expect(res.body.ingredients[0].ingredient.key).to.equal(getEnglishKey("Oignon"));
expect(res.body.steps).to.have.length(2);
});

View file

@ -3,7 +3,7 @@ import request from "supertest";
import { createApp } from "../src/app.js";
import { prisma } from "../src/db/prisma.js";
import { resetDatabase } from "../test-support/reset-db.js";
import { slugify } from "../src/utils/slugify.js";
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
describe("Reference data", () => {
const app = createApp();
@ -22,7 +22,7 @@ describe("Reference data", () => {
expect(res.status).to.equal(200);
expect(res.body).to.have.length(5);
expect(res.body.map((d: { key: string }) => d.key)).to.include(slugify("Végétarien"));
expect(res.body.map((d: { key: string }) => d.key)).to.include(getEnglishKey("Végétarien"));
expect(res.body[0]).to.have.keys(["id", "key"]);
});
});
@ -33,7 +33,7 @@ describe("Reference data", () => {
expect(res.status).to.equal(200);
expect(res.body).to.have.length(14);
expect(res.body.map((a: { key: string }) => a.key)).to.include(slugify("Arachides"));
expect(res.body.map((a: { key: string }) => a.key)).to.include(getEnglishKey("Arachides"));
expect(res.body[0]).to.have.keys(["id", "key", "kind"]);
});
@ -41,7 +41,7 @@ describe("Reference data", () => {
const res = await request(app).get("/reference/allergies");
const byKey = (name: string) =>
res.body.find((a: { key: string }) => a.key === slugify(name));
res.body.find((a: { key: string }) => a.key === getEnglishKey(name));
expect(byKey("Gluten").kind).to.equal("INTOLERANCE");
expect(byKey("Sulfites").kind).to.equal("INTOLERANCE");
expect(byKey("Arachides").kind).to.equal("ALLERGY");
@ -55,7 +55,7 @@ describe("Reference data", () => {
expect(res.status).to.equal(200);
expect(res.body.length).to.be.greaterThan(0);
expect(res.body.map((i: { key: string }) => i.key)).to.include(slugify("Tomate"));
expect(res.body.map((i: { key: string }) => i.key)).to.include(getEnglishKey("Tomate"));
expect(res.body[0]).to.have.keys([
"id",
"key",
@ -71,9 +71,9 @@ describe("Reference data", () => {
const res = await request(app).get("/reference/ingredients");
const byKey = (name: string) =>
res.body.find((i: { key: string }) => i.key === slugify(name));
res.body.find((i: { key: string }) => i.key === getEnglishKey(name));
expect(byKey("Œuf").allergens.map((a: { key: string }) => a.key)).to.include(
slugify("Œufs"),
getEnglishKey("Œufs"),
);
expect(byKey("Tomate").allergens).to.deep.equal([]);
});

View file

@ -301,465 +301,465 @@
"catalog": {
"diets": {
"omnivore": "Omnivore",
"vegetarien": "Végétarien",
"vegetarian": "Végétarien",
"vegan": "Végan",
"pescetarien": "Pescétarien",
"sans_gluten": "Sans gluten"
"pescatarian": "Pescétarien",
"gluten_free": "Sans gluten"
},
"allergens": {
"gluten": "Gluten",
"crustaces": "Crustacés",
"oeufs": "Œufs",
"poissons": "Poissons",
"arachides": "Arachides",
"soja": "Soja",
"lait": "Lait",
"fruits_a_coque": "Fruits à coque",
"celeri": "Céleri",
"moutarde": "Moutarde",
"graines_de_sesame": "Graines de sésame",
"crustaceans": "Crustacés",
"eggs": "Œufs",
"fish": "Poissons",
"peanuts": "Arachides",
"soy": "Soja",
"milk": "Lait",
"tree_nuts": "Fruits à coque",
"celery": "Céleri",
"mustard": "Moutarde",
"sesame_seeds": "Graines de sésame",
"sulfites": "Sulfites",
"lupin": "Lupin",
"mollusques": "Mollusques"
"molluscs": "Mollusques"
},
"ingredients": {
"tomate": "Tomate",
"oignon": "Oignon",
"echalote": "Échalote",
"ail": "Ail",
"carotte": "Carotte",
"courgette": "Courgette",
"concombre": "Concombre",
"cornichons": "Cornichons",
"poivron": "Poivron",
"champignon": "Champignon",
"cepes": "Cèpes",
"aubergine": "Aubergine",
"brocoli": "Brocoli",
"chou_fleur": "Chou-fleur",
"chou_blanc": "Chou blanc",
"chou_rouge": "Chou rouge",
"chou_de_bruxelles": "Chou de Bruxelles",
"epinard": "Épinard",
"blette": "Blette",
"salade": "Salade",
"roquette": "Roquette",
"cresson": "Cresson",
"poireau": "Poireau",
"celeri": "Céleri",
"radis": "Radis",
"betterave": "Betterave",
"navet": "Navet",
"panais": "Panais",
"haricot_vert": "Haricot vert",
"petit_pois": "Petit pois",
"mais": "Maïs",
"artichaut": "Artichaut",
"fenouil": "Fenouil",
"tomato": "Tomate",
"onion": "Oignon",
"shallot": "Échalote",
"garlic": "Ail",
"carrot": "Carotte",
"zucchini": "Courgette",
"cucumber": "Concombre",
"gherkins": "Cornichons",
"bell_pepper": "Poivron",
"mushroom": "Champignon",
"porcini": "Cèpes",
"eggplant": "Aubergine",
"broccoli": "Brocoli",
"cauliflower": "Chou-fleur",
"white_cabbage": "Chou blanc",
"red_cabbage": "Chou rouge",
"brussels_sprouts": "Chou de Bruxelles",
"spinach": "Épinard",
"swiss_chard": "Blette",
"lettuce": "Salade",
"arugula": "Roquette",
"watercress": "Cresson",
"leek": "Poireau",
"celery": "Céleri",
"radish": "Radis",
"beetroot": "Betterave",
"turnip": "Navet",
"parsnip": "Panais",
"green_bean": "Haricot vert",
"pea": "Petit pois",
"corn": "Maïs",
"artichoke": "Artichaut",
"fennel": "Fenouil",
"endive": "Endive",
"potiron": "Potiron",
"butternut": "Butternut",
"asperge": "Asperge",
"avocat": "Avocat",
"pomme_de_terre": "Pomme de terre",
"patate_douce": "Patate douce",
"tomates_cerises": "Tomates cerises",
"pak_choi": "Pak-choï",
"germes_de_soja": "Germes de soja",
"pumpkin": "Potiron",
"butternut_squash": "Butternut",
"asparagus": "Asperge",
"avocado": "Avocat",
"potato": "Pomme de terre",
"sweet_potato": "Patate douce",
"cherry_tomato": "Tomates cerises",
"bok_choy": "Pak-choï",
"soybean_sprouts": "Germes de soja",
"shiitake": "Shiitake",
"daikon": "Daikon",
"piment_vert_frais": "Piment vert frais",
"citron": "Citron",
"citron_vert": "Citron vert",
"pomme": "Pomme",
"poire": "Poire",
"banane": "Banane",
"fresh_green_chili": "Piment vert frais",
"lemon": "Citron",
"lime": "Citron vert",
"apple": "Pomme",
"pear": "Poire",
"banana": "Banane",
"orange": "Orange",
"clementine": "Clémentine",
"pamplemousse": "Pamplemousse",
"fraise": "Fraise",
"framboise": "Framboise",
"myrtille": "Myrtille",
"mure": "Mûre",
"cerise": "Cerise",
"abricot": "Abricot",
"peche": "Pêche",
"prune": "Prune",
"raisin": "Raisin",
"grapefruit": "Pamplemousse",
"strawberry": "Fraise",
"raspberry": "Framboise",
"blueberry": "Myrtille",
"blackberry": "Mûre",
"cherry": "Cerise",
"apricot": "Abricot",
"peach": "Pêche",
"plum": "Prune",
"grape": "Raisin",
"melon": "Melon",
"pasteque": "Pastèque",
"ananas": "Ananas",
"mangue": "Mangue",
"watermelon": "Pastèque",
"pineapple": "Ananas",
"mango": "Mangue",
"kiwi": "Kiwi",
"figue": "Figue",
"datte": "Datte",
"litchi": "Litchi",
"grenade": "Grenade",
"rhubarbe": "Rhubarbe",
"coing": "Coing",
"basilic": "Basilic",
"persil": "Persil",
"thym": "Thym",
"romarin": "Romarin",
"laurier": "Laurier",
"ciboulette": "Ciboulette",
"coriandre_fraiche": "Coriandre fraîche",
"menthe": "Menthe",
"origan": "Origan",
"aneth": "Aneth",
"estragon": "Estragon",
"sarriette": "Sarriette",
"marjolaine": "Marjolaine",
"sauge": "Sauge",
"cerfeuil": "Cerfeuil",
"gingembre": "Gingembre",
"citronnelle": "Citronnelle",
"combava": "Combava",
"lapin": "Lapin",
"boeuf_hache": "Bœuf haché",
"steak_de_boeuf": "Steak de bœuf",
"roti_de_boeuf": "Rôti de bœuf",
"escalope_de_veau": "Escalope de veau",
"filet_mignon_de_porc": "Filet mignon de porc",
"cote_de_porc": "Côte de porc",
"agneau": "Agneau",
"gigot_d_agneau": "Gigot d'agneau",
"lardons": "Lardons",
"fig": "Figue",
"date": "Datte",
"lychee": "Litchi",
"pomegranate": "Grenade",
"rhubarb": "Rhubarbe",
"quince": "Coing",
"basil": "Basilic",
"parsley": "Persil",
"thyme": "Thym",
"rosemary": "Romarin",
"bay_leaf": "Laurier",
"chives": "Ciboulette",
"fresh_cilantro": "Coriandre fraîche",
"mint": "Menthe",
"oregano": "Origan",
"dill": "Aneth",
"tarragon": "Estragon",
"savory": "Sarriette",
"marjoram": "Marjolaine",
"sage": "Sauge",
"chervil": "Cerfeuil",
"ginger": "Gingembre",
"lemongrass": "Citronnelle",
"kaffir_lime": "Combava",
"rabbit": "Lapin",
"ground_beef": "Bœuf haché",
"beef_steak": "Steak de bœuf",
"beef_roast": "Rôti de bœuf",
"veal_cutlet": "Escalope de veau",
"pork_tenderloin": "Filet mignon de porc",
"pork_chop": "Côte de porc",
"lamb": "Agneau",
"leg_of_lamb": "Gigot d'agneau",
"bacon_lardons": "Lardons",
"bacon": "Bacon",
"jambon_blanc": "Jambon blanc",
"jambon_cru": "Jambon cru",
"saucisse": "Saucisse",
"ham": "Jambon blanc",
"cured_ham": "Jambon cru",
"sausage": "Saucisse",
"chorizo": "Chorizo",
"merguez": "Merguez",
"prosciutto": "Prosciutto",
"pancetta": "Pancetta",
"mortadelle": "Mortadelle",
"mortadella": "Mortadelle",
"salami": "Salami",
"poulet": "Poulet",
"dinde": "Dinde",
"canard": "Canard",
"magret_de_canard": "Magret de canard",
"saumon": "Saumon",
"thon": "Thon",
"cabillaud": "Cabillaud",
"truite": "Truite",
"chicken": "Poulet",
"turkey": "Dinde",
"duck": "Canard",
"duck_breast": "Magret de canard",
"salmon": "Saumon",
"tuna": "Thon",
"cod": "Cabillaud",
"trout": "Truite",
"sardine": "Sardine",
"anchois": "Anchois",
"merlan": "Merlan",
"anchovy": "Anchois",
"whiting": "Merlan",
"surimi": "Surimi",
"bar_loup_de_mer": "Bar (loup de mer)",
"dorade": "Dorade",
"sea_bass": "Bar (loup de mer)",
"sea_bream": "Dorade",
"sole": "Sole",
"turbot": "Turbot",
"merlu": "Merlu",
"colin": "Colin",
"lieu_noir": "Lieu noir",
"eglefin": "Églefin",
"maquereau": "Maquereau",
"hareng": "Hareng",
"rouget": "Rouget",
"raie": "Raie",
"lotte": "Lotte",
"fletan": "Flétan",
"espadon": "Espadon",
"carpe": "Carpe",
"brochet": "Brochet",
"perche": "Perche",
"hake": "Merlu",
"pollock": "Colin",
"saithe": "Lieu noir",
"haddock": "Églefin",
"mackerel": "Maquereau",
"herring": "Hareng",
"red_mullet": "Rouget",
"skate": "Raie",
"monkfish": "Lotte",
"halibut": "Flétan",
"swordfish": "Espadon",
"carp": "Carpe",
"pike": "Brochet",
"perch": "Perche",
"tilapia": "Tilapia",
"panga": "Panga",
"saumon_fume": "Saumon fumé",
"poisson_seche": "Poisson séché",
"crevettes": "Crevettes",
"langoustines": "Langoustines",
"homard": "Homard",
"crabe": "Crabe",
"langouste": "Langouste",
"moules": "Moules",
"huitres": "Huîtres",
"saint_jacques": "Saint-Jacques",
"calamar": "Calamar",
"poulpe": "Poulpe",
"palourdes": "Palourdes",
"bulots": "Bulots",
"semoule": "Semoule",
"pangasius": "Panga",
"smoked_salmon": "Saumon fumé",
"dried_fish": "Poisson séché",
"shrimp": "Crevettes",
"langoustine": "Langoustines",
"lobster": "Homard",
"crab": "Crabe",
"spiny_lobster": "Langouste",
"mussels": "Moules",
"oysters": "Huîtres",
"scallops": "Saint-Jacques",
"squid": "Calamar",
"octopus": "Poulpe",
"clams": "Palourdes",
"whelks": "Bulots",
"semolina": "Semoule",
"couscous": "Couscous",
"boulgour": "Boulgour",
"bulgur": "Boulgour",
"polenta": "Polenta",
"quinoa": "Quinoa",
"pates": "Pâtes",
"pates_completes": "Pâtes complètes",
"riz": "Riz",
"riz_basmati": "Riz basmati",
"riz_complet": "Riz complet",
"flocons_d_avoine": "Flocons d'avoine",
"pasta": "Pâtes",
"whole_wheat_pasta": "Pâtes complètes",
"rice": "Riz",
"basmati_rice": "Riz basmati",
"brown_rice": "Riz complet",
"oats": "Flocons d'avoine",
"spaghetti": "Spaghetti",
"penne": "Penne",
"tagliatelles": "Tagliatelles",
"lasagnes_feuilles": "Lasagnes (feuilles)",
"tagliatelle": "Tagliatelles",
"lasagna_sheets": "Lasagnes (feuilles)",
"gnocchi": "Gnocchi",
"riz_arborio": "Riz arborio",
"nouilles_de_riz": "Nouilles de riz",
"nouilles_udon": "Nouilles udon",
"nouilles_soba": "Nouilles soba",
"nouilles_chinoises": "Nouilles chinoises",
"vermicelles_de_riz": "Vermicelles de riz",
"vermicelles_de_soja": "Vermicelles de soja",
"riz_gluant": "Riz gluant",
"riz_a_sushi": "Riz à sushi",
"riz_jasmin": "Riz jasmin",
"lentilles_vertes": "Lentilles vertes",
"lentilles_corail": "Lentilles corail",
"pois_chiches": "Pois chiches",
"haricots_blancs": "Haricots blancs",
"haricots_rouges": "Haricots rouges",
"haricots_noirs": "Haricots noirs",
"pois_casses": "Pois cassés",
"feves": "Fèves",
"arborio_rice": "Riz arborio",
"rice_noodles": "Nouilles de riz",
"udon_noodles": "Nouilles udon",
"soba_noodles": "Nouilles soba",
"chinese_noodles": "Nouilles chinoises",
"rice_vermicelli": "Vermicelles de riz",
"soy_vermicelli": "Vermicelles de soja",
"sticky_rice": "Riz gluant",
"sushi_rice": "Riz à sushi",
"jasmine_rice": "Riz jasmin",
"green_lentils": "Lentilles vertes",
"red_lentils": "Lentilles corail",
"chickpeas": "Pois chiches",
"white_beans": "Haricots blancs",
"kidney_beans": "Haricots rouges",
"black_beans": "Haricots noirs",
"split_peas": "Pois cassés",
"fava_beans": "Fèves",
"edamame": "Edamame",
"haricots_pinto": "Haricots pinto",
"cacahuetes": "Cacahuètes",
"amandes": "Amandes",
"noix": "Noix",
"noisettes": "Noisettes",
"noix_de_cajou": "Noix de cajou",
"pistaches": "Pistaches",
"noix_de_pecan": "Noix de pécan",
"poudre_d_amande": "Poudre d'amande",
"pignons_de_pin": "Pignons de pin",
"graines_de_tournesol": "Graines de tournesol",
"graines_de_courge": "Graines de courge",
"noix_de_coco_rapee": "Noix de coco râpée",
"raisins_secs": "Raisins secs",
"pruneaux": "Pruneaux",
"abricots_secs": "Abricots secs",
"graines_de_sesame": "Graines de sésame",
"champignons_noirs": "Champignons noirs",
"algue_nori": "Algue nori",
"algue_wakame": "Algue wakamé",
"algue_kombu": "Algue kombu",
"pousses_de_bambou": "Pousses de bambou",
"chataignes_d_eau": "Châtaignes d'eau",
"pain": "Pain",
"pain_de_mie": "Pain de mie",
"pain_complet": "Pain complet",
"pinto_beans": "Haricots pinto",
"peanuts_shelled": "Cacahuètes",
"almonds": "Amandes",
"walnuts": "Noix",
"hazelnuts": "Noisettes",
"cashews": "Noix de cajou",
"pistachios": "Pistaches",
"pecans": "Noix de pécan",
"almond_powder": "Poudre d'amande",
"pine_nuts": "Pignons de pin",
"sunflower_seeds": "Graines de tournesol",
"pumpkin_seeds": "Graines de courge",
"shredded_coconut": "Noix de coco râpée",
"raisins": "Raisins secs",
"prunes": "Pruneaux",
"dried_apricots": "Abricots secs",
"sesame_seeds": "Graines de sésame",
"black_mushrooms": "Champignons noirs",
"nori_seaweed": "Algue nori",
"wakame_seaweed": "Algue wakamé",
"kombu_seaweed": "Algue kombu",
"bamboo_shoots": "Pousses de bambou",
"water_chestnuts": "Châtaignes d'eau",
"bread": "Pain",
"sandwich_bread": "Pain de mie",
"whole_wheat_bread": "Pain complet",
"baguette": "Baguette",
"pain_de_seigle": "Pain de seigle",
"chapelure": "Chapelure",
"pain_a_burger": "Pain à burger",
"pain_brioche": "Pain brioché",
"pain_a_hot_dog": "Pain à hot-dog",
"pain_pita": "Pain pita",
"pain_bagel": "Pain bagel",
"rye_bread": "Pain de seigle",
"breadcrumbs": "Chapelure",
"burger_bun": "Pain à burger",
"brioche_bun": "Pain brioché",
"hot_dog_bun": "Pain à hot-dog",
"pita_bread": "Pain pita",
"bagel": "Pain bagel",
"naan": "Naan",
"pain_wrap": "Pain wrap",
"pain_viennois": "Pain viennois",
"pain_de_campagne": "Pain de campagne",
"pain_aux_cereales": "Pain aux céréales",
"petit_pain": "Petit pain",
"pain_suedois": "Pain suédois",
"pain_sans_gluten": "Pain sans gluten",
"biscotte": "Biscotte",
"wrap_bread": "Pain wrap",
"viennese_bread": "Pain viennois",
"country_bread": "Pain de campagne",
"multigrain_bread": "Pain aux céréales",
"bread_roll": "Petit pain",
"swedish_bread": "Pain suédois",
"gluten_free_bread": "Pain sans gluten",
"rusk": "Biscotte",
"croutons": "Croûtons",
"focaccia": "Focaccia",
"ciabatta": "Ciabatta",
"tortilla_de_mais": "Tortilla de maïs",
"tortilla_de_ble": "Tortilla de blé",
"pate_feuilletee": "Pâte feuilletée",
"pate_brisee": "Pâte brisée",
"pate_a_pizza": "Pâte à pizza",
"pate_a_tarte_sablee": "Pâte à tarte sablée",
"lait": "Lait",
"beurre": "Beurre",
"corn_tortilla": "Tortilla de maïs",
"wheat_tortilla": "Tortilla de blé",
"puff_pastry": "Pâte feuilletée",
"shortcrust_pastry": "Pâte brisée",
"pizza_dough": "Pâte à pizza",
"sweet_shortcrust_pastry": "Pâte à tarte sablée",
"milk": "Lait",
"butter": "Beurre",
"creme_fraiche": "Crème fraîche",
"creme_liquide": "Crème liquide",
"fromage": "Fromage",
"liquid_cream": "Crème liquide",
"cheese": "Fromage",
"emmental": "Emmental",
"gruyere": "Gruyère",
"parmesan": "Parmesan",
"mozzarella": "Mozzarella",
"chevre_fromage": "Chèvre (fromage)",
"goat_cheese": "Chèvre (fromage)",
"feta": "Feta",
"comte": "Comté",
"fromage_blanc": "Fromage blanc",
"mascarpone": "Mascarpone",
"yaourt": "Yaourt",
"yogurt": "Yaourt",
"burrata": "Burrata",
"ricotta": "Ricotta",
"pecorino": "Pecorino",
"gorgonzola": "Gorgonzola",
"cheddar": "Cheddar",
"oeuf": "Œuf",
"lait_de_coco": "Lait de coco",
"creme_de_coco": "Crème de coco",
"lait_d_amande": "Lait d'amande",
"lait_d_avoine": "Lait d'avoine",
"egg": "Œuf",
"coconut_milk": "Lait de coco",
"coconut_cream": "Crème de coco",
"almond_milk": "Lait d'amande",
"oat_milk": "Lait d'avoine",
"tofu": "Tofu",
"tofu_soyeux": "Tofu soyeux",
"silken_tofu": "Tofu soyeux",
"herbes_de_provence": "Herbes de Provence",
"poivre_noir": "Poivre noir",
"black_pepper": "Poivre noir",
"paprika": "Paprika",
"piment_d_espelette": "Piment d'Espelette",
"piment_de_cayenne": "Piment de Cayenne",
"espelette_pepper": "Piment d'Espelette",
"cayenne_pepper": "Piment de Cayenne",
"cumin": "Cumin",
"curry_poudre": "Curry (poudre)",
"curcuma": "Curcuma",
"cannelle": "Cannelle",
"muscade": "Muscade",
"safran": "Safran",
"clou_de_girofle": "Clou de girofle",
"vanille_gousse": "Vanille (gousse)",
"poivre_blanc": "Poivre blanc",
"poivre_rose": "Poivre rose",
"poivre_du_sichuan": "Poivre du Sichuan",
"paprika_fume": "Paprika fumé",
"piment_oiseau": "Piment oiseau",
"baies_de_genievre": "Baies de genièvre",
"anis_etoile_badiane": "Anis étoilé (badiane)",
"anis_vert": "Anis vert",
"graines_de_fenouil": "Graines de fenouil",
"curry_powder": "Curry (poudre)",
"turmeric": "Curcuma",
"cinnamon": "Cannelle",
"nutmeg": "Muscade",
"saffron": "Safran",
"clove": "Clou de girofle",
"vanilla_bean": "Vanille (gousse)",
"white_pepper": "Poivre blanc",
"pink_pepper": "Poivre rose",
"sichuan_pepper": "Poivre du Sichuan",
"smoked_paprika": "Paprika fumé",
"bird_eye_chili": "Piment oiseau",
"juniper_berries": "Baies de genièvre",
"star_anise": "Anis étoilé (badiane)",
"green_anise": "Anis vert",
"fennel_seeds": "Graines de fenouil",
"sumac": "Sumac",
"nigelle": "Nigelle",
"quatre_epices": "Quatre épices",
"colombo_poudre": "Colombo (poudre)",
"nigella": "Nigelle",
"allspice": "Quatre épices",
"colombo_powder": "Colombo (poudre)",
"baharat": "Baharat",
"raifort": "Raifort",
"sel_aux_herbes": "Sel aux herbes",
"sel_de_celeri": "Sel de céleri",
"horseradish": "Raifort",
"herb_salt": "Sel aux herbes",
"celery_salt": "Sel de céleri",
"fleur_de_sel": "Fleur de sel",
"sel": "Sel",
"cinq_epices": "Cinq épices",
"salt": "Sel",
"five_spice": "Cinq épices",
"garam_masala": "Garam masala",
"graines_de_coriandre": "Graines de coriandre",
"cardamome": "Cardamome",
"fenugrec": "Fenugrec",
"piment_jalapeno": "Piment jalapeño",
"piment_chipotle": "Piment chipotle",
"piment_poblano": "Piment poblano",
"piment_habanero": "Piment habanero",
"coriander_seeds": "Graines de coriandre",
"cardamom": "Cardamome",
"fenugreek": "Fenugrec",
"jalapeno": "Piment jalapeño",
"chipotle": "Piment chipotle",
"poblano_pepper": "Piment poblano",
"habanero": "Piment habanero",
"ras_el_hanout": "Ras el hanout",
"za_atar": "Za'atar",
"sauce_soja": "Sauce soja",
"moutarde": "Moutarde",
"zaatar": "Za'atar",
"soy_sauce": "Sauce soja",
"mustard": "Moutarde",
"mayonnaise": "Mayonnaise",
"ketchup": "Ketchup",
"tabasco": "Tabasco",
"sauce_worcestershire": "Sauce Worcestershire",
"sauce_nuoc_mam": "Sauce nuoc-mâm",
"worcestershire_sauce": "Sauce Worcestershire",
"fish_sauce": "Sauce nuoc-mâm",
"wasabi": "Wasabi",
"harissa": "Harissa",
"pate_de_curry": "Pâte de curry",
"beurre_de_cacahuete": "Beurre de cacahuète",
"moutarde_de_dijon": "Moutarde de Dijon",
"moutarde_a_l_ancienne": "Moutarde à l'ancienne",
"sauce_barbecue": "Sauce barbecue",
"sauce_tartare": "Sauce tartare",
"sauce_cocktail": "Sauce cocktail",
"sauce_bearnaise": "Sauce béarnaise",
"sauce_hollandaise": "Sauce hollandaise",
"sauce_bechamel": "Sauce béchamel",
"sauce_teriyaki": "Sauce teriyaki",
"sauce_ponzu": "Sauce ponzu",
"curry_paste": "Pâte de curry",
"peanut_butter": "Beurre de cacahuète",
"dijon_mustard": "Moutarde de Dijon",
"wholegrain_mustard": "Moutarde à l'ancienne",
"barbecue_sauce": "Sauce barbecue",
"tartar_sauce": "Sauce tartare",
"cocktail_sauce": "Sauce cocktail",
"bearnaise_sauce": "Sauce béarnaise",
"hollandaise_sauce": "Sauce hollandaise",
"bechamel_sauce": "Sauce béchamel",
"teriyaki_sauce": "Sauce teriyaki",
"ponzu_sauce": "Sauce ponzu",
"chimichurri": "Chimichurri",
"pesto_rouge_tomates_sechees": "Pesto rouge (tomates séchées)",
"red_pesto": "Pesto rouge (tomates séchées)",
"pesto": "Pesto",
"sauce_huitre": "Sauce huître",
"sauce_hoisin": "Sauce hoisin",
"sauce_sriracha": "Sauce sriracha",
"sauce_sweet_chili": "Sauce sweet chili",
"oyster_sauce": "Sauce huître",
"hoisin_sauce": "Sauce hoisin",
"sriracha": "Sauce sriracha",
"sweet_chili_sauce": "Sauce sweet chili",
"miso": "Miso",
"pate_de_crevettes": "Pâte de crevettes",
"pate_de_curry_rouge_thai": "Pâte de curry rouge (thaï)",
"pate_de_curry_vert_thai": "Pâte de curry vert (thaï)",
"shrimp_paste": "Pâte de crevettes",
"red_curry_paste": "Pâte de curry rouge (thaï)",
"green_curry_paste": "Pâte de curry vert (thaï)",
"tahini": "Tahini",
"huile_d_olive": "Huile d'olive",
"huile_de_tournesol": "Huile de tournesol",
"huile_de_colza": "Huile de colza",
"huile_de_coco": "Huile de coco",
"huile_de_sesame": "Huile de sésame",
"vinaigre_de_cidre": "Vinaigre de cidre",
"vinaigre_blanc": "Vinaigre blanc",
"vinaigre_balsamique": "Vinaigre balsamique",
"capres": "Câpres",
"olive_oil": "Huile d'olive",
"sunflower_oil": "Huile de tournesol",
"rapeseed_oil": "Huile de colza",
"coconut_oil": "Huile de coco",
"sesame_oil": "Huile de sésame",
"cider_vinegar": "Vinaigre de cidre",
"white_vinegar": "Vinaigre blanc",
"balsamic_vinegar": "Vinaigre balsamique",
"capers": "Câpres",
"olives": "Olives",
"vin_blanc_cuisine": "Vin blanc (cuisine)",
"vin_rouge_cuisine": "Vin rouge (cuisine)",
"vinaigre_de_vin_rouge": "Vinaigre de vin rouge",
"vinaigre_de_vin_blanc": "Vinaigre de vin blanc",
"vinaigre_de_xeres": "Vinaigre de xérès",
"huile_de_noix": "Huile de noix",
"huile_de_noisette": "Huile de noisette",
"huile_d_arachide": "Huile d'arachide",
"huile_pimentee": "Huile pimentée",
"vinaigre_de_riz": "Vinaigre de riz",
"white_wine": "Vin blanc (cuisine)",
"red_wine": "Vin rouge (cuisine)",
"red_wine_vinegar": "Vinaigre de vin rouge",
"white_wine_vinegar": "Vinaigre de vin blanc",
"sherry_vinegar": "Vinaigre de xérès",
"walnut_oil": "Huile de noix",
"hazelnut_oil": "Huile de noisette",
"peanut_oil": "Huile d'arachide",
"chili_oil": "Huile pimentée",
"rice_vinegar": "Vinaigre de riz",
"mirin": "Mirin",
"sake_cuisine": "Saké (cuisine)",
"jus_de_citron": "Jus de citron",
"jus_de_citron_vert": "Jus de citron vert",
"jus_d_orange": "Jus d'orange",
"jus_de_pomme": "Jus de pomme",
"jus_de_raisin": "Jus de raisin",
"jus_de_tomate": "Jus de tomate",
"jus_de_cranberry": "Jus de cranberry",
"cafe": "Café",
"the": "Thé",
"biere_cuisine": "Bière (cuisine)",
"cidre_cuisine": "Cidre (cuisine)",
"champagne_vin_petillant_cuisine": "Champagne / vin pétillant (cuisine)",
"porto_cuisine": "Porto (cuisine)",
"vin_jaune_cuisine": "Vin jaune (cuisine)",
"sake": "Saké (cuisine)",
"lemon_juice": "Jus de citron",
"lime_juice": "Jus de citron vert",
"orange_juice": "Jus d'orange",
"apple_juice": "Jus de pomme",
"grape_juice": "Jus de raisin",
"tomato_juice": "Jus de tomate",
"cranberry_juice": "Jus de cranberry",
"coffee": "Café",
"tea": "Thé",
"beer": "Bière (cuisine)",
"cider": "Cidre (cuisine)",
"champagne": "Champagne / vin pétillant (cuisine)",
"port_wine": "Porto (cuisine)",
"vin_jaune": "Vin jaune (cuisine)",
"cognac": "Cognac",
"rhum": "Rhum",
"rum": "Rhum",
"whisky": "Whisky",
"vodka": "Vodka",
"farine_de_ble": "Farine de blé",
"farine_complete": "Farine complète",
"farine_de_mais": "Farine de maïs",
"farine_de_sarrasin": "Farine de sarrasin",
"farine_de_riz": "Farine de riz",
"bouillon_cube_legumes": "Bouillon cube légumes",
"bouillon_cube_volaille": "Bouillon cube volaille",
"concentre_de_tomate": "Concentré de tomate",
"coulis_de_tomate": "Coulis de tomate",
"tomates_pelees_conserve": "Tomates pelées (conserve)",
"tomates_sechees": "Tomates séchées",
"fond_de_veau": "Fond de veau",
"fond_de_volaille": "Fond de volaille",
"bouillon_cube_boeuf": "Bouillon cube bœuf",
"bouillon_cube_poisson": "Bouillon cube poisson",
"bouillon_de_legumes": "Bouillon de légumes",
"bouillon_de_volaille": "Bouillon de volaille",
"bouillon_de_boeuf": "Bouillon de bœuf",
"wheat_flour": "Farine de blé",
"whole_wheat_flour": "Farine complète",
"corn_flour": "Farine de maïs",
"buckwheat_flour": "Farine de sarrasin",
"rice_flour": "Farine de riz",
"vegetable_stock_cube": "Bouillon cube légumes",
"chicken_stock_cube": "Bouillon cube volaille",
"tomato_paste": "Concentré de tomate",
"tomato_coulis": "Coulis de tomate",
"canned_peeled_tomatoes": "Tomates pelées (conserve)",
"sun_dried_tomatoes": "Tomates séchées",
"veal_stock": "Fond de veau",
"chicken_stock": "Fond de volaille",
"beef_stock_cube": "Bouillon cube bœuf",
"fish_stock_cube": "Bouillon cube poisson",
"vegetable_broth": "Bouillon de légumes",
"chicken_broth": "Bouillon de volaille",
"beef_broth": "Bouillon de bœuf",
"court_bouillon": "Court-bouillon",
"dashi_bouillon_japonais": "Dashi (bouillon japonais)",
"bisque_de_crustaces": "Bisque de crustacés",
"farine_de_tapioca": "Farine de tapioca",
"dashi": "Dashi (bouillon japonais)",
"shellfish_bisque": "Bisque de crustacés",
"tapioca_flour": "Farine de tapioca",
"masa_harina": "Masa harina",
"eau": "Eau",
"eau_gazeuse": "Eau gazeuse",
"eau_de_fleur_d_oranger": "Eau de fleur d'oranger",
"eau_de_rose": "Eau de rose",
"fumet_de_poisson": "Fumet de poisson",
"levure_boulangere": "Levure boulangère",
"levure_chimique": "Levure chimique",
"maizena": "Maïzena",
"farine_de_lupin": "Farine de lupin",
"gelatine": "Gélatine",
"bicarbonate_de_soude": "Bicarbonate de soude",
"fecule_de_pomme_de_terre": "Fécule de pomme de terre",
"sucre": "Sucre",
"miel": "Miel",
"sirop_d_erable": "Sirop d'érable",
"sucre_roux": "Sucre roux",
"sucre_glace": "Sucre glace",
"cassonade": "Cassonade",
"chocolat_noir": "Chocolat noir",
"chocolat_au_lait": "Chocolat au lait",
"chocolat_blanc": "Chocolat blanc",
"pepites_de_chocolat": "Pépites de chocolat",
"cacao_en_poudre": "Cacao en poudre",
"extrait_de_vanille": "Extrait de vanille",
"sucre_de_palme": "Sucre de palme",
"sirop_de_sucre_de_canne": "Sirop de sucre de canne"
"water": "Eau",
"sparkling_water": "Eau gazeuse",
"orange_blossom_water": "Eau de fleur d'oranger",
"rose_water": "Eau de rose",
"fish_fumet": "Fumet de poisson",
"bakers_yeast": "Levure boulangère",
"baking_powder": "Levure chimique",
"cornstarch": "Maïzena",
"lupin_flour": "Farine de lupin",
"gelatin": "Gélatine",
"baking_soda": "Bicarbonate de soude",
"potato_starch": "Fécule de pomme de terre",
"sugar": "Sucre",
"honey": "Miel",
"maple_syrup": "Sirop d'érable",
"brown_sugar": "Sucre roux",
"powdered_sugar": "Sucre glace",
"demerara_sugar": "Cassonade",
"dark_chocolate": "Chocolat noir",
"milk_chocolate": "Chocolat au lait",
"white_chocolate": "Chocolat blanc",
"chocolate_chips": "Pépites de chocolat",
"cocoa_powder": "Cacao en poudre",
"vanilla_extract": "Extrait de vanille",
"palm_sugar": "Sucre de palme",
"cane_syrup": "Sirop de sucre de canne"
}
}
}