feat(browser): expand cuisine taxonomy to 13 categories + 105 subcategories
Some checks failed
CI / Frontend (Vue) (push) Has been cancelled
CI / Backend (Python) (push) Has been cancelled
Mirror / mirror (push) Has been cancelled

- 5 new top-level categories: BBQ & Smoke, Central American, African,
  Pacific & Oceania, Central Asian & Caucasus
- British/Irish split into British + Irish + Scottish with regional keywords
- Scandinavian: dish-level keyword expansion to fix zero-count gap
- Mediterranean: Israeli → Jewish (Ashkenazi/Sephardic/NY deli/z'houg/hawaiij);
  Palestinian, Yemeni, Egyptian, Syrian added; Moroccan moved to African
- Mexican: +Baja/Cal-Mex, +Mexico City
- Asian: +Hong Kong, +Cambodian, +Laotian, +Mongolian (16 subcategories)
- Indian: +Bangladeshi, +Pakistani, +Sri Lankan, +Nepali (8 subcategories)
- Latin American: full Caribbean depth (Jamaican, Puerto Rican, Dominican,
  Haitian, Trinidad); +Argentinian, +Venezuelan, +Chilean
- American: +Pacific Northwest, +Hawaiian; BBQ promoted to own category
- BBQ & Smoke: 8 regional styles (Texas, Carolina, KC, Memphis, Alabama,
  Kentucky, St. Louis, Backyard)
- feat(shopping): locale_config.py — Amazon/Instacart/Walmart locale routing
  for multi-currency affiliate link support (#114)
- chore: gitleaks allowlist for Amazon grocery dept IDs in locale_config.py
This commit is contained in:
pyr0ball 2026-04-21 10:15:58 -07:00
parent 0bef082ff0
commit 69e2ca7914
3 changed files with 490 additions and 66 deletions

View file

@ -3,6 +3,16 @@
[extend] [extend]
path = "/Library/Development/CircuitForge/circuitforge-hooks/gitleaks.toml" path = "/Library/Development/CircuitForge/circuitforge-hooks/gitleaks.toml"
# ── Global allowlist ──────────────────────────────────────────────────────────
# Amazon grocery department IDs (rh=n:<10-digit>) false-positive as phone
# numbers. locale_config.py is a static lookup table with no secrets.
[allowlist]
# Amazon grocery dept IDs (rh=n:<digits>) false-positive as phone numbers.
regexes = [
'''rh=n:\d{8,12}''',
]
# ── Test fixture allowlists ─────────────────────────────────────────────────── # ── Test fixture allowlists ───────────────────────────────────────────────────
[[rules]] [[rules]]

View file

@ -43,88 +43,204 @@ DOMAINS: dict[str, dict] = {
}, },
}, },
"Mexican": { "Mexican": {
"keywords": ["mexican", "tex-mex", "taco", "enchilada", "burrito", "keywords": ["mexican", "taco", "enchilada", "burrito", "salsa",
"salsa", "guacamole"], "guacamole", "mole", "tamale"],
"subcategories": { "subcategories": {
"Oaxacan": ["oaxacan", "oaxaca", "mole negro", "tlayuda", "Oaxacan": ["oaxacan", "oaxaca", "mole negro", "tlayuda",
"chapulines", "mezcal"], "chapulines", "mezcal", "tasajo", "memelas"],
"Yucatecan": ["yucatecan", "yucatan", "cochinita pibil", "poc chuc", "Yucatecan": ["yucatecan", "yucatan", "cochinita pibil", "poc chuc",
"sopa de lima", "panuchos"], "sopa de lima", "panuchos", "papadzules"],
"Veracruz": ["veracruz", "huachinango", "picadas", "enfrijoladas"], "Veracruz": ["veracruz", "veracruzana", "huachinango",
"Street Food": ["taco", "elote", "tlacoyos", "torta", "picadas", "enfrijoladas", "caldo de mariscos"],
"tamale", "quesadilla"], "Street Food": ["taco", "elote", "tlacoyos", "torta", "tamale",
"Mole": ["mole", "mole negro", "mole rojo", "mole verde", "quesadilla", "tostada", "sope", "gordita"],
"mole poblano"], "Mole": ["mole", "mole negro", "mole rojo", "mole verde",
"mole poblano", "mole amarillo", "pipián"],
"Baja / Cal-Mex": ["baja", "baja california", "cal-mex", "baja fish taco",
"fish taco", "carne asada fries", "california burrito",
"birria", "birria tacos", "quesabirria",
"lobster puerto nuevo", "tijuana", "ensenada",
"agua fresca", "caesar salad tijuana"],
"Mexico City": ["mexico city", "chilaquiles", "tlayuda cdmx",
"tacos de canasta", "torta ahogada", "pozole",
"chiles en nogada"],
}, },
}, },
"Asian": { "Asian": {
"keywords": ["asian", "chinese", "japanese", "thai", "korean", "vietnamese", "keywords": ["asian", "chinese", "japanese", "thai", "korean", "vietnamese",
"stir fry", "stir-fry", "ramen", "sushi"], "stir fry", "stir-fry", "ramen", "sushi", "malaysian",
"taiwanese", "singaporean", "burmese", "cambodian",
"laotian", "mongolian", "hong kong"],
"subcategories": { "subcategories": {
"Korean": ["korean", "kimchi", "bibimbap", "bulgogi", "japchae", "Korean": ["korean", "kimchi", "bibimbap", "bulgogi", "japchae",
"doenjang", "gochujang"], "doenjang", "gochujang", "tteokbokki", "sundubu",
"Japanese": ["japanese", "sushi", "ramen", "tempura", "miso", "galbi", "jjigae", "kbbq", "korean fried chicken"],
"teriyaki", "udon", "soba", "bento", "yakitori"], "Japanese": ["japanese", "sushi", "ramen", "tempura", "miso",
"Chinese": ["chinese", "dim sum", "fried rice", "dumplings", "wonton", "teriyaki", "udon", "soba", "bento", "yakitori",
"spring roll", "szechuan", "sichuan", "cantonese", "tonkatsu", "onigiri", "okonomiyaki", "takoyaki",
"chow mein", "mapo", "lo mein"], "kaiseki", "izakaya"],
"Thai": ["thai", "pad thai", "green curry", "red curry", "Chinese": ["chinese", "dim sum", "fried rice", "dumplings", "wonton",
"coconut milk", "lemongrass", "satay", "tom yum"], "spring roll", "szechuan", "sichuan", "cantonese",
"Vietnamese": ["vietnamese", "pho", "banh mi", "spring rolls", "chow mein", "mapo tofu", "lo mein", "hot pot",
"vermicelli", "nuoc cham", "bun bo"], "peking duck", "char siu", "congee"],
"Filipino": ["filipino", "adobo", "sinigang", "pancit", "lumpia", "Thai": ["thai", "pad thai", "green curry", "red curry",
"kare-kare", "lechon"], "coconut milk", "lemongrass", "satay", "tom yum",
"Indonesian": ["indonesian", "rendang", "nasi goreng", "gado-gado", "larb", "khao man gai", "massaman", "pad see ew"],
"tempeh", "sambal"], "Vietnamese": ["vietnamese", "pho", "banh mi", "spring rolls",
"vermicelli", "nuoc cham", "bun bo hue",
"banh xeo", "com tam", "bun cha"],
"Filipino": ["filipino", "adobo", "sinigang", "pancit", "lumpia",
"kare-kare", "lechon", "sisig", "halo-halo",
"dinuguan", "tinola", "bistek"],
"Indonesian": ["indonesian", "rendang", "nasi goreng", "gado-gado",
"tempeh", "sambal", "soto", "opor ayam",
"bakso", "mie goreng", "nasi uduk"],
"Malaysian": ["malaysian", "laksa", "nasi lemak", "char kway teow",
"satay malaysia", "roti canai", "bak kut teh",
"cendol", "mee goreng mamak", "curry laksa"],
"Taiwanese": ["taiwanese", "beef noodle soup", "lu rou fan",
"oyster vermicelli", "scallion pancake taiwan",
"pork chop rice", "three cup chicken",
"bubble tea", "stinky tofu", "ba wan"],
"Singaporean": ["singaporean", "chicken rice", "chili crab",
"singaporean laksa", "bak chor mee", "rojak",
"kaya toast", "nasi padang", "satay singapore"],
"Burmese": ["burmese", "myanmar", "mohinga", "laphet thoke",
"tea leaf salad", "ohn no khao swe",
"mont di", "nangyi thoke"],
"Hong Kong": ["hong kong", "hk style", "pineapple bun",
"wonton noodle soup", "hk milk tea", "egg tart",
"typhoon shelter crab", "char siu bao", "jook",
"congee hk", "silk stocking tea", "dan tat",
"siu mai hk", "cheung fun"],
"Cambodian": ["cambodian", "khmer", "amok", "lok lak",
"kuy teav", "bai sach chrouk", "nom banh chok",
"samlor korko", "beef loc lac"],
"Laotian": ["laotian", "lao", "larb", "tam mak hoong",
"or lam", "khao niaw", "ping kai",
"naem khao", "khao piak sen", "mok pa"],
"Mongolian": ["mongolian", "buuz", "khuushuur", "tsuivan",
"boodog", "airag", "khorkhog", "bansh",
"guriltai shol", "suutei tsai"],
"South Asian Fusion": ["south asian fusion", "indo-chinese",
"hakka chinese", "chilli chicken",
"manchurian", "schezwan"],
}, },
}, },
"Indian": { "Indian": {
"keywords": ["indian", "curry", "lentil", "dal", "tikka", "masala", "keywords": ["indian", "curry", "lentil", "dal", "tikka", "masala",
"biryani", "naan", "chutney"], "biryani", "naan", "chutney", "pakistani", "sri lankan",
"bangladeshi", "nepali"],
"subcategories": { "subcategories": {
"North Indian": ["north indian", "punjabi", "mughal", "tikka masala", "North Indian": ["north indian", "punjabi", "mughal", "tikka masala",
"naan", "tandoori", "butter chicken", "palak"], "naan", "tandoori", "butter chicken", "palak paneer",
"South Indian": ["south indian", "tamil", "kerala", "dosa", "idli", "chole", "rajma", "aloo gobi"],
"sambar", "rasam", "coconut chutney"], "South Indian": ["south indian", "tamil", "kerala", "dosa", "idli",
"Bengali": ["bengali", "mustard fish", "hilsa", "shorshe"], "sambar", "rasam", "coconut chutney", "appam",
"Gujarati": ["gujarati", "dhokla", "thepla", "undhiyu"], "fish curry kerala", "puttu", "payasam"],
"Bengali": ["bengali", "mustard fish", "hilsa", "shorshe ilish",
"mishti doi", "rasgulla", "kosha mangsho"],
"Gujarati": ["gujarati", "dhokla", "thepla", "undhiyu",
"khandvi", "fafda", "gujarati dal"],
"Pakistani": ["pakistani", "nihari", "haleem", "seekh kebab",
"karahi", "biryani karachi", "chapli kebab",
"halwa puri", "paya"],
"Sri Lankan": ["sri lankan", "kottu roti", "hoppers", "pol sambol",
"sri lankan curry", "lamprais", "string hoppers",
"wambatu moju"],
"Bangladeshi": ["bangladeshi", "bangladesh", "dhaka biryani",
"shutki", "pitha", "hilsa curry", "kacchi biryani",
"bhuna khichuri", "doi maach", "rezala"],
"Nepali": ["nepali", "dal bhat", "momos", "sekuwa",
"sel roti", "gundruk", "thukpa"],
}, },
}, },
"Mediterranean": { "Mediterranean": {
"keywords": ["mediterranean", "greek", "middle eastern", "turkish", "keywords": ["mediterranean", "greek", "middle eastern", "turkish",
"moroccan", "lebanese"], "lebanese", "jewish", "palestinian", "yemeni", "egyptian",
"syrian", "iraqi", "jordanian"],
"subcategories": { "subcategories": {
"Greek": ["greek", "feta", "tzatziki", "moussaka", "spanakopita", "Greek": ["greek", "feta", "tzatziki", "moussaka", "spanakopita",
"souvlaki", "dolmades"], "souvlaki", "dolmades", "spanakopita", "tiropita",
"galaktoboureko"],
"Turkish": ["turkish", "kebab", "borek", "meze", "baklava", "Turkish": ["turkish", "kebab", "borek", "meze", "baklava",
"lahmacun"], "lahmacun", "menemen", "pide", "iskender",
"Moroccan": ["moroccan", "tagine", "couscous", "harissa", "kisir", "simit"],
"chermoula", "preserved lemon"], "Syrian": ["syrian", "fattet hummus", "kibbeh syria",
"muhammara", "maklouba syria", "sfeeha",
"halawet el jibn"],
"Lebanese": ["lebanese", "middle eastern", "hummus", "falafel", "Lebanese": ["lebanese", "middle eastern", "hummus", "falafel",
"tabbouleh", "kibbeh", "fattoush"], "tabbouleh", "kibbeh", "fattoush", "manakish",
"Israeli": ["israeli", "shakshuka", "sabich", "za'atar", "kafta", "sfiha"],
"tahini"], "Jewish": ["jewish", "israeli", "ashkenazi", "sephardic",
"shakshuka", "sabich", "za'atar", "tahini",
"zhug", "zhoug", "s'khug", "z'houg",
"hawaiij", "hawaij", "hawayej",
"matzo", "latke", "rugelach", "babka", "challah",
"cholent", "gefilte fish", "brisket", "kugel",
"new york jewish", "new york deli", "pastrami",
"knish", "lox", "bagel and lox", "jewish deli"],
"Palestinian": ["palestinian", "musakhan", "maqluba", "knafeh",
"maftoul", "freekeh", "sumac chicken"],
"Yemeni": ["yemeni", "saltah", "lahoh", "bint al-sahn",
"zhug", "zhoug", "hulba", "fahsa",
"hawaiij", "hawaij", "hawayej"],
"Egyptian": ["egyptian", "koshari", "molokhia", "mahshi",
"ful medames", "ta'ameya", "feteer meshaltet"],
}, },
}, },
"American": { "American": {
"keywords": ["american", "southern", "bbq", "barbecue", "comfort food", "keywords": ["american", "southern", "comfort food", "cajun", "creole",
"cajun", "creole"], "hawaiian", "tex-mex", "soul food"],
"subcategories": { "subcategories": {
"Southern": ["southern", "soul food", "fried chicken", "Southern": ["southern", "soul food", "fried chicken",
"collard greens", "cornbread", "biscuits and gravy"], "collard greens", "cornbread", "biscuits and gravy",
"mac and cheese", "sweet potato pie", "okra"],
"Cajun/Creole": ["cajun", "creole", "new orleans", "gumbo", "Cajun/Creole": ["cajun", "creole", "new orleans", "gumbo",
"jambalaya", "etouffee", "dirty rice"], "jambalaya", "etouffee", "dirty rice", "po'boy",
"BBQ": ["bbq", "barbecue", "smoked", "brisket", "pulled pork", "muffuletta", "red beans and rice"],
"ribs", "pit"],
"Tex-Mex": ["tex-mex", "southwestern", "chili", "fajita", "Tex-Mex": ["tex-mex", "southwestern", "chili", "fajita",
"queso"], "queso", "breakfast taco", "chile con carne"],
"New England": ["new england", "chowder", "lobster", "clam", "New England": ["new england", "chowder", "lobster", "clam",
"maple", "yankee"], "maple", "yankee", "boston baked beans",
"johnnycake", "fish and chips"],
"Pacific Northwest": ["pacific northwest", "pnw", "dungeness crab",
"salmon", "cedar plank", "razor clam",
"geoduck", "chanterelle", "marionberry"],
"Hawaiian": ["hawaiian", "hawaii", "plate lunch", "loco moco",
"poke", "spam musubi", "kalua pig", "lau lau",
"haupia", "poi", "manapua", "garlic shrimp",
"saimin", "huli huli", "malasada"],
},
},
"BBQ & Smoke": {
"keywords": ["bbq", "barbecue", "smoked", "pit", "smoke ring",
"low and slow", "brisket", "pulled pork", "ribs"],
"subcategories": {
"Texas BBQ": ["texas bbq", "central texas bbq", "brisket",
"beef ribs", "post oak", "salt and pepper rub",
"east texas bbq", "lockhart", "franklin style"],
"Carolina BBQ": ["carolina bbq", "north carolina bbq", "whole hog",
"vinegar sauce", "lexington style", "eastern nc",
"south carolina bbq", "mustard sauce"],
"Kansas City BBQ": ["kansas city bbq", "kc bbq", "burnt ends",
"sweet bbq sauce", "tomato molasses sauce",
"baby back ribs kc"],
"Memphis BBQ": ["memphis bbq", "dry rub ribs", "wet ribs",
"memphis style", "dry rub pork"],
"Alabama BBQ": ["alabama bbq", "white sauce", "alabama white sauce",
"smoked chicken alabama"],
"Kentucky BBQ": ["kentucky bbq", "mutton bbq", "owensboro bbq",
"black dip", "western kentucky barbecue"],
"St. Louis BBQ": ["st louis bbq", "st. louis ribs", "st louis cut ribs",
"st louis style spare ribs"],
"Backyard Grill": ["backyard bbq", "cookout", "grilled burgers",
"charcoal grill", "kettle grill", "tailgate"],
}, },
}, },
"European": { "European": {
"keywords": ["french", "german", "spanish", "british", "irish", "keywords": ["french", "german", "spanish", "british", "irish", "scottish",
"scandinavian"], "welsh", "scandinavian", "nordic", "eastern european"],
"subcategories": { "subcategories": {
"French": ["french", "provencal", "beurre", "crepe", "French": ["french", "provencal", "beurre", "crepe",
"ratatouille", "cassoulet", "bouillabaisse"], "ratatouille", "cassoulet", "bouillabaisse"],
@ -132,26 +248,164 @@ DOMAINS: dict[str, dict] = {
"tortilla espanola", "chorizo"], "tortilla espanola", "chorizo"],
"German": ["german", "bratwurst", "sauerkraut", "schnitzel", "German": ["german", "bratwurst", "sauerkraut", "schnitzel",
"pretzel", "strudel"], "pretzel", "strudel"],
"British/Irish": ["british", "irish", "english", "pub food", "British": ["british", "english", "pub food", "cornish",
"shepherd's pie", "bangers", "scones"], "shepherd's pie", "bangers", "toad in the hole",
"coronation chicken", "london", "londoner",
"cornish pasty", "ploughman's"],
"Irish": ["irish", "ireland", "colcannon", "coddle",
"irish stew", "soda bread", "boxty", "champ"],
"Scottish": ["scottish", "scotland", "haggis", "cullen skink",
"cranachan", "scotch broth", "glaswegian",
"neeps and tatties", "tablet"],
"Scandinavian": ["scandinavian", "nordic", "swedish", "norwegian", "Scandinavian": ["scandinavian", "nordic", "swedish", "norwegian",
"danish", "gravlax", "meatballs"], "danish", "finnish", "gravlax", "swedish meatballs",
"lefse", "smörgåsbord", "fika", "crispbread",
"cardamom bun", "herring", "æbleskiver",
"lingonberry", "lutefisk", "janssons frestelse",
"knäckebröd", "kladdkaka"],
"Eastern European": ["eastern european", "polish", "russian", "ukrainian",
"czech", "hungarian", "pierogi", "borscht",
"goulash", "kielbasa", "varenyky", "pelmeni"],
}, },
}, },
"Latin American": { "Latin American": {
"keywords": ["latin american", "peruvian", "argentinian", "colombian", "keywords": ["latin american", "peruvian", "argentinian", "colombian",
"cuban", "caribbean", "brazilian"], "cuban", "caribbean", "brazilian", "venezuelan", "chilean"],
"subcategories": { "subcategories": {
"Peruvian": ["peruvian", "ceviche", "lomo saltado", "anticucho", "Peruvian": ["peruvian", "ceviche", "lomo saltado", "anticucho",
"aji amarillo"], "aji amarillo", "causa", "leche de tigre",
"Brazilian": ["brazilian", "churrasco", "feijoada", "pao de queijo", "arroz con leche peru", "pollo a la brasa"],
"brigadeiro"], "Brazilian": ["brazilian", "churrasco", "feijoada", "pao de queijo",
"Colombian": ["colombian", "bandeja paisa", "arepas", "empanadas", "brigadeiro", "coxinha", "moqueca", "vatapa",
"sancocho"], "caipirinha", "acai bowl"],
"Cuban": ["cuban", "ropa vieja", "moros y cristianos", "Colombian": ["colombian", "bandeja paisa", "arepas", "empanadas",
"picadillo", "mojito"], "sancocho", "ajiaco", "buñuelos", "changua"],
"Caribbean": ["caribbean", "jamaican", "jerk", "trinidadian", "Argentinian": ["argentinian", "asado", "chimichurri", "empanadas argentina",
"plantain", "roti"], "milanesa", "locro", "dulce de leche", "medialunas"],
"Venezuelan": ["venezuelan", "pabellón criollo", "arepas venezuela",
"hallacas", "cachapas", "tequeños", "caraotas"],
"Chilean": ["chilean", "cazuela", "pastel de choclo", "curanto",
"sopaipillas", "charquicán", "completo"],
"Cuban": ["cuban", "ropa vieja", "moros y cristianos",
"picadillo", "lechon cubano", "vaca frita",
"tostones", "platanos maduros"],
"Jamaican": ["jamaican", "jerk chicken", "jerk pork", "ackee saltfish",
"curry goat", "rice and peas", "escovitch",
"jamaican patty", "callaloo jamaica", "festival"],
"Puerto Rican": ["puerto rican", "mofongo", "pernil", "arroz con gandules",
"sofrito", "pasteles", "tostones pr", "tembleque",
"coquito", "asopao"],
"Dominican": ["dominican", "mangu", "sancocho dominicano",
"pollo guisado", "habichuelas guisadas",
"tostones dominicanos", "morir soñando"],
"Haitian": ["haitian", "griot", "pikliz", "riz et pois",
"joumou", "akra", "pain patate", "labouyi"],
"Trinidad": ["trinidadian", "doubles", "roti trinidad", "pelau",
"callaloo trinidad", "bake and shark",
"curry duck", "oil down"],
},
},
"Central American": {
"keywords": ["central american", "salvadoran", "guatemalan",
"honduran", "nicaraguan", "costa rican", "panamanian"],
"subcategories": {
"Salvadoran": ["salvadoran", "el salvador", "pupusas", "curtido",
"sopa de pata", "nuégados", "atol shuco"],
"Guatemalan": ["guatemalan", "pepián", "jocon", "kak'ik",
"hilachas", "rellenitos", "fiambre"],
"Costa Rican": ["costa rican", "gallo pinto", "casado",
"olla de carne", "arroz con leche cr",
"tres leches cr"],
"Honduran": ["honduran", "baleadas", "sopa de caracol",
"tapado", "machuca", "catrachitas"],
"Nicaraguan": ["nicaraguan", "nacatamal", "vigorón", "indio viejo",
"gallo pinto nicaragua", "güirilas"],
},
},
"African": {
"keywords": ["african", "west african", "east african", "ethiopian",
"nigerian", "ghanaian", "kenyan", "south african",
"senegalese", "tunisian"],
"subcategories": {
"West African": ["west african", "nigerian", "ghanaian",
"jollof rice", "egusi soup", "fufu", "suya",
"groundnut stew", "kelewele", "kontomire",
"waakye", "ofam", "bitterleaf soup"],
"Senegalese": ["senegalese", "senegal", "thieboudienne",
"yassa", "mafe", "thiou", "ceebu jen",
"domoda"],
"Ethiopian & Eritrean": ["ethiopian", "eritrean", "injera", "doro wat",
"kitfo", "tibs", "shiro", "misir wat",
"gomen", "ful ethiopian", "tegamino"],
"East African": ["east african", "kenyan", "tanzanian", "ugandan",
"nyama choma", "ugali", "sukuma wiki",
"pilau kenya", "mandazi", "matoke",
"githeri", "irio"],
"North African": ["north african", "tunisian", "algerian", "libyan",
"brik", "lablabi", "merguez", "shakshuka tunisian",
"harissa tunisian", "couscous algerian"],
"South African": ["south african", "braai", "bobotie", "boerewors",
"bunny chow", "pap", "chakalaka", "biltong",
"malva pudding", "koeksister", "potjiekos"],
"Moroccan": ["moroccan", "tagine", "couscous morocco",
"harissa", "chermoula", "preserved lemon",
"pastilla", "mechoui", "bastilla"],
},
},
"Pacific & Oceania": {
"keywords": ["pacific", "oceania", "polynesian", "melanesian",
"micronesian", "maori", "fijian", "samoan", "tongan",
"hawaiian", "australian", "new zealand"],
"subcategories": {
"Māori / New Zealand": ["maori", "new zealand", "hangi", "rewena bread",
"boil-up", "paua", "kumara", "pavlova nz",
"whitebait fritter", "kina", "hokey pokey"],
"Australian": ["australian", "meat pie", "lamington",
"anzac biscuits", "damper", "barramundi",
"vegemite", "pavlova australia", "tim tam",
"sausage sizzle", "chiko roll", "fairy bread"],
"Fijian": ["fijian", "fiji", "kokoda", "lovo",
"rourou", "palusami fiji", "duruka",
"vakalolo"],
"Samoan": ["samoan", "samoa", "palusami", "oka",
"fa'ausi", "chop suey samoa", "sapasui",
"koko alaisa", "supo esi"],
"Tongan": ["tongan", "tonga", "lu pulu", "'ota 'ika",
"fekkai", "faikakai topai", "kapisi pulu"],
"Papua New Guinean": ["papua new guinea", "png", "mumu",
"sago", "aibika", "kaukau",
"taro png", "coconut crab"],
"Hawaiian": ["hawaiian", "hawaii", "poke", "loco moco",
"plate lunch", "kalua pig", "haupia",
"spam musubi", "poi", "malasada"],
},
},
"Central Asian & Caucasus": {
"keywords": ["central asian", "caucasus", "georgian", "armenian", "uzbek",
"afghan", "persian", "iranian", "azerbaijani", "kazakh"],
"subcategories": {
"Persian / Iranian": ["persian", "iranian", "ghormeh sabzi", "fesenjan",
"tahdig", "joojeh kabab", "ash reshteh",
"zereshk polo", "khoresh", "mast o khiar",
"kashk-e-bademjan", "mirza ghasemi",
"baghali polo"],
"Georgian": ["georgian", "georgia", "khachapuri", "khinkali",
"churchkhela", "ajapsandali", "satsivi",
"pkhali", "lobiani", "badrijani nigvzit"],
"Armenian": ["armenian", "dolma armenia", "lahmajoun",
"manti armenia", "ghapama", "basturma",
"harissa armenia", "nazook", "tolma"],
"Azerbaijani": ["azerbaijani", "azerbaijan", "plov azerbaijan",
"dolma azeri", "dushbara", "levengi",
"shah plov", "gutab"],
"Uzbek": ["uzbek", "uzbekistan", "plov", "samsa",
"lagman", "shashlik", "manti uzbek",
"non bread", "dimlama", "sumalak"],
"Afghan": ["afghan", "afghanistan", "kabuli pulao", "mantu",
"bolani", "qorma", "ashak", "shorwa",
"aushak", "borani banjan"],
"Kazakh": ["kazakh", "beshbarmak", "kuyrdak", "baursak",
"kurt", "shubat", "kazy"],
}, },
}, },
}, },

