feat(i18n): install react-i18next, create English locale files, and initialize i18n framework
- Install i18next, react-i18next, i18next-browser-languagedetector - Create 6 namespace JSON files (common, collection, threads, setups, onboarding, settings) - Initialize i18n with language detection (localStorage + navigator) - Wire i18n import in main.tsx before React rendering Phase 34, Plan 01
This commit is contained in:
15
bun.lock
15
bun.lock
@@ -17,11 +17,14 @@
|
|||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"framer-motion": "^12.38.0",
|
"framer-motion": "^12.38.0",
|
||||||
"hono": "^4.12.8",
|
"hono": "^4.12.8",
|
||||||
|
"i18next": "^26.0.4",
|
||||||
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
"postgres": "^3.4.9",
|
"postgres": "^3.4.9",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-easy-crop": "^5.5.7",
|
"react-easy-crop": "^5.5.7",
|
||||||
|
"react-i18next": "^17.0.2",
|
||||||
"recharts": "^3.8.0",
|
"recharts": "^3.8.0",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
@@ -166,6 +169,8 @@
|
|||||||
|
|
||||||
"@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="],
|
"@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="],
|
||||||
|
|
||||||
|
"@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||||
|
|
||||||
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
||||||
|
|
||||||
"@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
"@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
||||||
@@ -794,8 +799,14 @@
|
|||||||
|
|
||||||
"hono": ["hono@4.12.8", "", {}, "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A=="],
|
"hono": ["hono@4.12.8", "", {}, "sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A=="],
|
||||||
|
|
||||||
|
"html-parse-stringify": ["html-parse-stringify@3.0.1", "", { "dependencies": { "void-elements": "3.1.0" } }, "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg=="],
|
||||||
|
|
||||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
|
"i18next": ["i18next@26.0.4", "", { "dependencies": { "@babel/runtime": "^7.29.2" }, "peerDependencies": { "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-gXF7U9bfioXPLv7mw8Qt2nfO7vij5MyINvPgVv99pX3fL1Y01pw2mKBFrlYpRxRCl2wz3ISenj6VsMJT2isfuA=="],
|
||||||
|
|
||||||
|
"i18next-browser-languagedetector": ["i18next-browser-languagedetector@8.2.1", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw=="],
|
||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
@@ -964,6 +975,8 @@
|
|||||||
|
|
||||||
"react-easy-crop": ["react-easy-crop@5.5.7", "", { "dependencies": { "normalize-wheel": "^1.0.1", "tslib": "^2.0.1" }, "peerDependencies": { "react": ">=16.4.0", "react-dom": ">=16.4.0" } }, "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA=="],
|
"react-easy-crop": ["react-easy-crop@5.5.7", "", { "dependencies": { "normalize-wheel": "^1.0.1", "tslib": "^2.0.1" }, "peerDependencies": { "react": ">=16.4.0", "react-dom": ">=16.4.0" } }, "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA=="],
|
||||||
|
|
||||||
|
"react-i18next": ["react-i18next@17.0.2", "", { "dependencies": { "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.0.1", "react": ">= 16.8.0", "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-shBftH2vaTWK2Bsp7FiL+cevx3xFJlvFxmsDFQSrJc+6twHkP0tv/bGa01VVWzpreUVVwU+3Hev5iFqRg65RwA=="],
|
||||||
|
|
||||||
"react-is": ["react-is@19.2.4", "", {}, "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA=="],
|
"react-is": ["react-is@19.2.4", "", {}, "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA=="],
|
||||||
|
|
||||||
"react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
|
"react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
|
||||||
@@ -1102,6 +1115,8 @@
|
|||||||
|
|
||||||
"vite": ["vite@8.0.0", "", { "dependencies": { "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", "picomatch": "^4.0.3", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.9", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.0.0-alpha.31", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q=="],
|
"vite": ["vite@8.0.0", "", { "dependencies": { "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", "picomatch": "^4.0.3", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.9", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.0.0-alpha.31", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q=="],
|
||||||
|
|
||||||
|
"void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="],
|
||||||
|
|
||||||
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||||
|
|
||||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|||||||
@@ -48,11 +48,14 @@
|
|||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"framer-motion": "^12.38.0",
|
"framer-motion": "^12.38.0",
|
||||||
"hono": "^4.12.8",
|
"hono": "^4.12.8",
|
||||||
|
"i18next": "^26.0.4",
|
||||||
|
"i18next-browser-languagedetector": "^8.2.1",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
"postgres": "^3.4.9",
|
"postgres": "^3.4.9",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-easy-crop": "^5.5.7",
|
"react-easy-crop": "^5.5.7",
|
||||||
|
"react-i18next": "^17.0.2",
|
||||||
"recharts": "^3.8.0",
|
"recharts": "^3.8.0",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
|
|||||||
39
src/client/lib/i18n.ts
Normal file
39
src/client/lib/i18n.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import i18n from "i18next";
|
||||||
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
|
||||||
|
import enCollection from "../locales/en/collection.json";
|
||||||
|
import enCommon from "../locales/en/common.json";
|
||||||
|
import enOnboarding from "../locales/en/onboarding.json";
|
||||||
|
import enSettings from "../locales/en/settings.json";
|
||||||
|
import enSetups from "../locales/en/setups.json";
|
||||||
|
import enThreads from "../locales/en/threads.json";
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(LanguageDetector)
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
resources: {
|
||||||
|
en: {
|
||||||
|
common: enCommon,
|
||||||
|
collection: enCollection,
|
||||||
|
threads: enThreads,
|
||||||
|
setups: enSetups,
|
||||||
|
onboarding: enOnboarding,
|
||||||
|
settings: enSettings,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
supportedLngs: ["en", "de"],
|
||||||
|
fallbackLng: "en",
|
||||||
|
defaultNS: "common",
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false,
|
||||||
|
},
|
||||||
|
detection: {
|
||||||
|
order: ["localStorage", "navigator"],
|
||||||
|
lookupLocalStorage: "gearbox-language",
|
||||||
|
caches: ["localStorage"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
31
src/client/locales/en/collection.json
Normal file
31
src/client/locales/en/collection.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"title": "Collection",
|
||||||
|
"gear": "Gear",
|
||||||
|
"planning": "Planning",
|
||||||
|
"empty": {
|
||||||
|
"title": "Your collection is empty",
|
||||||
|
"description": "Start cataloging your gear by adding your first item. Track weight, price, and organize by category.",
|
||||||
|
"addFirst": "Add your first item"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"name": "Name",
|
||||||
|
"nameRequired": "Name *",
|
||||||
|
"namePlaceholder": "e.g. Osprey Talon 22",
|
||||||
|
"weight": "Weight (g)",
|
||||||
|
"weightPlaceholder": "e.g. 680",
|
||||||
|
"price": "Price ($)",
|
||||||
|
"pricePlaceholder": "e.g. 129.99",
|
||||||
|
"quantity": "Quantity",
|
||||||
|
"category": "Category",
|
||||||
|
"notes": "Notes",
|
||||||
|
"notesPlaceholder": "Any additional notes...",
|
||||||
|
"productLink": "Product Link",
|
||||||
|
"urlPlaceholder": "https://..."
|
||||||
|
},
|
||||||
|
"classification": {
|
||||||
|
"ultralight": "Ultralight",
|
||||||
|
"light": "Light",
|
||||||
|
"medium": "Medium",
|
||||||
|
"heavy": "Heavy"
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/client/locales/en/common.json
Normal file
80
src/client/locales/en/common.json
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"home": "Home",
|
||||||
|
"collection": "Collection",
|
||||||
|
"setups": "Setups",
|
||||||
|
"discover": "Discover",
|
||||||
|
"settings": "Settings",
|
||||||
|
"search": "Search",
|
||||||
|
"searchPlaceholder": "Search catalog...",
|
||||||
|
"profile": "Profile"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"save": "Save",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"delete": "Delete",
|
||||||
|
"edit": "Edit",
|
||||||
|
"create": "Create",
|
||||||
|
"close": "Close",
|
||||||
|
"back": "Back",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"continue": "Continue",
|
||||||
|
"tryAgain": "Try again",
|
||||||
|
"dismiss": "Dismiss",
|
||||||
|
"saving": "Saving...",
|
||||||
|
"deleting": "Deleting...",
|
||||||
|
"creating": "Creating...",
|
||||||
|
"loading": "Loading...",
|
||||||
|
"addItem": "Add Item",
|
||||||
|
"saveChanges": "Save Changes",
|
||||||
|
"revoke": "Revoke",
|
||||||
|
"skipStep": "Skip this step"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"somethingWentWrong": "Something went wrong",
|
||||||
|
"unexpectedError": "An unexpected error occurred",
|
||||||
|
"nameRequired": "Name is required",
|
||||||
|
"positiveNumber": "Must be a positive number",
|
||||||
|
"validUrl": "Must be a valid URL (https://...)"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"signIn": "Sign in",
|
||||||
|
"signOut": "Sign out",
|
||||||
|
"joinGearBox": "Join GearBox",
|
||||||
|
"signInToGearBox": "Sign in to GearBox",
|
||||||
|
"signInDescription": "To manage your own collection, sign in or sign up.",
|
||||||
|
"createAccount": "Create account",
|
||||||
|
"redirectDescription": "You will be redirected to sign in with your account."
|
||||||
|
},
|
||||||
|
"confirm": {
|
||||||
|
"deleteItem": "Delete Item",
|
||||||
|
"deleteItemMessage": "Are you sure you want to delete <bold>{{name}}</bold>? This action cannot be undone.",
|
||||||
|
"deleteCandidate": "Delete Candidate",
|
||||||
|
"deleteCandidateMessage": "Are you sure you want to delete <bold>{{name}}</bold>? This action cannot be undone.",
|
||||||
|
"pickWinner": "Pick Winner",
|
||||||
|
"pickWinnerMessage": "Pick <bold>{{name}}</bold> as the winner? This will add it to your collection and archive the thread."
|
||||||
|
},
|
||||||
|
"externalLink": {
|
||||||
|
"title": "You are about to leave GearBox",
|
||||||
|
"redirectMessage": "You will be redirected to:"
|
||||||
|
},
|
||||||
|
"fab": {
|
||||||
|
"addToCollection": "Add to Collection",
|
||||||
|
"startNewThread": "Start New Thread",
|
||||||
|
"newSetup": "New Setup"
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
"noResults": "No results found",
|
||||||
|
"noItems": "No items match your search"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"items": "Items",
|
||||||
|
"totalWeight": "Total Weight",
|
||||||
|
"totalSpent": "Total Spent"
|
||||||
|
},
|
||||||
|
"filter": {
|
||||||
|
"showing": "Showing {{filtered}} of {{total}} items",
|
||||||
|
"searchItems": "Search items...",
|
||||||
|
"allCategories": "All Categories"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/client/locales/en/onboarding.json
Normal file
34
src/client/locales/en/onboarding.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"welcome": {
|
||||||
|
"title": "Welcome to GearBox",
|
||||||
|
"subtitle": "Tell us what you're into, and we'll help you set up your collection with gear that people actually use.",
|
||||||
|
"cta": "Let's go"
|
||||||
|
},
|
||||||
|
"hobby": {
|
||||||
|
"title": "What are you into?",
|
||||||
|
"subtitle": "Pick one or more — we'll show you popular gear for each.",
|
||||||
|
"continue": "Continue"
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"title": "Popular gear for {{hobby}}",
|
||||||
|
"titleMultiple": "Popular gear for your hobbies",
|
||||||
|
"subtitle": "Tap items you already own. We'll add them to your collection.",
|
||||||
|
"noCatalog": "No gear cataloged yet",
|
||||||
|
"noCatalogDescription": "We're still building our catalog for this hobby. You can skip this step and add gear manually later.",
|
||||||
|
"reviewCount": "Review {{count}} items",
|
||||||
|
"reviewCount_one": "Review {{count}} item"
|
||||||
|
},
|
||||||
|
"review": {
|
||||||
|
"title": "Your starting collection",
|
||||||
|
"itemsReady": "{{count}} items ready to add",
|
||||||
|
"itemsReady_one": "{{count}} item ready to add",
|
||||||
|
"noItemsSelected": "No items selected — you can always add gear later from the catalog.",
|
||||||
|
"addToCollection": "Add to my collection",
|
||||||
|
"adding": "Adding..."
|
||||||
|
},
|
||||||
|
"done": {
|
||||||
|
"title": "You're all set!",
|
||||||
|
"subtitle": "Your collection is ready. Browse the catalog anytime to discover more gear.",
|
||||||
|
"cta": "Start exploring"
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/client/locales/en/settings.json
Normal file
32
src/client/locales/en/settings.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"title": "Settings",
|
||||||
|
"language": {
|
||||||
|
"title": "Language",
|
||||||
|
"description": "Change the display language of the app"
|
||||||
|
},
|
||||||
|
"weightUnit": {
|
||||||
|
"title": "Weight Unit",
|
||||||
|
"description": "Choose the unit used to display weights across the app"
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"title": "Currency",
|
||||||
|
"description": "Changes the currency symbol displayed. This does not convert values."
|
||||||
|
},
|
||||||
|
"apiKeys": {
|
||||||
|
"title": "API Keys",
|
||||||
|
"description": "API keys allow programmatic access to GearBox (e.g., from Claude Desktop or scripts).",
|
||||||
|
"copyWarning": "Copy this key now — it won't be shown again:",
|
||||||
|
"namePlaceholder": "Key name (e.g., claude-desktop)"
|
||||||
|
},
|
||||||
|
"importExport": {
|
||||||
|
"title": "Import / Export",
|
||||||
|
"description": "Export your gear collection as a CSV file, or import items from a CSV.",
|
||||||
|
"export": "Export CSV",
|
||||||
|
"import": "Import CSV",
|
||||||
|
"importing": "Importing...",
|
||||||
|
"imported": "{{count}} items imported.",
|
||||||
|
"imported_one": "{{count}} item imported.",
|
||||||
|
"newCategories": "New categories: {{categories}}",
|
||||||
|
"noItemsFound": "No items found in the CSV."
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/client/locales/en/setups.json
Normal file
43
src/client/locales/en/setups.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"title": "Setups",
|
||||||
|
"create": "New Setup",
|
||||||
|
"empty": {
|
||||||
|
"title": "No setups yet",
|
||||||
|
"description": "Create a setup to organize gear for specific trips or activities."
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"items": "{{count}} items",
|
||||||
|
"items_one": "{{count}} item",
|
||||||
|
"weight": "Weight",
|
||||||
|
"price": "Price"
|
||||||
|
},
|
||||||
|
"share": {
|
||||||
|
"title": "Share Setup",
|
||||||
|
"shareLinks": "Share Links",
|
||||||
|
"createLink": "Create Link",
|
||||||
|
"noLinks": "No share links yet",
|
||||||
|
"copyLink": "Copy link",
|
||||||
|
"revokeLink": "Revoke link",
|
||||||
|
"copied": "Copied!",
|
||||||
|
"noExpiration": "No expiration",
|
||||||
|
"expired": "Expired",
|
||||||
|
"expiresToday": "Expires today",
|
||||||
|
"expiresTomorrow": "Expires tomorrow",
|
||||||
|
"expiresInDays": "Expires in {{days}} days",
|
||||||
|
"daysOption": "{{days}} days",
|
||||||
|
"deactivateWarning": "Switching to private will deactivate all share links. They can be reactivated by switching back."
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"private": "Private",
|
||||||
|
"privateDescription": "Only you can access",
|
||||||
|
"link": "Link sharing",
|
||||||
|
"linkDescription": "Anyone with the link",
|
||||||
|
"public": "Public",
|
||||||
|
"publicDescription": "Visible on your profile"
|
||||||
|
},
|
||||||
|
"impact": {
|
||||||
|
"title": "Impact Preview",
|
||||||
|
"adding": "Adding",
|
||||||
|
"removing": "Removing"
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/client/locales/en/threads.json
Normal file
45
src/client/locales/en/threads.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"title": "Research Threads",
|
||||||
|
"create": {
|
||||||
|
"title": "New Thread",
|
||||||
|
"threadName": "Thread name",
|
||||||
|
"namePlaceholder": "e.g. Lightweight sleeping bag",
|
||||||
|
"category": "Category",
|
||||||
|
"nameRequired": "Thread name is required",
|
||||||
|
"selectCategory": "Please select a category",
|
||||||
|
"createFailed": "Failed to create thread",
|
||||||
|
"createThread": "Create Thread"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"active": "Active",
|
||||||
|
"researching": "Researching",
|
||||||
|
"ordered": "Ordered",
|
||||||
|
"arrived": "Arrived",
|
||||||
|
"resolved": "Resolved",
|
||||||
|
"archived": "Archived"
|
||||||
|
},
|
||||||
|
"candidate": {
|
||||||
|
"name": "Name",
|
||||||
|
"price": "Price",
|
||||||
|
"weight": "Weight",
|
||||||
|
"url": "URL",
|
||||||
|
"pros": "Pros",
|
||||||
|
"cons": "Cons",
|
||||||
|
"notes": "Notes",
|
||||||
|
"addCandidate": "Add Candidate"
|
||||||
|
},
|
||||||
|
"comparison": {
|
||||||
|
"weight": "Weight",
|
||||||
|
"price": "Price",
|
||||||
|
"pros": "Pros",
|
||||||
|
"cons": "Cons"
|
||||||
|
},
|
||||||
|
"resolve": {
|
||||||
|
"title": "Pick Winner",
|
||||||
|
"message": "Pick <bold>{{name}}</bold> as the winner? This will add it to your collection and archive the thread."
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
"noThreads": "No research threads yet",
|
||||||
|
"noCandidates": "No candidates yet"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import "./lib/i18n";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
|||||||
Reference in New Issue
Block a user