fix: currency suggestion uses region detection, seed adds market prices
- Currency auto-suggestion now uses locale region subtag (en-US → US → USD, en-DE → DE → EUR) instead of language prefix. Fixes wrong suggestion for users with English browser locale in European countries. - Added dismiss button (X) to suggestion banner - Dev seed script now clears existing dev data before re-seeding (safe to run repeatedly without manual DB cleanup) - Added DEV_MARKET_PRICES with multi-market UVP data for 10 global items (EU/US/UK prices) and community prices for 5 owned items Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { useUpdateSetting } from "../hooks/useSettings";
|
||||
import { useWeightUnit } from "../hooks/useWeightUnit";
|
||||
import type { Currency, WeightUnit } from "../lib/formatters";
|
||||
import i18n from "../lib/i18n";
|
||||
import { LucideIcon } from "../lib/iconData";
|
||||
|
||||
const LANGUAGES = [
|
||||
{ value: "en", label: "English" },
|
||||
@@ -215,27 +216,52 @@ function ImportExportSection() {
|
||||
);
|
||||
}
|
||||
|
||||
const LOCALE_CURRENCY_MAP: Record<string, Currency> = {
|
||||
// Map region codes (from navigator.language) to currencies.
|
||||
// Region is more reliable than language for currency detection:
|
||||
// en-US → US → USD, en-GB → GB → GBP, en-DE → DE → EUR
|
||||
const REGION_CURRENCY_MAP: Record<string, Currency> = {
|
||||
US: "USD",
|
||||
GB: "GBP",
|
||||
AU: "AUD",
|
||||
CA: "CAD",
|
||||
JP: "JPY",
|
||||
// EU countries
|
||||
DE: "EUR",
|
||||
FR: "EUR",
|
||||
ES: "EUR",
|
||||
IT: "EUR",
|
||||
NL: "EUR",
|
||||
PT: "EUR",
|
||||
AT: "EUR",
|
||||
BE: "EUR",
|
||||
IE: "EUR",
|
||||
FI: "EUR",
|
||||
GR: "EUR",
|
||||
};
|
||||
|
||||
// Fallback: language prefix → currency (when no region subtag)
|
||||
const LANG_CURRENCY_MAP: Record<string, Currency> = {
|
||||
de: "EUR",
|
||||
fr: "EUR",
|
||||
es: "EUR",
|
||||
it: "EUR",
|
||||
nl: "EUR",
|
||||
pt: "EUR",
|
||||
en: "USD",
|
||||
ja: "JPY",
|
||||
};
|
||||
|
||||
function getSuggestedCurrency(): Currency | null {
|
||||
try {
|
||||
const lang = navigator.language;
|
||||
// Check full locale first (e.g., en-GB → GBP)
|
||||
if (lang.startsWith("en-GB")) return "GBP";
|
||||
if (lang.startsWith("en-AU")) return "AUD";
|
||||
if (lang.startsWith("en-CA") || lang.startsWith("fr-CA")) return "CAD";
|
||||
const locale = navigator.language; // e.g., "en-US", "de-DE", "en"
|
||||
const parts = locale.split("-");
|
||||
// Try region subtag first (more accurate for currency)
|
||||
if (parts.length > 1) {
|
||||
const region = parts[parts.length - 1].toUpperCase();
|
||||
const fromRegion = REGION_CURRENCY_MAP[region];
|
||||
if (fromRegion) return fromRegion;
|
||||
}
|
||||
// Fall back to language prefix
|
||||
const prefix = lang.split("-")[0];
|
||||
return LOCALE_CURRENCY_MAP[prefix] ?? null;
|
||||
const lang = parts[0];
|
||||
return LANG_CURRENCY_MAP[lang] ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -267,9 +293,9 @@ function SettingsPage() {
|
||||
</div>
|
||||
|
||||
{showSuggestion && (
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-xl px-4 py-3 mb-4 flex items-center justify-between">
|
||||
<span className="text-sm text-blue-700">
|
||||
Based on your location, we suggest{" "}
|
||||
<div className="bg-blue-50 border border-blue-100 rounded-xl px-4 py-3 mb-4 flex items-center gap-3">
|
||||
<span className="text-sm text-blue-700 flex-1">
|
||||
Based on your region, we suggest{" "}
|
||||
{CURRENCIES.find((c) => c.value === suggestedCurrency)?.label ??
|
||||
suggestedCurrency}{" "}
|
||||
({suggestedCurrency})
|
||||
@@ -283,11 +309,17 @@ function SettingsPage() {
|
||||
});
|
||||
setSuggestionDismissed(true);
|
||||
}}
|
||||
className="text-sm font-medium text-blue-700 hover:text-blue-800 underline ml-3"
|
||||
className="text-sm font-medium text-blue-700 hover:text-blue-800 underline shrink-0"
|
||||
>
|
||||
Use{" "}
|
||||
{CURRENCIES.find((c) => c.value === suggestedCurrency)?.label ??
|
||||
suggestedCurrency}
|
||||
Switch
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSuggestionDismissed(true)}
|
||||
className="p-1 text-blue-400 hover:text-blue-600 rounded shrink-0"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<LucideIcon name="x" size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user