View file

@ -0,0 +1,160 @@
"""
Shopping locale configuration.
Maps a locale key to Amazon domain, currency metadata, and retailer availability.
Instacart and Walmart are US/CA-only; all other locales get Amazon only.
Amazon Fresh (&i=amazonfresh) is US-only international domains use the general
grocery department (&rh=n:16310101) where available, plain search elsewhere.
"""
from __future__ import annotations
from typing import TypedDict
class LocaleConfig(TypedDict):
amazon_domain: str
amazon_grocery_dept: str # URL fragment for grocery department on this locale's site
currency_code: str
currency_symbol: str
instacart: bool
walmart: bool
LOCALES: dict[str, LocaleConfig] = {
"us": {
"amazon_domain": "amazon.com",
"amazon_grocery_dept": "i=amazonfresh",
"currency_code": "USD",
"currency_symbol": "$",
"instacart": True,
"walmart": True,
},
"ca": {
"amazon_domain": "amazon.ca",
"amazon_grocery_dept": "rh=n:6967215011", # Grocery dept on .ca # gitleaks:allow
"currency_code": "CAD",
"currency_symbol": "CA$",
"instacart": True,
"walmart": False,
},
"gb": {
"amazon_domain": "amazon.co.uk",
"amazon_grocery_dept": "rh=n:340831031", # Grocery dept on .co.uk
"currency_code": "GBP",
"currency_symbol": "£",
"instacart": False,
"walmart": False,
},
"au": {
"amazon_domain": "amazon.com.au",
"amazon_grocery_dept": "rh=n:5765081051", # Pantry/grocery on .com.au # gitleaks:allow
"currency_code": "AUD",
"currency_symbol": "A$",
"instacart": False,
"walmart": False,
},
"nz": {
# NZ has no Amazon storefront — route to .com.au as nearest option
"amazon_domain": "amazon.com.au",
"amazon_grocery_dept": "rh=n:5765081051", # gitleaks:allow
"currency_code": "NZD",
"currency_symbol": "NZ$",
"instacart": False,
"walmart": False,
},
"de": {
"amazon_domain": "amazon.de",
"amazon_grocery_dept": "rh=n:340843031", # Lebensmittel & Getränke
"currency_code": "EUR",
"currency_symbol": "",
"instacart": False,
"walmart": False,
},
"fr": {
"amazon_domain": "amazon.fr",
"amazon_grocery_dept": "rh=n:197858031",
"currency_code": "EUR",
"currency_symbol": "",
"instacart": False,
"walmart": False,
},
"it": {
"amazon_domain": "amazon.it",
"amazon_grocery_dept": "rh=n:525616031",
"currency_code": "EUR",
"currency_symbol": "",
"instacart": False,
"walmart": False,
},
"es": {
"amazon_domain": "amazon.es",
"amazon_grocery_dept": "rh=n:599364031",
"currency_code": "EUR",
"currency_symbol": "",
"instacart": False,
"walmart": False,
},
"nl": {
"amazon_domain": "amazon.nl",
"amazon_grocery_dept": "rh=n:16584827031",
"currency_code": "EUR",
"currency_symbol": "",
"instacart": False,
"walmart": False,
},
"se": {
"amazon_domain": "amazon.se",
"amazon_grocery_dept": "rh=n:20741393031",
"currency_code": "SEK",
"currency_symbol": "kr",
"instacart": False,
"walmart": False,
},
"jp": {
"amazon_domain": "amazon.co.jp",
"amazon_grocery_dept": "rh=n:2246283051", # gitleaks:allow
"currency_code": "JPY",
"currency_symbol": "¥",
"instacart": False,
"walmart": False,
},
"in": {
"amazon_domain": "amazon.in",
"amazon_grocery_dept": "rh=n:2454178031", # gitleaks:allow
"currency_code": "INR",
"currency_symbol": "",
"instacart": False,
"walmart": False,
},
"mx": {
"amazon_domain": "amazon.com.mx",
"amazon_grocery_dept": "rh=n:10737659011",
"currency_code": "MXN",
"currency_symbol": "MX$",
"instacart": False,
"walmart": False,
},
"br": {
"amazon_domain": "amazon.com.br",
"amazon_grocery_dept": "rh=n:17878420011",
"currency_code": "BRL",
"currency_symbol": "R$",
"instacart": False,
"walmart": False,
},
"sg": {
"amazon_domain": "amazon.sg",
"amazon_grocery_dept": "rh=n:6981647051", # gitleaks:allow
"currency_code": "SGD",
"currency_symbol": "S$",
"instacart": False,
"walmart": False,
},
}
DEFAULT_LOCALE = "us"
def get_locale(key: str) -> LocaleConfig:
"""Return locale config for *key*, falling back to US if unknown."""
return LOCALES.get(key, LOCALES[DEFAULT_LOCALE])