diff --git a/fe/package-lock.json b/fe/package-lock.json
index a49cb17..23a1c65 100644
--- a/fe/package-lock.json
+++ b/fe/package-lock.json
@@ -23,7 +23,7 @@
"workbox-background-sync": "^7.3.0"
},
"devDependencies": {
- "@intlify/unplugin-vue-i18n": "^6.0.8",
+ "@intlify/unplugin-vue-i18n": "^4.0.0",
"@playwright/test": "^1.51.1",
"@storybook/addon-docs": "^9.0.2",
"@storybook/addon-onboarding": "^9.0.2",
@@ -2558,14 +2558,13 @@
}
},
"node_modules/@intlify/bundle-utils": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-10.0.1.tgz",
- "integrity": "sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-8.0.0.tgz",
+ "integrity": "sha512-1B++zykRnMwQ+20SpsZI1JCnV/YJt9Oq7AGlEurzkWJOFtFAVqaGc/oV36PBRYeiKnTbY9VYfjBimr2Vt42wLQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@intlify/message-compiler": "^11.1.2",
- "@intlify/shared": "^11.1.2",
+ "@intlify/message-compiler": "^9.4.0",
+ "@intlify/shared": "^9.4.0",
"acorn": "^8.8.2",
"escodegen": "^2.1.0",
"estree-walker": "^2.0.2",
@@ -2575,7 +2574,7 @@
"yaml-eslint-parser": "^1.2.2"
},
"engines": {
- "node": ">= 18"
+ "node": ">= 14.16"
},
"peerDependenciesMeta": {
"petite-vue-i18n": {
@@ -2587,13 +2586,12 @@
}
},
"node_modules/@intlify/bundle-utils/node_modules/@intlify/message-compiler": {
- "version": "11.1.3",
- "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.1.3.tgz",
- "integrity": "sha512-7rbqqpo2f5+tIcwZTAG/Ooy9C8NDVwfDkvSeDPWUPQW+Dyzfw2o9H103N5lKBxO7wxX9dgCDjQ8Umz73uYw3hw==",
+ "version": "9.14.4",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.4.tgz",
+ "integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@intlify/shared": "11.1.3",
+ "@intlify/shared": "9.14.4",
"source-map-js": "^1.0.2"
},
"engines": {
@@ -2604,11 +2602,10 @@
}
},
"node_modules/@intlify/bundle-utils/node_modules/@intlify/shared": {
- "version": "11.1.3",
- "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.3.tgz",
- "integrity": "sha512-pTFBgqa/99JRA2H1qfyqv97MKWJrYngXBA/I0elZcYxvJgcCw3mApAoPW3mJ7vx3j+Ti0FyKUFZ4hWxdjKaxvA==",
+ "version": "9.14.4",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.4.tgz",
+ "integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">= 16"
},
@@ -2661,19 +2658,15 @@
}
},
"node_modules/@intlify/unplugin-vue-i18n": {
- "version": "6.0.8",
- "resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-6.0.8.tgz",
- "integrity": "sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-4.0.0.tgz",
+ "integrity": "sha512-q2Mhqa/mLi0tulfLFO4fMXXvEbkSZpI5yGhNNsLTNJJ41icEGUuyDe+j5zRZIKSkOJRgX6YbCyibTDJdRsukmw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@intlify/bundle-utils": "^10.0.1",
- "@intlify/shared": "^11.1.2",
- "@intlify/vue-i18n-extensions": "^8.0.0",
+ "@intlify/bundle-utils": "^8.0.0",
+ "@intlify/shared": "^9.4.0",
"@rollup/pluginutils": "^5.1.0",
- "@typescript-eslint/scope-manager": "^8.13.0",
- "@typescript-eslint/typescript-estree": "^8.13.0",
+ "@vue/compiler-sfc": "^3.2.47",
"debug": "^4.3.3",
"fast-glob": "^3.2.12",
"js-yaml": "^4.1.0",
@@ -2681,16 +2674,15 @@
"pathe": "^1.0.0",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2",
- "unplugin": "^1.1.0",
- "vue": "^3.4"
+ "unplugin": "^1.1.0"
},
"engines": {
- "node": ">= 18"
+ "node": ">= 14.16"
},
"peerDependencies": {
"petite-vue-i18n": "*",
- "vue": "^3.2.25",
- "vue-i18n": "*"
+ "vue-i18n": "*",
+ "vue-i18n-bridge": "*"
},
"peerDependenciesMeta": {
"petite-vue-i18n": {
@@ -2698,15 +2690,17 @@
},
"vue-i18n": {
"optional": true
+ },
+ "vue-i18n-bridge": {
+ "optional": true
}
}
},
"node_modules/@intlify/unplugin-vue-i18n/node_modules/@intlify/shared": {
- "version": "11.1.3",
- "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.3.tgz",
- "integrity": "sha512-pTFBgqa/99JRA2H1qfyqv97MKWJrYngXBA/I0elZcYxvJgcCw3mApAoPW3mJ7vx3j+Ti0FyKUFZ4hWxdjKaxvA==",
+ "version": "9.14.4",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.4.tgz",
+ "integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">= 16"
},
@@ -2744,117 +2738,6 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
- "node_modules/@intlify/vue-i18n-extensions": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz",
- "integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.24.6",
- "@intlify/shared": "^10.0.0",
- "@vue/compiler-dom": "^3.2.45",
- "vue-i18n": "^10.0.0"
- },
- "engines": {
- "node": ">= 18"
- },
- "peerDependencies": {
- "@intlify/shared": "^9.0.0 || ^10.0.0 || ^11.0.0",
- "@vue/compiler-dom": "^3.0.0",
- "vue": "^3.0.0",
- "vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0"
- },
- "peerDependenciesMeta": {
- "@intlify/shared": {
- "optional": true
- },
- "@vue/compiler-dom": {
- "optional": true
- },
- "vue": {
- "optional": true
- },
- "vue-i18n": {
- "optional": true
- }
- }
- },
- "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/core-base": {
- "version": "10.0.7",
- "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.7.tgz",
- "integrity": "sha512-mE71aUH5baH0me8duB4FY5qevUJizypHsYw3eCvmOx07QvmKppgOONx3dYINxuA89Z2qkAGb/K6Nrpi7aAMwew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@intlify/message-compiler": "10.0.7",
- "@intlify/shared": "10.0.7"
- },
- "engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/kazupon"
- }
- },
- "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/message-compiler": {
- "version": "10.0.7",
- "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.7.tgz",
- "integrity": "sha512-nrC4cDL/UHZSUqd8sRbVz+DPukzZ8NnG5OK+EB/nlxsH35deyzyVkXP/QuR8mFZrISJ+4hCd6VtCQCcT+RO+5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@intlify/shared": "10.0.7",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/kazupon"
- }
- },
- "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": {
- "version": "10.0.7",
- "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.7.tgz",
- "integrity": "sha512-oeoq0L5+5P4ShXa6jBQcx+BT+USe3MjX0xJexZO1y7rfDJdwZ9+QP3jO4tcS1nxhBYYdjvFTqe4bmnLijV0GxQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/kazupon"
- }
- },
- "node_modules/@intlify/vue-i18n-extensions/node_modules/@vue/devtools-api": {
- "version": "6.6.4",
- "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
- "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@intlify/vue-i18n-extensions/node_modules/vue-i18n": {
- "version": "10.0.7",
- "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.7.tgz",
- "integrity": "sha512-bKsk0PYwP9gdYF4nqSAT0kDpnLu1gZzlxFl885VH4mHVhEnqP16+/mAU05r1U6NIrc0fGDWP89tZ8GzeJZpe+w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@intlify/core-base": "10.0.7",
- "@intlify/shared": "10.0.7",
- "@vue/devtools-api": "^6.5.0"
- },
- "engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/kazupon"
- },
- "peerDependencies": {
- "vue": "^3.0.0"
- }
- },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -6040,8 +5923,7 @@
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
- "dev": true,
- "license": "MIT"
+ "dev": true
},
"node_modules/config-chain": {
"version": "1.1.13",
@@ -6848,7 +6730,6 @@
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
@@ -9092,7 +8973,6 @@
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz",
"integrity": "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"acorn": "^8.5.0",
"eslint-visitor-keys": "^3.0.0",
@@ -9111,7 +8991,6 @@
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
@@ -9444,7 +9323,6 @@
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
"integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"acorn": "^8.14.0",
"pathe": "^2.0.1",
@@ -10118,7 +9996,6 @@
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"confbox": "^0.1.8",
"mlly": "^1.7.4",
@@ -12273,8 +12150,7 @@
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
- "dev": true,
- "license": "MIT"
+ "dev": true
},
"node_modules/unbox-primitive": {
"version": "1.1.0",
@@ -13868,7 +13744,6 @@
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.0.tgz",
"integrity": "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"eslint-visitor-keys": "^3.0.0",
"yaml": "^2.0.0"
diff --git a/fe/package.json b/fe/package.json
index 6f3f247..1a6b9d6 100644
--- a/fe/package.json
+++ b/fe/package.json
@@ -34,7 +34,7 @@
"workbox-background-sync": "^7.3.0"
},
"devDependencies": {
- "@intlify/unplugin-vue-i18n": "^6.0.8",
+ "@intlify/unplugin-vue-i18n": "^4.0.0",
"@playwright/test": "^1.51.1",
"@storybook/addon-docs": "^9.0.2",
"@storybook/addon-onboarding": "^9.0.2",
diff --git a/fe/src/i18n/de.json b/fe/src/i18n/de.json
new file mode 100644
index 0000000..838bc94
--- /dev/null
+++ b/fe/src/i18n/de.json
@@ -0,0 +1,274 @@
+{
+ "message": {
+ "hello": "Hallo"
+ },
+ "loginPage": {
+ "emailLabel": "DE: Email",
+ "passwordLabel": "DE: Password",
+ "togglePasswordVisibilityLabel": "DE: Toggle password visibility",
+ "loginButton": "DE: Login",
+ "signupLink": "DE: Don't have an account? Sign up",
+ "errors": {
+ "emailRequired": "DE: Email is required",
+ "emailInvalid": "DE: Invalid email format",
+ "passwordRequired": "DE: Password is required",
+ "loginFailed": "DE: Login failed. Please check your credentials."
+ },
+ "notifications": {
+ "loginSuccess": "DE: Login successful"
+ }
+ },
+ "listsPage": {
+ "retryButton": "DE: Retry",
+ "emptyState": {
+ "noListsForGroup": "DE: No lists found for this group.",
+ "noListsYet": "DE: You have no lists yet.",
+ "personalGlobalInfo": "DE: Create a personal list or join a group to see shared lists.",
+ "groupSpecificInfo": "DE: This group doesn't have any lists yet."
+ },
+ "createNewListButton": "DE: Create New List",
+ "loadingLists": "DE: Loading lists...",
+ "noDescription": "DE: No description",
+ "addItemPlaceholder": "DE: Add new item...",
+ "createCard": {
+ "title": "DE: + Create a new list"
+ },
+ "pageTitle": {
+ "forGroup": "DE: Lists for {groupName}",
+ "forGroupId": "DE: Lists for Group {groupId}",
+ "myLists": "DE: My Lists"
+ },
+ "errors": {
+ "fetchFailed": "DE: Failed to fetch lists."
+ }
+ },
+ "groupsPage": {
+ "retryButton": "DE: Retry",
+ "emptyState": {
+ "title": "DE: No Groups Yet!",
+ "description": "DE: You are not a member of any groups yet. Create one or join using an invite code.",
+ "createButton": "DE: Create New Group"
+ },
+ "groupCard": {
+ "newListButton": "DE: List"
+ },
+ "createCard": {
+ "title": "DE: + Group"
+ },
+ "joinGroup": {
+ "title": "DE: Join a Group with Invite Code",
+ "inputLabel": "DE: Enter Invite Code",
+ "inputPlaceholder": "DE: Enter Invite Code",
+ "joinButton": "DE: Join"
+ },
+ "createDialog": {
+ "title": "DE: Create New Group",
+ "closeButtonLabel": "DE: Close",
+ "groupNameLabel": "DE: Group Name",
+ "cancelButton": "DE: Cancel",
+ "createButton": "DE: Create"
+ },
+ "errors": {
+ "fetchFailed": "DE: Failed to load groups",
+ "groupNameRequired": "DE: Group name is required",
+ "createFailed": "DE: Failed to create group. Please try again.",
+ "inviteCodeRequired": "DE: Invite code is required",
+ "joinFailed": "DE: Failed to join group. Please check the invite code and try again."
+ },
+ "notifications": {
+ "groupCreatedSuccess": "DE: Group '{groupName}' created successfully.",
+ "joinSuccessNamed": "DE: Successfully joined group '{groupName}'.",
+ "joinSuccessGeneric": "DE: Successfully joined group.",
+ "listCreatedSuccess": "DE: List '{listName}' created successfully."
+ }
+ },
+ "authCallbackPage": {
+ "redirecting": "DE: Redirecting...",
+ "errors": {
+ "authenticationFailed": "DE: Authentication failed"
+ }
+ },
+ "choresPage": {
+ "title": "DE: Chores",
+ "tabs": {
+ "overdue": "DE: Overdue",
+ "today": "DE: Today",
+ "upcoming": "DE: Upcoming",
+ "allPending": "DE: All Pending",
+ "completed": "DE: Completed"
+ },
+ "viewToggle": {
+ "calendarLabel": "DE: Calendar View",
+ "calendarText": "DE: Calendar",
+ "listLabel": "DE: List View",
+ "listText": "DE: List"
+ },
+ "newChoreButtonLabel": "DE: New Chore",
+ "newChoreButtonText": "DE: New Chore",
+ "loadingState": {
+ "loadingChores": "DE: Loading chores..."
+ },
+ "calendar": {
+ "prevMonthLabel": "DE: Previous month",
+ "nextMonthLabel": "DE: Next month",
+ "weekdays": {
+ "sun": "DE: Sun",
+ "mon": "DE: Mon",
+ "tue": "DE: Tue",
+ "wed": "DE: Wed",
+ "thu": "DE: Thu",
+ "fri": "DE: Fri",
+ "sat": "DE: Sat"
+ },
+ "addChoreToDayLabel": "DE: Add chore to this day",
+ "emptyState": "DE: No chores to display for this period."
+ },
+ "listView": {
+ "choreTypePersonal": "DE: Personal",
+ "choreTypeGroupFallback": "DE: Group",
+ "completedDatePrefix": "DE: Completed:",
+ "actions": {
+ "doneTitle": "DE: Mark as Done",
+ "doneText": "DE: Done",
+ "undoTitle": "DE: Mark as Not Done",
+ "undoText": "DE: Undo",
+ "editTitle": "DE: Edit",
+ "editLabel": "DE: Edit chore",
+ "editText": "DE: Edit",
+ "deleteTitle": "DE: Delete",
+ "deleteLabel": "DE: Delete chore",
+ "deleteText": "DE: Delete"
+ },
+ "emptyState": {
+ "message": "DE: No chores in this view. Well done!",
+ "viewAllButton": "DE: View All Pending"
+ }
+ },
+ "choreModal": {
+ "editTitle": "DE: Edit Chore",
+ "newTitle": "DE: New Chore",
+ "closeButtonLabel": "DE: Close modal",
+ "nameLabel": "DE: Name",
+ "namePlaceholder": "DE: Enter chore name",
+ "typeLabel": "DE: Type",
+ "typePersonal": "DE: Personal",
+ "typeGroup": "DE: Group",
+ "groupLabel": "DE: Group",
+ "groupSelectDefault": "DE: Select a group",
+ "descriptionLabel": "DE: Description",
+ "descriptionPlaceholder": "DE: Add a description (optional)",
+ "frequencyLabel": "DE: Frequency",
+ "intervalLabel": "DE: Interval (days)",
+ "intervalPlaceholder": "DE: e.g. 3",
+ "dueDateLabel": "DE: Due Date",
+ "quickDueDateToday": "DE: Today",
+ "quickDueDateTomorrow": "DE: Tomorrow",
+ "quickDueDateNextWeek": "DE: Next Week",
+ "cancelButton": "DE: Cancel",
+ "saveButton": "DE: Save"
+ },
+ "deleteDialog": {
+ "title": "DE: Delete Chore",
+ "confirmationText": "DE: Are you sure you want to delete this chore? This action cannot be undone.",
+ "deleteButton": "DE: Delete"
+ },
+ "shortcutsModal": {
+ "title": "DE: Keyboard Shortcuts",
+ "descNewChore": "DE: New Chore",
+ "descToggleView": "DE: Toggle View (List/Calendar)",
+ "descToggleShortcuts": "DE: Show/Hide Shortcuts",
+ "descCloseModal": "DE: Close any open Modal/Dialog"
+ },
+ "frequencyOptions": {
+ "oneTime": "DE: One Time",
+ "daily": "DE: Daily",
+ "weekly": "DE: Weekly",
+ "monthly": "DE: Monthly",
+ "custom": "DE: Custom"
+ },
+ "formatters": {
+ "noDueDate": "DE: No due date",
+ "dueToday": "DE: Due Today",
+ "dueTomorrow": "DE: Due Tomorrow",
+ "overdueFull": "DE: Overdue: {date}",
+ "dueFull": "DE: Due {date}",
+ "invalidDate": "DE: Invalid Date"
+ },
+ "notifications": {
+ "loadFailed": "DE: Failed to load chores",
+ "updateSuccess": "DE: Chore '{name}' updated successfully",
+ "createSuccess": "DE: Chore '{name}' created successfully",
+ "updateFailed": "DE: Failed to update chore",
+ "createFailed": "DE: Failed to create chore",
+ "deleteSuccess": "DE: Chore '{name}' deleted successfully",
+ "deleteFailed": "DE: Failed to delete chore",
+ "markedDone": "DE: {name} marked as done.",
+ "markedNotDone": "DE: {name} marked as not done.",
+ "statusUpdateFailed": "DE: Failed to update chore status."
+ },
+ "validation": {
+ "nameRequired": "DE: Chore name is required.",
+ "groupRequired": "DE: Please select a group for group chores.",
+ "intervalRequired": "DE: Custom interval must be at least 1 day.",
+ "dueDateRequired": "DE: Due date is required.",
+ "invalidDueDate": "DE: Invalid due date format."
+ },
+ "unsavedChangesConfirmation": "DE: You have unsaved changes in the chore form. Are you sure you want to leave?"
+ },
+ "errorNotFoundPage": {
+ "errorCode": "DE: 404",
+ "errorMessage": "DE: Oops. Nothing here...",
+ "goHomeButton": "DE: Go Home"
+ },
+ "groupDetailPage": {
+ "loadingLabel": "DE: Loading group details...",
+ "retryButton": "DE: Retry",
+ "groupNotFound": "DE: Group not found or an error occurred.",
+ "members": {
+ "title": "DE: Group Members",
+ "defaultRole": "DE: Member",
+ "removeButton": "DE: Remove",
+ "emptyState": "DE: No members found."
+ },
+ "invites": {
+ "title": "DE: Invite Members",
+ "regenerateButton": "DE: Regenerate Invite Code",
+ "generateButton": "DE: Generate Invite Code",
+ "activeCodeLabel": "DE: Current Active Invite Code:",
+ "copyButtonLabel": "DE: Copy invite code",
+ "copySuccess": "DE: Invite code copied to clipboard!",
+ "emptyState": "DE: No active invite code. Click the button above to generate one.",
+ "errors": {
+ "newDataInvalid": "DE: New invite code data is invalid."
+ }
+ },
+ "chores": {
+ "title": "DE: Group Chores",
+ "manageButton": "DE: Manage Chores",
+ "duePrefix": "DE: Due:",
+ "emptyState": "DE: No chores scheduled. Click \"Manage Chores\" to create some!"
+ },
+ "expenses": {
+ "title": "DE: Group Expenses",
+ "manageButton": "DE: Manage Expenses",
+ "emptyState": "DE: No expenses recorded. Click \"Manage Expenses\" to add some!",
+ "splitTypes": {
+ "equal": "DE: Equal",
+ "exactAmounts": "DE: Exact Amounts",
+ "percentage": "DE: Percentage",
+ "shares": "DE: Shares",
+ "itemBased": "DE: Item Based"
+ }
+ },
+ "notifications": {
+ "fetchDetailsFailed": "DE: Failed to fetch group details.",
+ "fetchInviteFailed": "DE: Failed to fetch active invite code.",
+ "generateInviteSuccess": "DE: New invite code generated successfully!",
+ "generateInviteError": "DE: Failed to generate invite code.",
+ "clipboardNotSupported": "DE: Clipboard not supported or no code to copy.",
+ "copyInviteFailed": "DE: Failed to copy invite code.",
+ "removeMemberSuccess": "DE: Member removed successfully",
+ "removeMemberFailed": "DE: Failed to remove member"
+ }
+ }
+}
diff --git a/fe/src/i18n/en-US/index.ts b/fe/src/i18n/en-US/index.ts
deleted file mode 100644
index d555d3f..0000000
--- a/fe/src/i18n/en-US/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// This is just an example,
-// so you can safely delete all default props below
-
-export default {
- failed: 'Action failed',
- success: 'Action was successful'
-};
diff --git a/fe/src/i18n/en.json b/fe/src/i18n/en.json
new file mode 100644
index 0000000..133984c
--- /dev/null
+++ b/fe/src/i18n/en.json
@@ -0,0 +1,274 @@
+{
+ "message": {
+ "hello": "Hello"
+ },
+ "loginPage": {
+ "emailLabel": "Email",
+ "passwordLabel": "Password",
+ "togglePasswordVisibilityLabel": "Toggle password visibility",
+ "loginButton": "Login",
+ "signupLink": "Don't have an account? Sign up",
+ "errors": {
+ "emailRequired": "Email is required",
+ "emailInvalid": "Invalid email format",
+ "passwordRequired": "Password is required",
+ "loginFailed": "Login failed. Please check your credentials."
+ },
+ "notifications": {
+ "loginSuccess": "Login successful"
+ }
+ },
+ "listsPage": {
+ "retryButton": "Retry",
+ "emptyState": {
+ "noListsForGroup": "No lists found for this group.",
+ "noListsYet": "You have no lists yet.",
+ "personalGlobalInfo": "Create a personal list or join a group to see shared lists.",
+ "groupSpecificInfo": "This group doesn't have any lists yet."
+ },
+ "createNewListButton": "Create New List",
+ "loadingLists": "Loading lists...",
+ "noDescription": "No description",
+ "addItemPlaceholder": "Add new item...",
+ "createCard": {
+ "title": "+ Create a new list"
+ },
+ "pageTitle": {
+ "forGroup": "Lists for {groupName}",
+ "forGroupId": "Lists for Group {groupId}",
+ "myLists": "My Lists"
+ },
+ "errors": {
+ "fetchFailed": "Failed to fetch lists."
+ }
+ },
+ "groupsPage": {
+ "retryButton": "Retry",
+ "emptyState": {
+ "title": "No Groups Yet!",
+ "description": "You are not a member of any groups yet. Create one or join using an invite code.",
+ "createButton": "Create New Group"
+ },
+ "groupCard": {
+ "newListButton": "List"
+ },
+ "createCard": {
+ "title": "+ Group"
+ },
+ "joinGroup": {
+ "title": "Join a Group with Invite Code",
+ "inputLabel": "Enter Invite Code",
+ "inputPlaceholder": "Enter Invite Code",
+ "joinButton": "Join"
+ },
+ "createDialog": {
+ "title": "Create New Group",
+ "closeButtonLabel": "Close",
+ "groupNameLabel": "Group Name",
+ "cancelButton": "Cancel",
+ "createButton": "Create"
+ },
+ "errors": {
+ "fetchFailed": "Failed to load groups",
+ "groupNameRequired": "Group name is required",
+ "createFailed": "Failed to create group. Please try again.",
+ "inviteCodeRequired": "Invite code is required",
+ "joinFailed": "Failed to join group. Please check the invite code and try again."
+ },
+ "notifications": {
+ "groupCreatedSuccess": "Group '{groupName}' created successfully.",
+ "joinSuccessNamed": "Successfully joined group '{groupName}'.",
+ "joinSuccessGeneric": "Successfully joined group.",
+ "listCreatedSuccess": "List '{listName}' created successfully."
+ }
+ },
+ "authCallbackPage": {
+ "redirecting": "Redirecting...",
+ "errors": {
+ "authenticationFailed": "Authentication failed"
+ }
+ },
+ "choresPage": {
+ "title": "Chores",
+ "tabs": {
+ "overdue": "Overdue",
+ "today": "Today",
+ "upcoming": "Upcoming",
+ "allPending": "All Pending",
+ "completed": "Completed"
+ },
+ "viewToggle": {
+ "calendarLabel": "Calendar View",
+ "calendarText": "Calendar",
+ "listLabel": "List View",
+ "listText": "List"
+ },
+ "newChoreButtonLabel": "New Chore",
+ "newChoreButtonText": "New Chore",
+ "loadingState": {
+ "loadingChores": "Loading chores..."
+ },
+ "calendar": {
+ "prevMonthLabel": "Previous month",
+ "nextMonthLabel": "Next month",
+ "weekdays": {
+ "sun": "Sun",
+ "mon": "Mon",
+ "tue": "Tue",
+ "wed": "Wed",
+ "thu": "Thu",
+ "fri": "Fri",
+ "sat": "Sat"
+ },
+ "addChoreToDayLabel": "Add chore to this day",
+ "emptyState": "No chores to display for this period."
+ },
+ "listView": {
+ "choreTypePersonal": "Personal",
+ "choreTypeGroupFallback": "Group",
+ "completedDatePrefix": "Completed:",
+ "actions": {
+ "doneTitle": "Mark as Done",
+ "doneText": "Done",
+ "undoTitle": "Mark as Not Done",
+ "undoText": "Undo",
+ "editTitle": "Edit",
+ "editLabel": "Edit chore",
+ "editText": "Edit",
+ "deleteTitle": "Delete",
+ "deleteLabel": "Delete chore",
+ "deleteText": "Delete"
+ },
+ "emptyState": {
+ "message": "No chores in this view. Well done!",
+ "viewAllButton": "View All Pending"
+ }
+ },
+ "choreModal": {
+ "editTitle": "Edit Chore",
+ "newTitle": "New Chore",
+ "closeButtonLabel": "Close modal",
+ "nameLabel": "Name",
+ "namePlaceholder": "Enter chore name",
+ "typeLabel": "Type",
+ "typePersonal": "Personal",
+ "typeGroup": "Group",
+ "groupLabel": "Group",
+ "groupSelectDefault": "Select a group",
+ "descriptionLabel": "Description",
+ "descriptionPlaceholder": "Add a description (optional)",
+ "frequencyLabel": "Frequency",
+ "intervalLabel": "Interval (days)",
+ "intervalPlaceholder": "e.g. 3",
+ "dueDateLabel": "Due Date",
+ "quickDueDateToday": "Today",
+ "quickDueDateTomorrow": "Tomorrow",
+ "quickDueDateNextWeek": "Next Week",
+ "cancelButton": "Cancel",
+ "saveButton": "Save"
+ },
+ "deleteDialog": {
+ "title": "Delete Chore",
+ "confirmationText": "Are you sure you want to delete this chore? This action cannot be undone.",
+ "deleteButton": "Delete"
+ },
+ "shortcutsModal": {
+ "title": "Keyboard Shortcuts",
+ "descNewChore": "New Chore",
+ "descToggleView": "Toggle View (List/Calendar)",
+ "descToggleShortcuts": "Show/Hide Shortcuts",
+ "descCloseModal": "Close any open Modal/Dialog"
+ },
+ "frequencyOptions": {
+ "oneTime": "One Time",
+ "daily": "Daily",
+ "weekly": "Weekly",
+ "monthly": "Monthly",
+ "custom": "Custom"
+ },
+ "formatters": {
+ "noDueDate": "No due date",
+ "dueToday": "Due Today",
+ "dueTomorrow": "Due Tomorrow",
+ "overdueFull": "Overdue: {date}",
+ "dueFull": "Due {date}",
+ "invalidDate": "Invalid Date"
+ },
+ "notifications": {
+ "loadFailed": "Failed to load chores",
+ "updateSuccess": "Chore '{name}' updated successfully",
+ "createSuccess": "Chore '{name}' created successfully",
+ "updateFailed": "Failed to update chore",
+ "createFailed": "Failed to create chore",
+ "deleteSuccess": "Chore '{name}' deleted successfully",
+ "deleteFailed": "Failed to delete chore",
+ "markedDone": "{name} marked as done.",
+ "markedNotDone": "{name} marked as not done.",
+ "statusUpdateFailed": "Failed to update chore status."
+ },
+ "validation": {
+ "nameRequired": "Chore name is required.",
+ "groupRequired": "Please select a group for group chores.",
+ "intervalRequired": "Custom interval must be at least 1 day.",
+ "dueDateRequired": "Due date is required.",
+ "invalidDueDate": "Invalid due date format."
+ },
+ "unsavedChangesConfirmation": "You have unsaved changes in the chore form. Are you sure you want to leave?"
+ },
+ "errorNotFoundPage": {
+ "errorCode": "404",
+ "errorMessage": "Oops. Nothing here...",
+ "goHomeButton": "Go Home"
+ },
+ "groupDetailPage": {
+ "loadingLabel": "Loading group details...",
+ "retryButton": "Retry",
+ "groupNotFound": "Group not found or an error occurred.",
+ "members": {
+ "title": "Group Members",
+ "defaultRole": "Member",
+ "removeButton": "Remove",
+ "emptyState": "No members found."
+ },
+ "invites": {
+ "title": "Invite Members",
+ "regenerateButton": "Regenerate Invite Code",
+ "generateButton": "Generate Invite Code",
+ "activeCodeLabel": "Current Active Invite Code:",
+ "copyButtonLabel": "Copy invite code",
+ "copySuccess": "Invite code copied to clipboard!",
+ "emptyState": "No active invite code. Click the button above to generate one.",
+ "errors": {
+ "newDataInvalid": "New invite code data is invalid."
+ }
+ },
+ "chores": {
+ "title": "Group Chores",
+ "manageButton": "Manage Chores",
+ "duePrefix": "Due:",
+ "emptyState": "No chores scheduled. Click \"Manage Chores\" to create some!"
+ },
+ "expenses": {
+ "title": "Group Expenses",
+ "manageButton": "Manage Expenses",
+ "emptyState": "No expenses recorded. Click \"Manage Expenses\" to add some!",
+ "splitTypes": {
+ "equal": "Equal",
+ "exactAmounts": "Exact Amounts",
+ "percentage": "Percentage",
+ "shares": "Shares",
+ "itemBased": "Item Based"
+ }
+ },
+ "notifications": {
+ "fetchDetailsFailed": "Failed to fetch group details.",
+ "fetchInviteFailed": "Failed to fetch active invite code.",
+ "generateInviteSuccess": "New invite code generated successfully!",
+ "generateInviteError": "Failed to generate invite code.",
+ "clipboardNotSupported": "Clipboard not supported or no code to copy.",
+ "copyInviteFailed": "Failed to copy invite code.",
+ "removeMemberSuccess": "Member removed successfully",
+ "removeMemberFailed": "Failed to remove member"
+ }
+ }
+}
diff --git a/fe/src/i18n/es.json b/fe/src/i18n/es.json
new file mode 100644
index 0000000..b57d2e5
--- /dev/null
+++ b/fe/src/i18n/es.json
@@ -0,0 +1,274 @@
+{
+ "message": {
+ "hello": "Hola"
+ },
+ "loginPage": {
+ "emailLabel": "ES: Email",
+ "passwordLabel": "ES: Password",
+ "togglePasswordVisibilityLabel": "ES: Toggle password visibility",
+ "loginButton": "ES: Login",
+ "signupLink": "ES: Don't have an account? Sign up",
+ "errors": {
+ "emailRequired": "ES: Email is required",
+ "emailInvalid": "ES: Invalid email format",
+ "passwordRequired": "ES: Password is required",
+ "loginFailed": "ES: Login failed. Please check your credentials."
+ },
+ "notifications": {
+ "loginSuccess": "ES: Login successful"
+ }
+ },
+ "listsPage": {
+ "retryButton": "ES: Retry",
+ "emptyState": {
+ "noListsForGroup": "ES: No lists found for this group.",
+ "noListsYet": "ES: You have no lists yet.",
+ "personalGlobalInfo": "ES: Create a personal list or join a group to see shared lists.",
+ "groupSpecificInfo": "ES: This group doesn't have any lists yet."
+ },
+ "createNewListButton": "ES: Create New List",
+ "loadingLists": "ES: Loading lists...",
+ "noDescription": "ES: No description",
+ "addItemPlaceholder": "ES: Add new item...",
+ "createCard": {
+ "title": "ES: + Create a new list"
+ },
+ "pageTitle": {
+ "forGroup": "ES: Lists for {groupName}",
+ "forGroupId": "ES: Lists for Group {groupId}",
+ "myLists": "ES: My Lists"
+ },
+ "errors": {
+ "fetchFailed": "ES: Failed to fetch lists."
+ }
+ },
+ "groupsPage": {
+ "retryButton": "ES: Retry",
+ "emptyState": {
+ "title": "ES: No Groups Yet!",
+ "description": "ES: You are not a member of any groups yet. Create one or join using an invite code.",
+ "createButton": "ES: Create New Group"
+ },
+ "groupCard": {
+ "newListButton": "ES: List"
+ },
+ "createCard": {
+ "title": "ES: + Group"
+ },
+ "joinGroup": {
+ "title": "ES: Join a Group with Invite Code",
+ "inputLabel": "ES: Enter Invite Code",
+ "inputPlaceholder": "ES: Enter Invite Code",
+ "joinButton": "ES: Join"
+ },
+ "createDialog": {
+ "title": "ES: Create New Group",
+ "closeButtonLabel": "ES: Close",
+ "groupNameLabel": "ES: Group Name",
+ "cancelButton": "ES: Cancel",
+ "createButton": "ES: Create"
+ },
+ "errors": {
+ "fetchFailed": "ES: Failed to load groups",
+ "groupNameRequired": "ES: Group name is required",
+ "createFailed": "ES: Failed to create group. Please try again.",
+ "inviteCodeRequired": "ES: Invite code is required",
+ "joinFailed": "ES: Failed to join group. Please check the invite code and try again."
+ },
+ "notifications": {
+ "groupCreatedSuccess": "ES: Group '{groupName}' created successfully.",
+ "joinSuccessNamed": "ES: Successfully joined group '{groupName}'.",
+ "joinSuccessGeneric": "ES: Successfully joined group.",
+ "listCreatedSuccess": "ES: List '{listName}' created successfully."
+ }
+ },
+ "authCallbackPage": {
+ "redirecting": "ES: Redirecting...",
+ "errors": {
+ "authenticationFailed": "ES: Authentication failed"
+ }
+ },
+ "choresPage": {
+ "title": "ES: Chores",
+ "tabs": {
+ "overdue": "ES: Overdue",
+ "today": "ES: Today",
+ "upcoming": "ES: Upcoming",
+ "allPending": "ES: All Pending",
+ "completed": "ES: Completed"
+ },
+ "viewToggle": {
+ "calendarLabel": "ES: Calendar View",
+ "calendarText": "ES: Calendar",
+ "listLabel": "ES: List View",
+ "listText": "ES: List"
+ },
+ "newChoreButtonLabel": "ES: New Chore",
+ "newChoreButtonText": "ES: New Chore",
+ "loadingState": {
+ "loadingChores": "ES: Loading chores..."
+ },
+ "calendar": {
+ "prevMonthLabel": "ES: Previous month",
+ "nextMonthLabel": "ES: Next month",
+ "weekdays": {
+ "sun": "ES: Sun",
+ "mon": "ES: Mon",
+ "tue": "ES: Tue",
+ "wed": "ES: Wed",
+ "thu": "ES: Thu",
+ "fri": "ES: Fri",
+ "sat": "ES: Sat"
+ },
+ "addChoreToDayLabel": "ES: Add chore to this day",
+ "emptyState": "ES: No chores to display for this period."
+ },
+ "listView": {
+ "choreTypePersonal": "ES: Personal",
+ "choreTypeGroupFallback": "ES: Group",
+ "completedDatePrefix": "ES: Completed:",
+ "actions": {
+ "doneTitle": "ES: Mark as Done",
+ "doneText": "ES: Done",
+ "undoTitle": "ES: Mark as Not Done",
+ "undoText": "ES: Undo",
+ "editTitle": "ES: Edit",
+ "editLabel": "ES: Edit chore",
+ "editText": "ES: Edit",
+ "deleteTitle": "ES: Delete",
+ "deleteLabel": "ES: Delete chore",
+ "deleteText": "ES: Delete"
+ },
+ "emptyState": {
+ "message": "ES: No chores in this view. Well done!",
+ "viewAllButton": "ES: View All Pending"
+ }
+ },
+ "choreModal": {
+ "editTitle": "ES: Edit Chore",
+ "newTitle": "ES: New Chore",
+ "closeButtonLabel": "ES: Close modal",
+ "nameLabel": "ES: Name",
+ "namePlaceholder": "ES: Enter chore name",
+ "typeLabel": "ES: Type",
+ "typePersonal": "ES: Personal",
+ "typeGroup": "ES: Group",
+ "groupLabel": "ES: Group",
+ "groupSelectDefault": "ES: Select a group",
+ "descriptionLabel": "ES: Description",
+ "descriptionPlaceholder": "ES: Add a description (optional)",
+ "frequencyLabel": "ES: Frequency",
+ "intervalLabel": "ES: Interval (days)",
+ "intervalPlaceholder": "ES: e.g. 3",
+ "dueDateLabel": "ES: Due Date",
+ "quickDueDateToday": "ES: Today",
+ "quickDueDateTomorrow": "ES: Tomorrow",
+ "quickDueDateNextWeek": "ES: Next Week",
+ "cancelButton": "ES: Cancel",
+ "saveButton": "ES: Save"
+ },
+ "deleteDialog": {
+ "title": "ES: Delete Chore",
+ "confirmationText": "ES: Are you sure you want to delete this chore? This action cannot be undone.",
+ "deleteButton": "ES: Delete"
+ },
+ "shortcutsModal": {
+ "title": "ES: Keyboard Shortcuts",
+ "descNewChore": "ES: New Chore",
+ "descToggleView": "ES: Toggle View (List/Calendar)",
+ "descToggleShortcuts": "ES: Show/Hide Shortcuts",
+ "descCloseModal": "ES: Close any open Modal/Dialog"
+ },
+ "frequencyOptions": {
+ "oneTime": "ES: One Time",
+ "daily": "ES: Daily",
+ "weekly": "ES: Weekly",
+ "monthly": "ES: Monthly",
+ "custom": "ES: Custom"
+ },
+ "formatters": {
+ "noDueDate": "ES: No due date",
+ "dueToday": "ES: Due Today",
+ "dueTomorrow": "ES: Due Tomorrow",
+ "overdueFull": "ES: Overdue: {date}",
+ "dueFull": "ES: Due {date}",
+ "invalidDate": "ES: Invalid Date"
+ },
+ "notifications": {
+ "loadFailed": "ES: Failed to load chores",
+ "updateSuccess": "ES: Chore '{name}' updated successfully",
+ "createSuccess": "ES: Chore '{name}' created successfully",
+ "updateFailed": "ES: Failed to update chore",
+ "createFailed": "ES: Failed to create chore",
+ "deleteSuccess": "ES: Chore '{name}' deleted successfully",
+ "deleteFailed": "ES: Failed to delete chore",
+ "markedDone": "ES: {name} marked as done.",
+ "markedNotDone": "ES: {name} marked as not done.",
+ "statusUpdateFailed": "ES: Failed to update chore status."
+ },
+ "validation": {
+ "nameRequired": "ES: Chore name is required.",
+ "groupRequired": "ES: Please select a group for group chores.",
+ "intervalRequired": "ES: Custom interval must be at least 1 day.",
+ "dueDateRequired": "ES: Due date is required.",
+ "invalidDueDate": "ES: Invalid due date format."
+ },
+ "unsavedChangesConfirmation": "ES: You have unsaved changes in the chore form. Are you sure you want to leave?"
+ },
+ "errorNotFoundPage": {
+ "errorCode": "ES: 404",
+ "errorMessage": "ES: Oops. Nothing here...",
+ "goHomeButton": "ES: Go Home"
+ },
+ "groupDetailPage": {
+ "loadingLabel": "ES: Loading group details...",
+ "retryButton": "ES: Retry",
+ "groupNotFound": "ES: Group not found or an error occurred.",
+ "members": {
+ "title": "ES: Group Members",
+ "defaultRole": "ES: Member",
+ "removeButton": "ES: Remove",
+ "emptyState": "ES: No members found."
+ },
+ "invites": {
+ "title": "ES: Invite Members",
+ "regenerateButton": "ES: Regenerate Invite Code",
+ "generateButton": "ES: Generate Invite Code",
+ "activeCodeLabel": "ES: Current Active Invite Code:",
+ "copyButtonLabel": "ES: Copy invite code",
+ "copySuccess": "ES: Invite code copied to clipboard!",
+ "emptyState": "ES: No active invite code. Click the button above to generate one.",
+ "errors": {
+ "newDataInvalid": "ES: New invite code data is invalid."
+ }
+ },
+ "chores": {
+ "title": "ES: Group Chores",
+ "manageButton": "ES: Manage Chores",
+ "duePrefix": "ES: Due:",
+ "emptyState": "ES: No chores scheduled. Click \"Manage Chores\" to create some!"
+ },
+ "expenses": {
+ "title": "ES: Group Expenses",
+ "manageButton": "ES: Manage Expenses",
+ "emptyState": "ES: No expenses recorded. Click \"Manage Expenses\" to add some!",
+ "splitTypes": {
+ "equal": "ES: Equal",
+ "exactAmounts": "ES: Exact Amounts",
+ "percentage": "ES: Percentage",
+ "shares": "ES: Shares",
+ "itemBased": "ES: Item Based"
+ }
+ },
+ "notifications": {
+ "fetchDetailsFailed": "ES: Failed to fetch group details.",
+ "fetchInviteFailed": "ES: Failed to fetch active invite code.",
+ "generateInviteSuccess": "ES: New invite code generated successfully!",
+ "generateInviteError": "ES: Failed to generate invite code.",
+ "clipboardNotSupported": "ES: Clipboard not supported or no code to copy.",
+ "copyInviteFailed": "ES: Failed to copy invite code.",
+ "removeMemberSuccess": "ES: Member removed successfully",
+ "removeMemberFailed": "ES: Failed to remove member"
+ }
+ }
+}
diff --git a/fe/src/i18n/fr.json b/fe/src/i18n/fr.json
new file mode 100644
index 0000000..0251c52
--- /dev/null
+++ b/fe/src/i18n/fr.json
@@ -0,0 +1,274 @@
+{
+ "message": {
+ "hello": "Bonjour"
+ },
+ "loginPage": {
+ "emailLabel": "FR: Email",
+ "passwordLabel": "FR: Password",
+ "togglePasswordVisibilityLabel": "FR: Toggle password visibility",
+ "loginButton": "FR: Login",
+ "signupLink": "FR: Don't have an account? Sign up",
+ "errors": {
+ "emailRequired": "FR: Email is required",
+ "emailInvalid": "FR: Invalid email format",
+ "passwordRequired": "FR: Password is required",
+ "loginFailed": "FR: Login failed. Please check your credentials."
+ },
+ "notifications": {
+ "loginSuccess": "FR: Login successful"
+ }
+ },
+ "listsPage": {
+ "retryButton": "FR: Retry",
+ "emptyState": {
+ "noListsForGroup": "FR: No lists found for this group.",
+ "noListsYet": "FR: You have no lists yet.",
+ "personalGlobalInfo": "FR: Create a personal list or join a group to see shared lists.",
+ "groupSpecificInfo": "FR: This group doesn't have any lists yet."
+ },
+ "createNewListButton": "FR: Create New List",
+ "loadingLists": "FR: Loading lists...",
+ "noDescription": "FR: No description",
+ "addItemPlaceholder": "FR: Add new item...",
+ "createCard": {
+ "title": "FR: + Create a new list"
+ },
+ "pageTitle": {
+ "forGroup": "FR: Lists for {groupName}",
+ "forGroupId": "FR: Lists for Group {groupId}",
+ "myLists": "FR: My Lists"
+ },
+ "errors": {
+ "fetchFailed": "FR: Failed to fetch lists."
+ }
+ },
+ "groupsPage": {
+ "retryButton": "FR: Retry",
+ "emptyState": {
+ "title": "FR: No Groups Yet!",
+ "description": "FR: You are not a member of any groups yet. Create one or join using an invite code.",
+ "createButton": "FR: Create New Group"
+ },
+ "groupCard": {
+ "newListButton": "FR: List"
+ },
+ "createCard": {
+ "title": "FR: + Group"
+ },
+ "joinGroup": {
+ "title": "FR: Join a Group with Invite Code",
+ "inputLabel": "FR: Enter Invite Code",
+ "inputPlaceholder": "FR: Enter Invite Code",
+ "joinButton": "FR: Join"
+ },
+ "createDialog": {
+ "title": "FR: Create New Group",
+ "closeButtonLabel": "FR: Close",
+ "groupNameLabel": "FR: Group Name",
+ "cancelButton": "FR: Cancel",
+ "createButton": "FR: Create"
+ },
+ "errors": {
+ "fetchFailed": "FR: Failed to load groups",
+ "groupNameRequired": "FR: Group name is required",
+ "createFailed": "FR: Failed to create group. Please try again.",
+ "inviteCodeRequired": "FR: Invite code is required",
+ "joinFailed": "FR: Failed to join group. Please check the invite code and try again."
+ },
+ "notifications": {
+ "groupCreatedSuccess": "FR: Group '{groupName}' created successfully.",
+ "joinSuccessNamed": "FR: Successfully joined group '{groupName}'.",
+ "joinSuccessGeneric": "FR: Successfully joined group.",
+ "listCreatedSuccess": "FR: List '{listName}' created successfully."
+ }
+ },
+ "authCallbackPage": {
+ "redirecting": "FR: Redirecting...",
+ "errors": {
+ "authenticationFailed": "FR: Authentication failed"
+ }
+ },
+ "choresPage": {
+ "title": "FR: Chores",
+ "tabs": {
+ "overdue": "FR: Overdue",
+ "today": "FR: Today",
+ "upcoming": "FR: Upcoming",
+ "allPending": "FR: All Pending",
+ "completed": "FR: Completed"
+ },
+ "viewToggle": {
+ "calendarLabel": "FR: Calendar View",
+ "calendarText": "FR: Calendar",
+ "listLabel": "FR: List View",
+ "listText": "FR: List"
+ },
+ "newChoreButtonLabel": "FR: New Chore",
+ "newChoreButtonText": "FR: New Chore",
+ "loadingState": {
+ "loadingChores": "FR: Loading chores..."
+ },
+ "calendar": {
+ "prevMonthLabel": "FR: Previous month",
+ "nextMonthLabel": "FR: Next month",
+ "weekdays": {
+ "sun": "FR: Sun",
+ "mon": "FR: Mon",
+ "tue": "FR: Tue",
+ "wed": "FR: Wed",
+ "thu": "FR: Thu",
+ "fri": "FR: Fri",
+ "sat": "FR: Sat"
+ },
+ "addChoreToDayLabel": "FR: Add chore to this day",
+ "emptyState": "FR: No chores to display for this period."
+ },
+ "listView": {
+ "choreTypePersonal": "FR: Personal",
+ "choreTypeGroupFallback": "FR: Group",
+ "completedDatePrefix": "FR: Completed:",
+ "actions": {
+ "doneTitle": "FR: Mark as Done",
+ "doneText": "FR: Done",
+ "undoTitle": "FR: Mark as Not Done",
+ "undoText": "FR: Undo",
+ "editTitle": "FR: Edit",
+ "editLabel": "FR: Edit chore",
+ "editText": "FR: Edit",
+ "deleteTitle": "FR: Delete",
+ "deleteLabel": "FR: Delete chore",
+ "deleteText": "FR: Delete"
+ },
+ "emptyState": {
+ "message": "FR: No chores in this view. Well done!",
+ "viewAllButton": "FR: View All Pending"
+ }
+ },
+ "choreModal": {
+ "editTitle": "FR: Edit Chore",
+ "newTitle": "FR: New Chore",
+ "closeButtonLabel": "FR: Close modal",
+ "nameLabel": "FR: Name",
+ "namePlaceholder": "FR: Enter chore name",
+ "typeLabel": "FR: Type",
+ "typePersonal": "FR: Personal",
+ "typeGroup": "FR: Group",
+ "groupLabel": "FR: Group",
+ "groupSelectDefault": "FR: Select a group",
+ "descriptionLabel": "FR: Description",
+ "descriptionPlaceholder": "FR: Add a description (optional)",
+ "frequencyLabel": "FR: Frequency",
+ "intervalLabel": "FR: Interval (days)",
+ "intervalPlaceholder": "FR: e.g. 3",
+ "dueDateLabel": "FR: Due Date",
+ "quickDueDateToday": "FR: Today",
+ "quickDueDateTomorrow": "FR: Tomorrow",
+ "quickDueDateNextWeek": "FR: Next Week",
+ "cancelButton": "FR: Cancel",
+ "saveButton": "FR: Save"
+ },
+ "deleteDialog": {
+ "title": "FR: Delete Chore",
+ "confirmationText": "FR: Are you sure you want to delete this chore? This action cannot be undone.",
+ "deleteButton": "FR: Delete"
+ },
+ "shortcutsModal": {
+ "title": "FR: Keyboard Shortcuts",
+ "descNewChore": "FR: New Chore",
+ "descToggleView": "FR: Toggle View (List/Calendar)",
+ "descToggleShortcuts": "FR: Show/Hide Shortcuts",
+ "descCloseModal": "FR: Close any open Modal/Dialog"
+ },
+ "frequencyOptions": {
+ "oneTime": "FR: One Time",
+ "daily": "FR: Daily",
+ "weekly": "FR: Weekly",
+ "monthly": "FR: Monthly",
+ "custom": "FR: Custom"
+ },
+ "formatters": {
+ "noDueDate": "FR: No due date",
+ "dueToday": "FR: Due Today",
+ "dueTomorrow": "FR: Due Tomorrow",
+ "overdueFull": "FR: Overdue: {date}",
+ "dueFull": "FR: Due {date}",
+ "invalidDate": "FR: Invalid Date"
+ },
+ "notifications": {
+ "loadFailed": "FR: Failed to load chores",
+ "updateSuccess": "FR: Chore '{name}' updated successfully",
+ "createSuccess": "FR: Chore '{name}' created successfully",
+ "updateFailed": "FR: Failed to update chore",
+ "createFailed": "FR: Failed to create chore",
+ "deleteSuccess": "FR: Chore '{name}' deleted successfully",
+ "deleteFailed": "FR: Failed to delete chore",
+ "markedDone": "FR: {name} marked as done.",
+ "markedNotDone": "FR: {name} marked as not done.",
+ "statusUpdateFailed": "FR: Failed to update chore status."
+ },
+ "validation": {
+ "nameRequired": "FR: Chore name is required.",
+ "groupRequired": "FR: Please select a group for group chores.",
+ "intervalRequired": "FR: Custom interval must be at least 1 day.",
+ "dueDateRequired": "FR: Due date is required.",
+ "invalidDueDate": "FR: Invalid due date format."
+ },
+ "unsavedChangesConfirmation": "FR: You have unsaved changes in the chore form. Are you sure you want to leave?"
+ },
+ "errorNotFoundPage": {
+ "errorCode": "FR: 404",
+ "errorMessage": "FR: Oops. Nothing here...",
+ "goHomeButton": "FR: Go Home"
+ },
+ "groupDetailPage": {
+ "loadingLabel": "FR: Loading group details...",
+ "retryButton": "FR: Retry",
+ "groupNotFound": "FR: Group not found or an error occurred.",
+ "members": {
+ "title": "FR: Group Members",
+ "defaultRole": "FR: Member",
+ "removeButton": "FR: Remove",
+ "emptyState": "FR: No members found."
+ },
+ "invites": {
+ "title": "FR: Invite Members",
+ "regenerateButton": "FR: Regenerate Invite Code",
+ "generateButton": "FR: Generate Invite Code",
+ "activeCodeLabel": "FR: Current Active Invite Code:",
+ "copyButtonLabel": "FR: Copy invite code",
+ "copySuccess": "FR: Invite code copied to clipboard!",
+ "emptyState": "FR: No active invite code. Click the button above to generate one.",
+ "errors": {
+ "newDataInvalid": "FR: New invite code data is invalid."
+ }
+ },
+ "chores": {
+ "title": "FR: Group Chores",
+ "manageButton": "FR: Manage Chores",
+ "duePrefix": "FR: Due:",
+ "emptyState": "FR: No chores scheduled. Click \"Manage Chores\" to create some!"
+ },
+ "expenses": {
+ "title": "FR: Group Expenses",
+ "manageButton": "FR: Manage Expenses",
+ "emptyState": "FR: No expenses recorded. Click \"Manage Expenses\" to add some!",
+ "splitTypes": {
+ "equal": "FR: Equal",
+ "exactAmounts": "FR: Exact Amounts",
+ "percentage": "FR: Percentage",
+ "shares": "FR: Shares",
+ "itemBased": "FR: Item Based"
+ }
+ },
+ "notifications": {
+ "fetchDetailsFailed": "FR: Failed to fetch group details.",
+ "fetchInviteFailed": "FR: Failed to fetch active invite code.",
+ "generateInviteSuccess": "FR: New invite code generated successfully!",
+ "generateInviteError": "FR: Failed to generate invite code.",
+ "clipboardNotSupported": "FR: Clipboard not supported or no code to copy.",
+ "copyInviteFailed": "FR: Failed to copy invite code.",
+ "removeMemberSuccess": "FR: Member removed successfully",
+ "removeMemberFailed": "FR: Failed to remove member"
+ }
+ }
+}
diff --git a/fe/src/i18n/index.ts b/fe/src/i18n/index.ts
index 5851f87..f9c5e18 100644
--- a/fe/src/i18n/index.ts
+++ b/fe/src/i18n/index.ts
@@ -1,5 +1,11 @@
-import enUS from './en-US';
+import en from './en.json'; // Changed from enUS and path
+import de from './de.json';
+import fr from './fr.json';
+import es from './es.json';
export default {
- 'en-US': enUS
+ 'en': en, // Changed from 'en-US': enUS
+ 'de': de,
+ 'fr': fr,
+ 'es': es
};
diff --git a/fe/src/main.ts b/fe/src/main.ts
index 2a271cb..9c71ae5 100644
--- a/fe/src/main.ts
+++ b/fe/src/main.ts
@@ -4,8 +4,8 @@ import * as Sentry from '@sentry/vue';
import { BrowserTracing } from '@sentry/tracing';
import App from './App.vue';
import router from './router';
-// import { createI18n } from 'vue-i18n';
-// import messages from '@/i18n'; // Import from absolute path
+import { createI18n } from 'vue-i18n';
+import messages from '@/i18n';
// Global styles
import './assets/main.scss';
@@ -15,21 +15,22 @@ import { api, globalAxios } from '@/services/api'; // Renamed from boot/axios to
import { useAuthStore } from '@/stores/auth';
// Vue I18n setup (from your i18n boot file)
-// export type MessageLanguages = keyof typeof messages;
-// export type MessageSchema = (typeof messages)['en-US'];
+// // export type MessageLanguages = keyof typeof messages;
+// // export type MessageSchema = (typeof messages)['en-US'];
-// declare module 'vue-i18n' {
-// export interface DefineLocaleMessage extends MessageSchema {}
-// // eslint-disable-next-line @typescript-eslint/no-empty-object-type
-// export interface DefineDateTimeFormat {}
-// // eslint-disable-next-line @typescript-eslint/no-empty-object-type
-// export interface DefineNumberFormat {}
-// }
-// const i18n = createI18n<{ message: MessageSchema }>({
-// locale: 'en-US',
-// fallbackLocale: 'en-US',
-// messages,
-// });
+// // declare module 'vue-i18n' {
+// // export interface DefineLocaleMessage extends MessageSchema {}
+// // // eslint-disable-next-line @typescript-eslint/no-empty-object-type
+// // export interface DefineDateTimeFormat {}
+// // // eslint-disable-next-line @typescript-eslint/no-empty-object-type
+// // export interface DefineNumberFormat {}
+// // }
+const i18n = createI18n({
+ legacy: false, // Recommended for Vue 3
+ locale: 'en', // Default locale
+ fallbackLocale: 'en', // Fallback locale
+ messages,
+});
const app = createApp(App);
const pinia = createPinia();
@@ -62,7 +63,7 @@ if (authStore.accessToken) {
}
app.use(router);
-// app.use(i18n);
+app.use(i18n);
// Make API instance globally available (optional, prefer provide/inject or store)
app.config.globalProperties.$api = api;
diff --git a/fe/src/pages/AuthCallbackPage.vue b/fe/src/pages/AuthCallbackPage.vue
index 874fc12..81ed4d7 100644
--- a/fe/src/pages/AuthCallbackPage.vue
+++ b/fe/src/pages/AuthCallbackPage.vue
@@ -6,7 +6,7 @@
{{ error }}
- Redirecting...
+ {{ t('authCallbackPage.redirecting') }}
@@ -14,6 +14,7 @@
diff --git a/fe/src/pages/GroupDetailPage.vue b/fe/src/pages/GroupDetailPage.vue
index 1c9ca17..ac34505 100644
--- a/fe/src/pages/GroupDetailPage.vue
+++ b/fe/src/pages/GroupDetailPage.vue
@@ -1,11 +1,11 @@
-
+
- Retry
+ {{ t('groupDetailPage.retryButton') }}
@@ -14,42 +14,42 @@
- Group Members
+ {{ t('groupDetailPage.members.title') }}
{{ member.email }}
-
+
- Remove
+ {{ t('groupDetailPage.members.removeButton') }}
-
No members found.
+
{{ t('groupDetailPage.members.emptyState') }}
- Invite Members
+ {{ t('groupDetailPage.invites.title') }}
- {{ inviteCode ? 'Regenerate Invite Code' : 'Generate Invite Code' }}
+ {{ inviteCode ? t('groupDetailPage.invites.regenerateButton') : t('groupDetailPage.invites.generateButton') }}
-
+
-
+
- Invite code copied to clipboard!
+ {{ t('groupDetailPage.invites.copySuccess') }}
-
No active invite code. Click the button above to generate one.
+
{{ t('groupDetailPage.invites.emptyState') }}
@@ -63,9 +63,9 @@
- Group Chores
+ {{ t('groupDetailPage.chores.title') }}
- cleaning_services Manage Chores
+ cleaning_services {{ t('groupDetailPage.chores.manageButton') }}
@@ -73,14 +73,14 @@
{{ chore.name }}
- Due: {{ formatDate(chore.next_due_date) }}
+ {{ t('groupDetailPage.chores.duePrefix') }} {{ formatDate(chore.next_due_date) }}
{/* Assuming cleaning_services is a valid VIcon name or will be added */}
-
No chores scheduled. Click "Manage Chores" to create some!
+
{{ t('groupDetailPage.chores.emptyState') }}
@@ -88,9 +88,9 @@
- Group Expenses
+ {{ t('groupDetailPage.expenses.title') }}
- payments Manage Expenses
+ payments {{ t('groupDetailPage.expenses.manageButton') }}
@@ -108,18 +108,19 @@
{/* Assuming payments is a valid VIcon name or will be added */}
-
No expenses recorded. Click "Manage Expenses" to add some!
+
{{ t('groupDetailPage.expenses.emptyState') }}
-
+