- Created type-safe i18n system with TranslationKeys interface - Added German (de) and English (en) translations with 160+ keys - Implemented I18nContext provider and useI18n hook - Added LanguageSwitcher component for language selection - Refactored all major components to use translations: * Home page, StatsDashboard, DramOfTheDay * BottleGrid, EditBottleForm, CameraCapture * BuddyList, SessionList, TastingNoteForm * StatusSwitcher and bottle management features - Implemented locale-aware currency formatting (EUR) - Implemented locale-aware date formatting - Added localStorage persistence for language preference - Added automatic browser language detection - Organized translations into 8 main categories - System is extensible for additional languages
163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
import { TranslationKeys } from './types';
|
||
|
||
export const en: TranslationKeys = {
|
||
common: {
|
||
save: 'Save',
|
||
cancel: 'Cancel',
|
||
edit: 'Edit',
|
||
delete: 'Delete',
|
||
loading: 'Loading...',
|
||
error: 'Error',
|
||
success: 'Success',
|
||
search: 'Search',
|
||
back: 'Back',
|
||
confirm: 'Confirm',
|
||
check: 'Check',
|
||
link: 'Link',
|
||
none: 'None',
|
||
},
|
||
home: {
|
||
title: 'Whisky Vault',
|
||
logout: 'Logout',
|
||
stats: {
|
||
title: 'Your Bar Statistics',
|
||
totalValue: 'Total Value',
|
||
activeBottles: 'In the Bar',
|
||
avgRating: 'Avg Rating',
|
||
topDistillery: 'Top Distillery',
|
||
},
|
||
dramOfDay: {
|
||
button: 'Dram of the Day',
|
||
rollAgain: 'Not today, roll again',
|
||
suggestion: 'How about a...',
|
||
noOpenBottles: 'No open bottles found! Maybe time for a new tasting? 🥃',
|
||
title: 'Your Dram for today',
|
||
viewBottle: 'View Bottle',
|
||
},
|
||
searchPlaceholder: 'Search bottles or notes...',
|
||
noBottles: 'No bottles found. Time to go shopping! 🥃',
|
||
collection: 'Your Collection',
|
||
reTry: 'Retry',
|
||
all: 'All',
|
||
},
|
||
grid: {
|
||
searchPlaceholder: 'Search by name or distillery...',
|
||
noResults: 'No bottles found matching your filters. 🔎',
|
||
sortBy: {
|
||
createdAt: 'Newest first',
|
||
lastTasted: 'Last tasted',
|
||
name: 'Alphabetical',
|
||
},
|
||
filter: {
|
||
category: 'Category',
|
||
distillery: 'Distillery',
|
||
status: 'Status',
|
||
},
|
||
addSession: 'ADD TO SESSION',
|
||
addedOn: 'Added on',
|
||
reviewRequired: 'REVIEW',
|
||
unknownBottle: 'Unknown Bottle',
|
||
},
|
||
bottle: {
|
||
details: 'Details',
|
||
distillery: 'Distillery',
|
||
category: 'Category',
|
||
abv: 'ABV',
|
||
age: 'Age',
|
||
years: 'years',
|
||
lastTasted: 'Last Tasted',
|
||
neverTasted: 'Never',
|
||
purchasePrice: 'Purchase Price',
|
||
distilled: 'Distilled',
|
||
bottled: 'Bottled',
|
||
batch: 'Batch / Code',
|
||
status: {
|
||
sealed: 'Sealed',
|
||
open: 'Open',
|
||
sampled: 'Sample',
|
||
empty: 'Empty',
|
||
},
|
||
whiskybaseId: 'Whiskybase ID',
|
||
tastingNotes: 'Tasting Notes',
|
||
tastingNotesDesc: 'Your previous impressions and notes.',
|
||
noNotes: 'No notes yet.',
|
||
editDetails: 'Edit Details',
|
||
editTitle: 'Fix Details',
|
||
autoSearch: 'Auto Search',
|
||
applyId: 'Apply ID',
|
||
saveChanges: 'Save Changes',
|
||
noMatchFound: 'No match found.',
|
||
priceLabel: 'Purchase Price',
|
||
nameLabel: 'Name',
|
||
distilleryLabel: 'Distillery',
|
||
categoryLabel: 'Category',
|
||
abvLabel: 'ABV%',
|
||
ageLabel: 'Age',
|
||
distilledLabel: 'Distilled',
|
||
bottledLabel: 'Bottled',
|
||
batchLabel: 'Batch / Code',
|
||
bottleStatus: 'Bottle Status',
|
||
},
|
||
camera: {
|
||
scanBottle: 'Scan Bottle',
|
||
uploadImage: 'Upload Image',
|
||
analyzing: 'Analyzing bottle...',
|
||
analysisError: 'Analysis failed',
|
||
matchFound: 'Bottle identified!',
|
||
notAWhisky: "Doesn't look like whisky.",
|
||
lowConfidence: 'Unsure about details. Please check.',
|
||
saveToVault: 'Save to Vault',
|
||
tastingNow: 'Tasting Now',
|
||
backToList: 'Back to List',
|
||
whiskybaseSearch: 'Search Whiskybase',
|
||
searchingWb: 'Searching Whiskybase...',
|
||
wbMatchFound: 'Match found',
|
||
magicShot: 'Magic Shot',
|
||
saveSuccess: 'Successfully saved!',
|
||
later: 'Later (Back to List)',
|
||
openingCamera: 'Open Camera',
|
||
saving: 'Saving...',
|
||
nextBottle: 'Next Bottle',
|
||
newPhoto: 'Take New Photo',
|
||
inVault: 'Save in Vault',
|
||
offlineNotice: "Offline! Photo captured – it'll be analyzed automatically once you're back online. 📡",
|
||
alreadyInVault: 'Already in Vault!',
|
||
alreadyInVaultDesc: 'You already have this whisky in your collection. Want to go directly to the bottle?',
|
||
saveAnyway: 'Save as new bottle anyway',
|
||
analysisSuccess: 'Image analyzed successfully',
|
||
results: 'Results',
|
||
toVault: 'Go to bottle in Vault',
|
||
authRequired: 'Please sign in to save bottles.',
|
||
processingError: 'Processing failed. Please try again.',
|
||
},
|
||
tasting: {
|
||
addNote: 'Add Tasting Note',
|
||
isSample: "I'm drinking a sample",
|
||
isBottle: "I'm drinking from the bottle",
|
||
rating: 'Rating',
|
||
nose: 'Nose',
|
||
palate: 'Palate',
|
||
finish: 'Finish',
|
||
notesPlaceholder: 'What do you smell and taste?',
|
||
overall: 'Overall Impression',
|
||
saveTasting: 'Save Tasting',
|
||
participants: 'Participants',
|
||
addParticipant: 'Add Buddy',
|
||
},
|
||
buddy: {
|
||
title: 'Your Buddies',
|
||
addBuddy: 'Add Buddy',
|
||
placeholder: 'Buddy name...',
|
||
noBuddies: 'No buddies added yet.',
|
||
},
|
||
session: {
|
||
title: 'Tasting Sessions',
|
||
activeSession: 'Active Session',
|
||
allSessions: 'All Sessions',
|
||
newSession: 'Start New Session',
|
||
sessionName: 'Session Name',
|
||
noSessions: 'No sessions yet.',
|
||
expiryWarning: 'This session will expire soon.',
|
||
},
|
||
};
|