diff --git a/android/app/build.gradle b/android/app/build.gradle index f45d9a0..4ae9f64 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.donetick.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 66 - versionName "1.2.45" + versionCode 68 + versionName "1.2.47" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/index.html b/index.html index 93cec0b..035f40f 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,28 @@ + + +
diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 784fc3e..dfae351 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -462,12 +462,12 @@ CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES; CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 66; + CURRENT_PROJECT_VERSION = 68; DEVELOPMENT_TEAM = 6UJJ78R3BS; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.2.45; + MARKETING_VERSION = 1.2.47; PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -485,12 +485,12 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; PROVISIONING_PROFILE_SPECIFIER = "Donetick App Store(fastline)"; - CURRENT_PROJECT_VERSION = 66; + CURRENT_PROJECT_VERSION = 68; DEVELOPMENT_TEAM = 6UJJ78R3BS; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 1.2.45; + MARKETING_VERSION = 1.2.47; PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; @@ -504,12 +504,12 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = DonetickWidget/DonetickWidget.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 66; + CURRENT_PROJECT_VERSION = 68; DEVELOPMENT_TEAM = 6UJJ78R3BS; INFOPLIST_FILE = DonetickWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 1.2.45; + MARKETING_VERSION = 1.2.47; PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app.widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -527,12 +527,12 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; PROVISIONING_PROFILE_SPECIFIER = "Donetick Widget App Store(fastline)"; - CURRENT_PROJECT_VERSION = 66; + CURRENT_PROJECT_VERSION = 68; DEVELOPMENT_TEAM = 6UJJ78R3BS; INFOPLIST_FILE = DonetickWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 1.2.45; + MARKETING_VERSION = 1.2.47; PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app.widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/package-lock.json b/package-lock.json index d692d3a..0415b44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,6 +64,7 @@ "js-cookie": "^3.0.5", "moment": "^2.30.1", "murmurhash": "^2.0.1", + "posthog-js": "^1.415.5", "prop-types": "^15.8.1", "qrcode": "^1.5.4", "quill": "^2.0.3", @@ -2322,8 +2323,6 @@ }, "node_modules/@emotion/cache": { "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", "license": "MIT", "dependencies": { "@emotion/memoize": "^0.9.0", @@ -4586,6 +4585,31 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@posthog/browser-common": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@posthog/browser-common/-/browser-common-0.5.0.tgz", + "integrity": "sha512-8DaxVZS1bQPbA514RePurLNbYjei3P4jhnC206DwVv5XThmZM3QdlsXenI2ujE3pLbgQ79hYn9o1Kda8I3WK/Q==", + "license": "MIT", + "dependencies": { + "@posthog/core": "^1.47.0", + "@posthog/types": "^1.402.2" + } + }, + "node_modules/@posthog/core": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.47.0.tgz", + "integrity": "sha512-LW62V+9yx7G7mLd+EYpwW0PiaPZI8XRJ1tLuhw+9fXHpugLp8XQD5JuCQ2kIXvoUfyx1DpKfGQY3dUnzMOIn7Q==", + "license": "MIT", + "dependencies": { + "@posthog/types": "^1.402.2" + } + }, + "node_modules/@posthog/types": { + "version": "1.402.3", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.402.3.tgz", + "integrity": "sha512-nnqKIGUqggeNbCZg6of/hYN+4shYZUoPFkILIIpYq0p9HDX8PgOrnrtBAUH8kr1MOmDh2nm+fY5Ceks7A5y6BQ==", + "license": "MIT" + }, "node_modules/@prettier/plugin-xml": { "version": "2.2.0", "dev": true, @@ -7391,6 +7415,20 @@ "version": "1.9.0", "license": "MIT" }, + "node_modules/core-js": { + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz", + "integrity": "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==", + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.45.1", "license": "MIT", @@ -7932,6 +7970,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz", + "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "dev": true, @@ -12018,6 +12065,48 @@ "dev": true, "license": "MIT" }, + "node_modules/posthog-js": { + "version": "1.415.5", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.415.5.tgz", + "integrity": "sha512-IUmPFtZjwDZYnRmttl+Xo5f/zDVdMdfWdZFxHqtAqq4wMwElr4dIpXmEUzh4nO8NtWT3zBmrL9+c7zZ3avzJZw==", + "license": "(Apache-2.0 AND MIT)", + "dependencies": { + "@posthog/browser-common": "^0.5.0", + "@posthog/core": "^1.47.0", + "@posthog/types": "^1.402.3", + "core-js": "^3.49.0", + "dompurify": "^3.4.13", + "fflate": "^0.4.8", + "preact": "^10.29.3", + "query-selector-shadow-dom": "^1.0.1", + "web-vitals": "^5.3.0", + "web-vitals-soft-navs": "npm:web-vitals@6.0.0" + } + }, + "node_modules/posthog-js/node_modules/fflate": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.9.tgz", + "integrity": "sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==", + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.29.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "devOptional": true, @@ -12402,6 +12491,12 @@ ], "license": "MIT" }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", + "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "dev": true, @@ -12466,15 +12561,6 @@ "regenerator-runtime": "^0.13.9" } }, - "node_modules/quilljs-markdown/node_modules/core-js": { - "version": "3.45.1", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/raf-schd": { "version": "4.0.3", "license": "MIT" @@ -15262,6 +15348,19 @@ "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", "license": "Apache-2.0" }, + "node_modules/web-vitals": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.3.0.tgz", + "integrity": "sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==", + "license": "Apache-2.0" + }, + "node_modules/web-vitals-soft-navs": { + "name": "web-vitals", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-6.0.0.tgz", + "integrity": "sha512-Guaibvy/+uNtL6Bsu4jmMJGzuSl91oeRH5iO9pPRbYftnFUr3yqT1TUNX/OE4o9HexuEMU3Kb/Wg7iKhlffZUA==", + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "license": "BSD-2-Clause" diff --git a/package.json b/package.json index 9c20283..f8db11a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "donetick", "private": true, - "version": "1.2.45", + "version": "1.2.47", "type": "module", "engines": { "node": ">=20.0.0", @@ -96,6 +96,7 @@ "js-cookie": "^3.0.5", "moment": "^2.30.1", "murmurhash": "^2.0.1", + "posthog-js": "^1.415.5", "prop-types": "^15.8.1", "qrcode": "^1.5.4", "quill": "^2.0.3", diff --git a/public/locales/ar/auth.json b/public/locales/ar/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/ar/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/ar/chores.json b/public/locales/ar/chores.json index 38887d1..ebd0674 100644 --- a/public/locales/ar/chores.json +++ b/public/locales/ar/chores.json @@ -71,5 +71,11 @@ "reset": "إعادة تعيين المؤقت", "delete": "حذف الجلسة" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/ar/common.json b/public/locales/ar/common.json index ef4ad0a..cae00e2 100644 --- a/public/locales/ar/common.json +++ b/public/locales/ar/common.json @@ -29,7 +29,8 @@ "filters": "الفلاتر", "activities": "الأنشطة", "points": "النقاط", - "settings": "الإعدادات" + "settings": "الإعدادات", + "reportBug": "Report a Bug" }, "search": { "title": "بحث", @@ -64,11 +65,7 @@ "footer": { "navigate": "تنقل", "open": "فتح", - "results_zero": "لا توجد نتائج", "results_one": "نتيجة واحدة", - "results_two": "نتيجتان", - "results_few": "{{count}} نتائج", - "results_many": "{{count}} نتيجة", "results_other": "{{count}} نتيجة", "typeToSearch": "اكتب للبحث" } @@ -118,5 +115,37 @@ "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", "open": "Open the issue" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/ar/filters.json b/public/locales/ar/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/ar/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/ar/history.json b/public/locales/ar/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/ar/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/ar/labels.json b/public/locales/ar/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/ar/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/ar/points.json b/public/locales/ar/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/ar/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/ar/projects.json b/public/locales/ar/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/ar/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/ar/settings.json b/public/locales/ar/settings.json index ea5a7c4..8b6f1a8 100644 --- a/public/locales/ar/settings.json +++ b/public/locales/ar/settings.json @@ -171,7 +171,7 @@ "description": "غيّر إعدادات حسابك أو نوع خطتك أو كلمة المرور", "accountType": "نوع الحساب: {{type}}", "free": "مجاني", - "plus": "Plus", + "plus": "بلس", "plusUntil": "Plus (حتى {{date}})", "activeDescription": "أنت مشترك حاليًا في خطة Plus. سيتجدد اشتراكك في {{date}}.", "cancelledDescription": "لقد ألغيت اشتراكك. سيتم خفض حسابك إلى الخطة المجانية في {{date}}.", @@ -490,5 +490,78 @@ "notConnected": "التحديثات الفورية مفعّلة لكن لا يوجد اتصال حاليًا.", "basicPlanNotice": "التحديثات الفورية غير متاحة في الخطة الأساسية. رقِّ إلى Plus لتلقّي إشعارات فورية عندما تنجز أنت أو أعضاء دائرتك المهام أو تتخطّونها أو تعدّلونها." } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/ar/things.json b/public/locales/ar/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/ar/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/ar/timer.json b/public/locales/ar/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/ar/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/de/auth.json b/public/locales/de/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/de/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/de/chores.json b/public/locales/de/chores.json index e47f49e..cba362c 100644 --- a/public/locales/de/chores.json +++ b/public/locales/de/chores.json @@ -71,5 +71,11 @@ "reset": "Timer zurücksetzen", "delete": "Sitzung löschen" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/de/common.json b/public/locales/de/common.json index 20c0c0a..da4073f 100644 --- a/public/locales/de/common.json +++ b/public/locales/de/common.json @@ -29,7 +29,8 @@ "filters": "Filter", "activities": "Aktivitäten", "points": "Punkte", - "settings": "Einstellungen" + "settings": "Einstellungen", + "reportBug": "Report a Bug" }, "search": { "title": "Suche", @@ -114,5 +115,37 @@ "subtitle": "Wir haben ein Problem mit Ihren Notizen und Versionsdetails vorausgefüllt. Es wurde noch nichts verschickt – überprüfe es und poste, sobald du bereit bist.", "open": "Eröffne das Problem" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/de/filters.json b/public/locales/de/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/de/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/de/history.json b/public/locales/de/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/de/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/de/labels.json b/public/locales/de/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/de/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/de/points.json b/public/locales/de/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/de/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/de/projects.json b/public/locales/de/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/de/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/de/settings.json b/public/locales/de/settings.json index 8717f88..bc674cf 100644 --- a/public/locales/de/settings.json +++ b/public/locales/de/settings.json @@ -490,5 +490,78 @@ "notConnected": "Echtzeit-Updates sind aktiviert, aber derzeit nicht verbunden.", "basicPlanNotice": "Echtzeit-Updates sind im Basis-Tarif nicht verfügbar. Wechsle zu Plus, um sofort benachrichtigt zu werden, wenn du oder andere Circle-Mitglieder Aufgaben erledigen, überspringen oder ändern." } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/de/things.json b/public/locales/de/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/de/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/de/timer.json b/public/locales/de/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/de/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/en/chores.json b/public/locales/en/chores.json index 72c8dc6..31db9aa 100644 --- a/public/locales/en/chores.json +++ b/public/locales/en/chores.json @@ -72,7 +72,17 @@ "delete": "Delete Session" }, "unableUndo": "Failed to undo", - "restoreFailed": "Failed to restore" + "offlineComplete": "You're offline — completion will sync when back online", + "unableComplete": "Unable to complete task", + "offlineSkip": "You're offline — skip will sync when back online", + "unableSkip": "Unable to skip task", + "offlineStart": "You're offline — start will sync when back online", + "unableStart": "Unable to start task", + "offlinePause": "You're offline — pause will sync when back online", + "unablePause": "Unable to pause task", + "offlineRestore": "You're offline — restore will sync when back online", + "restoreFailed": "Failed to restore", + "unableRestore": "Unable to restore task" }, "toolbar": { "defaultProject": "Default Project", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index fe1f4d4..6f55fb1 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -70,6 +70,13 @@ "typeToSearch": "Type to search" } }, + "policyUpdate": { + "title": "We've updated our Privacy Policy and Terms", + "subtitle": "Please take a moment to review them. By continuing to use Donetick, you accept the updated documents.", + "readPrivacy": "Privacy Policy", + "readTerms": "Terms", + "acknowledge": "Got it" + }, "feedback": { "later": "Maybe later", "sentiment": { diff --git a/public/locales/en/settings.json b/public/locales/en/settings.json index 5e9e6c8..ead40df 100644 --- a/public/locales/en/settings.json +++ b/public/locales/en/settings.json @@ -78,6 +78,10 @@ "title": "Advanced Settings", "description": "Configure webhooks, real-time updates, and other advanced features for enhanced productivity." }, + "privacy": { + "title": "Privacy & Analytics", + "description": "Control anonymous usage analytics and crash reporting." + }, "developer": { "title": "Developer Settings", "description": "View technical information about authentication tokens, SSE connections, and debug data." @@ -348,7 +352,8 @@ "backupCodesModal": { "title": "New Backup Codes", "warning": "Your previous backup codes are now invalid. Save these new codes in a safe place. Each code can only be used once." - } + }, + "generateCodes": "Generate New Codes" }, "apiTokens": { "title": "API Tokens", @@ -491,6 +496,15 @@ "basicPlanNotice": "Real-time updates are not available in the Basic plan. Upgrade to Plus to receive instant notifications when you or other circle members complete, skip, or modify tasks." } }, + "privacyAnalytics": { + "title": "Privacy & Analytics", + "description": "Donetick never sends task names, descriptions, notes, or other task content. Session recording is never enabled.", + "analyticsToggle": "Anonymous analytics", + "analyticsHelper": "Help improve Donetick by sharing anonymous usage statistics.", + "crashToggle": "Crash reports", + "crashHelper": "Send us the error and what led to it when Donetick crashes no task content.", + "footnote": "You can change these choices at any time." + }, "subscription": { "sessionFailed": "Failed to create subscription session", "errorTitle": "Subscription Error", diff --git a/public/locales/es/auth.json b/public/locales/es/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/es/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/es/chores.json b/public/locales/es/chores.json index 2af9159..68f93f6 100644 --- a/public/locales/es/chores.json +++ b/public/locales/es/chores.json @@ -71,5 +71,11 @@ "reset": "Reiniciar Temporizador", "delete": "Eliminar Sesión" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/es/common.json b/public/locales/es/common.json index b3e9f38..edfc97b 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -29,7 +29,8 @@ "filters": "Filtros", "activities": "Actividades", "points": "Puntos", - "settings": "Configuración" + "settings": "Configuración", + "reportBug": "Report a Bug" }, "search": { "title": "Buscar", @@ -114,5 +115,37 @@ "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", "open": "Open the issue" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/es/filters.json b/public/locales/es/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/es/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/es/history.json b/public/locales/es/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/es/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/es/labels.json b/public/locales/es/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/es/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/es/points.json b/public/locales/es/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/es/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/es/projects.json b/public/locales/es/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/es/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/es/settings.json b/public/locales/es/settings.json index 15583d7..1c9a6d4 100644 --- a/public/locales/es/settings.json +++ b/public/locales/es/settings.json @@ -490,5 +490,78 @@ "notConnected": "Las actualizaciones en tiempo real están activadas pero ahora mismo no hay conexión.", "basicPlanNotice": "Las actualizaciones en tiempo real no están disponibles en el plan Básico. Mejora a Plus para recibir notificaciones instantáneas cuando tú u otros miembros del círculo completéis, omitáis o modifiquéis tareas." } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/es/things.json b/public/locales/es/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/es/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/es/timer.json b/public/locales/es/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/es/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/fr/auth.json b/public/locales/fr/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/fr/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/fr/chores.json b/public/locales/fr/chores.json index 38a6911..f73ba4e 100644 --- a/public/locales/fr/chores.json +++ b/public/locales/fr/chores.json @@ -71,5 +71,11 @@ "reset": "Réinitialiser le Minuteur", "delete": "Supprimer la Session" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index d8c05b0..4a40f04 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -29,7 +29,8 @@ "filters": "Filtres", "activities": "Activités", "points": "Points", - "settings": "Paramètres" + "settings": "Paramètres", + "reportBug": "Report a Bug" }, "search": { "title": "Recherche", @@ -44,7 +45,7 @@ }, "groups": { "tasks": "Tâches", - "history": "Notes", + "history": "Remarques", "projects": "Projets", "labels": "Étiquettes", "people": "Personnes", @@ -70,49 +71,81 @@ } }, "feedback": { - "later": "Maybe later", + "later": "Peut-être plus tard", "sentiment": { "title": "How's Donetick working for you?", "subtitle": "Votre réponse nous aide à décider de ce qu'il faut construire ensuite.", "options": { - "love": "Love it", - "okay": "It's okay", - "issues": "Having issues" + "love": "J'adore", + "okay": "C'est ok", + "issues": "Problèmes rencontrés" } }, "categories": { - "bugs": "Bugs", - "missingFeature": "Missing feature", - "tooComplicated": "Too complicated", - "slow": "Slow", + "bugs": "Anomalies", + "missingFeature": "Fonctionnalités manquantes", + "tooComplicated": "Trop compliqué", + "slow": "Lent", "notifications": "Notifications", - "ai": "AI", - "other": "Other" + "ai": "IA", + "other": "Autres" }, "details": { - "title": "What could we improve?", - "messageLabel": "Tell us more", + "title": "Que pouvons-nous améliorer ?", + "messageLabel": "Dites-nous-en plus", "messagePlaceholder": "What happened, or what would make this better?", "contextNote": "We'll include your app version, device and platform so we can reproduce issues.", - "submit": "Send feedback", - "contextNoteSelfHosted": "You're on a self-hosted instance, so nothing is sent from your server — we'll open a pre-filled GitHub issue you can review and edit first.", - "submitSelfHosted": "Continue to GitHub" + "submit": "Envoyer vos remarques", + "contextNoteSelfHosted": "Vous êtes sur une instance autohébergée, donc rien n'est envoyé depuis votre serveur — nous allons ouvrir un problème GitHub prérempli que vous pouvez examiner et modifier si besoin.", + "submitSelfHosted": "Continuer sur GitHub" }, "review": { - "title": "Glad you're enjoying it!", + "title": "Nous sommes ravis que vous l'appréciez !", "subtitle": "A rating or a star helps other people find Donetick.", - "github": "Star on GitHub", - "appStore": "Rate on the App Store", - "playStore": "Rate on Google Play" + "github": "Mettez une étoile sur GitHub", + "appStore": "Évaluer sur l'App Store", + "playStore": "Évaluer sur Google Play" }, "thanks": { - "title": "Thanks for the feedback", + "title": "Merci pour votre retour", "subtitle": "We read every response and it shapes what we work on next." }, "github": { - "title": "Report it on GitHub", + "title": "Signaler sur GitHub", "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", - "open": "Open the issue" + "open": "Ouvrir un ticket" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/fr/filters.json b/public/locales/fr/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/fr/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/fr/history.json b/public/locales/fr/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/fr/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/fr/labels.json b/public/locales/fr/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/fr/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/fr/points.json b/public/locales/fr/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/fr/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/fr/projects.json b/public/locales/fr/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/fr/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/fr/settings.json b/public/locales/fr/settings.json index 64e90a5..4f8968f 100644 --- a/public/locales/fr/settings.json +++ b/public/locales/fr/settings.json @@ -490,5 +490,78 @@ "notConnected": "Les mises à jour en temps réel sont activées mais non connectées actuellement.", "basicPlanNotice": "Les mises à jour en temps réel ne sont pas disponibles dans la formule de base. Passez à Plus pour recevoir des notifications instantanées lorsque vous ou d'autres membres du cercle terminez, passez ou modifiez des tâches." } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/fr/things.json b/public/locales/fr/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/fr/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/fr/timer.json b/public/locales/fr/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/fr/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/ja/auth.json b/public/locales/ja/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/ja/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/ja/chores.json b/public/locales/ja/chores.json index cb1dac3..a9c5c99 100644 --- a/public/locales/ja/chores.json +++ b/public/locales/ja/chores.json @@ -71,5 +71,11 @@ "reset": "タイマーをリセット", "delete": "セッションを削除" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index 0fb166f..06cae8c 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -29,7 +29,8 @@ "filters": "フィルター", "activities": "アクティビティ", "points": "ポイント", - "settings": "設定" + "settings": "設定", + "reportBug": "Report a Bug" }, "search": { "title": "検索", @@ -64,6 +65,7 @@ "footer": { "navigate": "移動", "open": "開く", + "results_one": "{{count}} result", "results_other": "{{count}} 件の結果", "typeToSearch": "入力して検索" } @@ -113,5 +115,37 @@ "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", "open": "Open the issue" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/ja/filters.json b/public/locales/ja/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/ja/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/ja/history.json b/public/locales/ja/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/ja/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/ja/labels.json b/public/locales/ja/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/ja/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/ja/points.json b/public/locales/ja/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/ja/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/ja/projects.json b/public/locales/ja/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/ja/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/ja/settings.json b/public/locales/ja/settings.json index dc433ab..3199f0f 100644 --- a/public/locales/ja/settings.json +++ b/public/locales/ja/settings.json @@ -490,5 +490,78 @@ "notConnected": "リアルタイム更新は有効ですが、現在接続されていません。", "basicPlanNotice": "リアルタイム更新はベーシックプランではご利用いただけません。Plusにアップグレードすると、あなたや他のサークルメンバーがタスクを完了・スキップ・変更したときにすぐ通知を受け取れます。" } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/ja/things.json b/public/locales/ja/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/ja/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/ja/timer.json b/public/locales/ja/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/ja/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/nl/auth.json b/public/locales/nl/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/nl/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/nl/chores.json b/public/locales/nl/chores.json index 04569df..0bb8f25 100644 --- a/public/locales/nl/chores.json +++ b/public/locales/nl/chores.json @@ -71,5 +71,11 @@ "reset": "Timer Resetten", "delete": "Sessie Verwijderen" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/nl/common.json b/public/locales/nl/common.json index 5da32d4..bc5fb3f 100644 --- a/public/locales/nl/common.json +++ b/public/locales/nl/common.json @@ -29,7 +29,8 @@ "filters": "Filters", "activities": "Activiteiten", "points": "Punten", - "settings": "Instellingen" + "settings": "Instellingen", + "reportBug": "Report a Bug" }, "search": { "title": "Zoeken", @@ -114,5 +115,37 @@ "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", "open": "Open the issue" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/nl/filters.json b/public/locales/nl/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/nl/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/nl/history.json b/public/locales/nl/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/nl/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/nl/labels.json b/public/locales/nl/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/nl/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/nl/points.json b/public/locales/nl/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/nl/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/nl/projects.json b/public/locales/nl/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/nl/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/nl/settings.json b/public/locales/nl/settings.json index a188d42..00720e6 100644 --- a/public/locales/nl/settings.json +++ b/public/locales/nl/settings.json @@ -490,5 +490,78 @@ "notConnected": "Realtime-updates staan aan maar er is momenteel geen verbinding.", "basicPlanNotice": "Realtime-updates zijn niet beschikbaar in het basisabonnement. Upgrade naar Plus om direct meldingen te ontvangen wanneer jij of andere circle-leden taken afronden, overslaan of wijzigen." } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/nl/things.json b/public/locales/nl/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/nl/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/nl/timer.json b/public/locales/nl/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/nl/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/pt/auth.json b/public/locales/pt/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/pt/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/pt/chores.json b/public/locales/pt/chores.json index 7e4df50..52f8fbf 100644 --- a/public/locales/pt/chores.json +++ b/public/locales/pt/chores.json @@ -71,5 +71,11 @@ "reset": "Reiniciar cronômetro", "delete": "Excluir sessão" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json index dd2514a..15a71ad 100644 --- a/public/locales/pt/common.json +++ b/public/locales/pt/common.json @@ -29,7 +29,8 @@ "filters": "Filtros", "activities": "Atividades", "points": "Pontos", - "settings": "Configurações" + "settings": "Configurações", + "reportBug": "Report a Bug" }, "search": { "title": "Pesquisar", @@ -114,5 +115,37 @@ "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", "open": "Open the issue" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/pt/filters.json b/public/locales/pt/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/pt/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/pt/history.json b/public/locales/pt/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/pt/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/pt/labels.json b/public/locales/pt/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/pt/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/pt/points.json b/public/locales/pt/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/pt/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/pt/projects.json b/public/locales/pt/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/pt/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/pt/settings.json b/public/locales/pt/settings.json index b12cfee..e27322f 100644 --- a/public/locales/pt/settings.json +++ b/public/locales/pt/settings.json @@ -490,5 +490,78 @@ "notConnected": "As atualizações em tempo real estão ativadas, mas não há conexão no momento.", "basicPlanNotice": "As atualizações em tempo real não estão disponíveis no plano Básico. Mude para o Plus para receber notificações instantâneas quando você ou outros membros do círculo concluírem, pularem ou modificarem tarefas." } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/pt/things.json b/public/locales/pt/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/pt/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/pt/timer.json b/public/locales/pt/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/pt/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/public/locales/zh/auth.json b/public/locales/zh/auth.json new file mode 100644 index 0000000..28cdec1 --- /dev/null +++ b/public/locales/zh/auth.json @@ -0,0 +1,111 @@ +{ + "validationError": "Validation Error", + "primaryUsernameRequired": "Primary username is required for sub account login", + "subNameRequired": "Sub account name is required for sub account login", + "usernameRequired": "Username is required", + "passwordRequired": "Password is required", + "loginFailed": "Login Failed", + "genericError": "An error occurred, please try again", + "providerLoginFailed": "{{provider}} Login Failed", + "providerLoginFailedMsg": "Couldn't log in with {{provider}}, please try again", + "providerLoginError": "{{provider}} Login Error", + "networkError": "Network error occurred, please try again", + "mfaFailed": "Two-Factor Authentication Failed", + "oauthError": "OAuth Error", + "oauthBrowserFailed": "Failed to open authentication browser", + "primaryAccount": "Primary Account", + "subAccount": "Sub Account", + "username": "Username", + "primaryUsernamePlaceholder": "Enter primary account username", + "subNamePlaceholder": "Enter sub account name", + "forgotPassword": "Forgot password?", + "or": "or", + "email": "Email", + "displayNamePlaceholder": "How others see your name", + "signupFailed": "Signup Failed", + "signupDisabled": "Signup disabled, please contact admin", + "usernameMinLength": "Username must be at least 4 characters", + "invalidEmail": "Invalid email address", + "passwordLength": "Password must be between 8 and 64 characters", + "displayNameRequired": "Display name is required", + "displayNameChars": "Display name can only contain letters and numbers", + "resetEmailSent": "Reset Email Sent", + "resetEmailSentMsg": "Check your email for password reset instructions", + "resetFailed": "Reset Failed", + "resetFailedMsg": "Failed to send reset email, please try again later", + "emailPlaceholder": "you@example.com", + "passwordUpdated": "Password Updated", + "passwordUpdatedMsg": "Your password has been updated successfully. Redirecting to login...", + "passwordUpdateFailed": "Password Update Failed", + "passwordUpdateFailedMsg": "Failed to update password, please try again later", + "savePassword": "Save password", + "server": { + "url": "Server URL", + "dnsFailed": "Hostname could not be resolved. Check the URL for typos or verify DNS.", + "unreachable": "Unable to reach the server. Check the URL, port, and network connection.", + "invalidUrl": "Invalid URL format. Include the protocol (http:// or https://) and port if needed." + }, + "mfaModal": { + "title": "Two-factor authentication", + "codeRequired": "Please enter a verification code", + "verifyFailed": "Failed to verify code. Please try again.", + "backupHint": "Enter one of the backup codes you saved when setting up two-factor authentication.", + "codeHint": "Enter the 6-digit code from your authenticator app.", + "verify": "Verify & Sign In", + "backupLabel": "Backup code", + "codeLabel": "Verification code", + "backupPlaceholder": "Enter backup code", + "useAuthenticator": "Use authenticator app instead", + "useBackup": "Use a backup code instead", + "backupOnce": "Each backup code can only be used once.", + "invalidCode": "Invalid verification code. Please try again." + }, + "appleLoginFailed": "Apple Login Failed", + "googleLoginFailed": "Google Login Failed", + "createOne": "Create one", + "primaryAccountUsernameLabel": "Primary account username", + "subAccountNameLabel": "Sub account name", + "serverSettings": "Server settings", + "useDifferentAccount": "Use a different account", + "yourUsername": "Your username", + "backToSignIn": "Back to sign in", + "confirmNewPassword": "Confirm new password", + "newPassword": "New password", + "reenterPassword": "Re-enter your password", + "requestNewLink": "Request a new link", + "setNewPassword": "Set a new password", + "linkInvalid": "This link is not valid", + "createAccountButton": "Create account", + "createYourAccount": "Create your account", + "displayNameLabel": "Display name", + "usernameHint": "lowercase letters, numbers, dot and dash", + "termsShort": "By creating an account you agree to our Terms of Service and Privacy", + "checkYourEmail": "Check your email", + "resetYourPassword": "Reset your password", + "serverChangeWarning": "Changing the server clears locally cached data on this device.", + "serverConnected": "Connected. Taking you to sign in...", + "passwordLabel": "Password", + "showPassword": "Show password", + "hidePassword": "Hide password", + "loginPasswordPlaceholder": "Enter your password", + "signupPasswordPlaceholder": "At least 8 characters", + "signupPasswordHelper": "Use 8 to 64 characters.", + "orContinueWith": "or continue with", + "signupSignInFailed": "Your account was created, but signing in failed. Please sign in.", + "authenticating": { + "signingIn": "Signing you in", + "signingInSub": "This will only take a moment.", + "unknownProvider": "Unknown sign-in provider", + "contactSupport": "Please contact support.", + "signInFailed": "Sign-in failed", + "mfaCancelled": "Two-factor authentication was cancelled.", + "requestNotVerified": "The sign-in request could not be verified.", + "tryAgain": "Please try again.", + "mfaSessionMissing": "The MFA session is missing. Please try again.", + "twoFactor": "Two-factor authentication", + "verifyToContinue": "Verify your login to continue.", + "noToken": "No valid authentication token was returned.", + "backToSignIn": "Back to sign in", + "mfaFailed": "Two-factor authentication failed. Please try again." + } +} diff --git a/public/locales/zh/chores.json b/public/locales/zh/chores.json index ec82037..50b151f 100644 --- a/public/locales/zh/chores.json +++ b/public/locales/zh/chores.json @@ -71,5 +71,11 @@ "reset": "Reset Timer", "delete": "Delete Session" } + }, + "toolbar": { + "defaultProject": "Default Project" + }, + "sort": { + "assignedToMe": "Assigned to me" } } diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 258c263..aa1a1d5 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -29,7 +29,8 @@ "filters": "Filters", "activities": "Activities", "points": "Points", - "settings": "Settings" + "settings": "Settings", + "reportBug": "Report a Bug" }, "search": { "title": "搜索", @@ -114,5 +115,37 @@ "subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.", "open": "Open the issue" } + }, + "done": "Done", + "clearAll": "Clear all", + "upload": { + "quotaTitle": "Storage Quota Exceeded", + "quotaMessage": "You have exceeded your quota for uploading files.", + "tooLargeTitle": "File Too Large", + "tooLargeMessage": "The file you are trying to upload is too large.", + "deniedTitle": "Permission Denied", + "deniedMessage": "You do not have permission to upload files." + }, + "getStarted": "Get Started!", + "imageLoadFailed": "Failed to load image.", + "typeHere": "Type in here…", + "summaryOfChores": "This is a summary of your chores", + "loadingOffline": "You are offline", + "loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.", + "loadingSlow": "This is taking longer than usual. There might be an issue.", + "navigateBack": "Navigate Back", + "bottomNav": "Bottom Navigation", + "profile": "Profile", + "autocompletePlaceholder": "Type here...", + "errorScreen": { + "title": "Something went wrong", + "fallback": "An unexpected error occurred. Try reloading — it usually fixes it.", + "tryAgain": "Try again", + "home": "Home", + "login": "Login", + "hideDetails": "Hide error details", + "showDetails": "Show error details", + "copyToClipboard": "Copy to clipboard", + "copied": "Error details copied to clipboard" } } diff --git a/public/locales/zh/filters.json b/public/locales/zh/filters.json new file mode 100644 index 0000000..6b10047 --- /dev/null +++ b/public/locales/zh/filters.json @@ -0,0 +1,17 @@ +{ + "builder": { + "errorName": "Please enter a filter name", + "errorConditions": "Please configure at least one filter condition", + "editTitle": "Edit Filter", + "newTitle": "New Filter", + "addConditions": "Add conditions to preview", + "save": "Save Filter", + "name": "Filter Name", + "namePlaceholder": "e.g. Overdue tasks for Alice", + "color": "Color" + }, + "nameExists": "A filter with this name already exists", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "overdue": "{{count}} overdue" +} diff --git a/public/locales/zh/history.json b/public/locales/zh/history.json new file mode 100644 index 0000000..1452849 --- /dev/null +++ b/public/locales/zh/history.json @@ -0,0 +1,89 @@ +{ + "edit": { + "title": "Edit History", + "completedDate": "Completed Date", + "note": "Note", + "additionalNotes": "Additional Notes", + "deleteTitle": "Delete History", + "deleteMessage": "Are you sure you want to delete this history?" + }, + "status": { + "inProgress": "In Progress", + "completed": "Completed", + "skipped": "Skipped", + "pendingApproval": "Pending Approval", + "rejected": "Rejected", + "missed": "Missed", + "rescheduled": "Rescheduled", + "scheduled": "Scheduled" + }, + "badge": { + "onTime": "On Time", + "onTimeLabel": "On time" + }, + "detail": { + "title": "Activity Detail", + "openTask": "Open Task", + "editEntry": "Edit Entry", + "performedBy": "Performed by", + "assignedTo": "Assigned to", + "lastUpdated": "Last updated", + "duration": "Duration", + "pointsEarned": "Points earned", + "points_one": "{{count}} pt", + "points_other": "{{count}} pts", + "note": "Note" + }, + "charts": { + "noData": "No data available", + "noLabels": "No Labels", + "unassigned": "Unassigned", + "unknownTask": "Unknown Task", + "late": "Late", + "status": { + "title": "Status", + "description": "Completed tasks status" + }, + "due": { + "title": "Due Date", + "description": "Current tasks due date" + }, + "priority": { + "title": "Priority", + "description": "Tasks by priority" + }, + "labels": { + "title": "Labels", + "description": "Tasks by labels" + }, + "labelsDuration": { + "title": "Labels (time)", + "description": "Time spent by labels (hours)" + }, + "tasksTime": { + "title": "Tasks (time)", + "description": "Time spent by individual tasks (hours)" + }, + "assigneeBreakdown": { + "title": "by Assignee", + "description": "Tasks grouped by assignee" + } + }, + "filter": { + "status": "Status", + "pending": "Pending", + "labels": "Labels", + "hasNotes": "Has Notes", + "hasPoints": "Has Points", + "timePeriod": "Time Period", + "user": "User" + }, + "period": { + "days_one": "{{count}} Day", + "days_other": "{{count}} Days", + "allTime": "All Time" + }, + "activities": { + "title": "Activities" + } +} diff --git a/public/locales/zh/labels.json b/public/locales/zh/labels.json new file mode 100644 index 0000000..93f96cb --- /dev/null +++ b/public/locales/zh/labels.json @@ -0,0 +1,9 @@ +{ + "shared": "Shared", + "delete": { + "title": "Delete Label", + "message": "Are you sure you want to delete this label? This will remove the label from all tasks." + }, + "loadError": "Failed to load labels. Please try again.", + "blurb": "Manage your labels and organize your tasks effectively. Labels will be automatically shared with your circle if they are used on a shared task." +} diff --git a/public/locales/zh/points.json b/public/locales/zh/points.json new file mode 100644 index 0000000..a6ccd40 --- /dev/null +++ b/public/locales/zh/points.json @@ -0,0 +1,74 @@ +{ + "redeem": "Redeem Points", + "redeemModal": { + "redeemButton": "Redeem", + "pointsAvailable": "{{count}} points available", + "pointsToRedeem": "Points to Redeem", + "placeholder": "Enter points...", + "cannotExceed": "Cannot exceed available points", + "quickAdd": "Quick Add:", + "aboutToRedeem": "You are about to redeem", + "amount": "{{count}} points", + "remaining": "Remaining: {{count}} points" + }, + "leaderboard": { + "titlePoints": "Points Leaderboard", + "titleTasks": "Tasks Leaderboard", + "subtitlePoints": "Rankings based on points earned during the selected time period", + "subtitleTasks": "Rankings based on tasks completed during the selected time period", + "modePoints": "Points", + "modeTasks": "Tasks", + "you": "You", + "tasksAndAvg": "{{tasks}} tasks • {{avg}} avg per task", + "available": "{{count}} available", + "points_one": "{{count}} point", + "points_other": "{{count}} points" + }, + "tabs": { + "short7d": "7D", + "short6m": "6M", + "shortAll": "All", + "days7": "7 Days", + "months6": "6 Months", + "allTime": "All Time" + }, + "filter": { + "analysisTitle": "Filter & Analysis", + "title": "Filter Points", + "showFor": "Show points for:", + "timePeriod": "Time period:" + }, + "cards": { + "available": { + "title": "Available", + "subtext": "Ready to redeem" + }, + "redeemed": { + "title": "Redeemed", + "subtext": "Previously used" + }, + "total": { + "title": "Total", + "subtext": "All time earned" + }, + "period": { + "title": "Period Points", + "allTime": "All time", + "last6Months": "Last 6 months", + "lastDays_one": "Last {{count}} day", + "lastDays_other": "Last {{count}} days" + } + }, + "summary": { + "showingFor": "Showing points for", + "unknownUser": "Unknown User", + "overThe": "over the", + "allTime": "All Time", + "last6Months": "Last 6 Months", + "lastDays_one": "Last {{count}} Day", + "lastDays_other": "Last {{count}} Days" + }, + "trend": { + "title": "Points Trend" + } +} diff --git a/public/locales/zh/projects.json b/public/locales/zh/projects.json new file mode 100644 index 0000000..0e7eed4 --- /dev/null +++ b/public/locales/zh/projects.json @@ -0,0 +1,24 @@ +{ + "iconPicker": { + "chooseIcon": "Choose Project Icon", + "availableIcons": "Available Icons" + }, + "defaultChip": "Default", + "tasks_one": "{{count}} task", + "tasks_other": "{{count}} tasks", + "shared": "Shared", + "delete": { + "title": "Delete Project", + "message": "Are you sure you want to delete \"{{name}}\"? This will remove the project but keep all tasks (they'll move to the Default Project)." + }, + "loadError": "Failed to load projects. Please try again.", + "blurb": "Organize your tasks into projects. Create custom workspaces to keep your tasks organized and easily accessible.", + "defaultDescription": "All tasks without a specific project", + "selector": { + "title": "Projects", + "createNew": "Create New Project", + "createNewDescription": "Add a custom project workspace", + "manage": "Manage Projects", + "manageDescription": "View, edit, and organize all projects" + } +} diff --git a/public/locales/zh/settings.json b/public/locales/zh/settings.json index c342420..8e9c3a9 100644 --- a/public/locales/zh/settings.json +++ b/public/locales/zh/settings.json @@ -490,5 +490,78 @@ "notConnected": "实时更新已开启,但当前未连接。", "basicPlanNotice": "基础套餐不支持实时更新。升级到 Plus,当你或其他圈子成员完成、跳过或修改任务时即可立即收到通知。" } + }, + "subscription": { + "sessionFailed": "Failed to create subscription session", + "errorTitle": "Subscription Error", + "errorMessage": "Failed to start subscription process. Please try again.", + "subscribe": "Subscribe", + "cancelAnytime": "Cancel anytime. No hidden fees. Secure payment powered by Stripe." + }, + "childUsers": { + "errNameRequired": "Sub account name is required", + "errNameMin": "Sub account name must be at least 2 characters", + "errNameMax": "Sub account name must be less than 20 characters", + "errNameChars": "Sub account name can only contain lowercase letters, dot and dash", + "errPasswordRequired": "Password is required", + "errPasswordLength": "Password must be between 8 and 64 characters", + "errPasswordMatch": "Passwords do not match", + "errDisplayNameMax": "Display name must be less than 50 characters", + "createTitle": "Create Sub Account", + "createButton": "Create Account", + "nameLabel": "Sub Account Name *", + "namePlaceholder": "Enter sub account name (e.g., sarah)", + "displayNameLabel": "Display Name", + "displayNamePlaceholder": "Display name (optional, defaults to sub account name)", + "passwordLabel": "Password *", + "passwordPlaceholder": "Enter password (8-64 characters)", + "confirmPasswordLabel": "Confirm Password *", + "confirmPasswordPlaceholder": "Confirm password" + }, + "passwordChange": { + "minLength": "Password must be at least 8 characters", + "maxLength": "Password must be less than 64 characters" + }, + "payment": { + "cancelledTitle": "Payment has been cancelled", + "cancelledRedirect": "You will be redirected to the main page shortly.", + "successTitle": "Payment Successful!", + "successRedirect": "You will be redirected to the settings page shortly." + }, + "developer": { + "tokenRefreshed": "Token refreshed successfully", + "endpointCalled": "Refresh token endpoint called successfully", + "clearDbCleared": "Local offline database cleared. Starting full sync...", + "clearDbDone": "Full sync completed from the beginning", + "intro": "View technical information about your authentication tokens and session", + "refreshCookieNote": "Refresh tokens are managed via HTTP-only cookies on web platform", + "noReconnectInfo": "No reconnection information available", + "noTimeoutInfo": "No timeout information available", + "noDebugInfo": "No debug information available" + }, + "backup": { + "keyRequired": "Encryption key is required", + "created": "Backup created and downloaded successfully", + "createFailed": "Failed to create backup", + "selectFile": "Please select a backup file", + "restored": "Backup restored successfully. Please refresh the page.", + "restoreFailed": "Failed to restore backup", + "readFailed": "Failed to read backup file", + "createIntro": "Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files.", + "keyPlaceholder": "Enter a strong encryption key", + "includeAssets": "Include uploaded files and assets", + "warningLabel": "Warning:", + "restoreWarning": "Restoring a backup will replace all your current data. This action cannot be undone.", + "restoreKeyPlaceholder": "Enter the encryption key used for this backup" + }, + "deletion": { + "passwordRequired": "Please enter your password to continue", + "passwordAndDelete": "Please enter your password and type DELETE to confirm", + "failed": "Failed to delete account", + "passwordPlaceholder": "Enter your password", + "transferIntro": "You own circles that require ownership transfer before deletion. Please select new owners:", + "selectOwner": "Select new owner", + "confirmPrompt": "Please enter your password and type DELETE to confirm", + "typeDelete": "DELETE" } } diff --git a/public/locales/zh/things.json b/public/locales/zh/things.json new file mode 100644 index 0000000..1181eb5 --- /dev/null +++ b/public/locales/zh/things.json @@ -0,0 +1,18 @@ +{ + "errName": "Name is required", + "errStateNumber": "State must be a number", + "errStateBool": "State must be true or false", + "errStateRequired": "State is required", + "name": "Name", + "namePlaceholder": "Thing name", + "value": "Value", + "valuePlaceholder": "Thing value", + "updateState": "Update state", + "update": "Update", + "history": { + "overview": "Things Overview", + "visualization": "Data Visualization", + "changeHistory": "Change History", + "updated": "Updated" + } +} diff --git a/public/locales/zh/timer.json b/public/locales/zh/timer.json new file mode 100644 index 0000000..848fd53 --- /dev/null +++ b/public/locales/zh/timer.json @@ -0,0 +1,29 @@ +{ + "toast": { + "sessionUpdatedTitle": "Session updated", + "sessionUpdatedMessage": "Timer session has been updated successfully.", + "sessionUpdateFailTitle": "Failed to update session", + "tryAgain": "Please try again.", + "sessionUpdateErrorTitle": "Error updating session", + "sessionDeletedTitle": "Session deleted", + "sessionDeletedMessage": "Timer session has been deleted successfully.", + "sessionDeleteErrorTitle": "Error deleting session", + "deleteConfirmTitle": "Delete Timer Session", + "deleteConfirmMessage": "Are you sure you want to delete this timer session?" + }, + "loading": "Loading timer data...", + "noData": "No timer data found for this chore.", + "activeWork": "Active Work", + "breakTime": "Break Time", + "workSessions": "Work Sessions", + "totalTime": "Total Time", + "distribution": "Work vs Break Distribution", + "noActiveTime": "No active time yet", + "sessionBreakdown": "Session Breakdown", + "sessions": "Sessions", + "addSession": "Add Session", + "startTime": "Start Time", + "endTime": "End Time", + "leaveEmpty": "Leave empty if session is ongoing", + "noSessionForChore": "No timer session found for this chore." +} diff --git a/src/App.jsx b/src/App.jsx index b1a275e..43ec306 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,11 @@ import { useRegisterSW } from 'virtual:pwa-register/react' import NavBar from '@/views/components/NavBar' +import { + initialize as initializeAnalytics, + installGlobalErrorHandlers, +} from './analytics' +import useAnalyticsIdentity from './analytics/useAnalyticsIdentity' import { registerCapacitorListeners } from './CapacitorListener' import PageTransition from './components/animations/PageTransition' import { ImpersonateUserProvider } from './contexts/ImpersonateUserContext' @@ -36,6 +41,7 @@ const AppContent = () => { const { showNotification } = useNotification() const location = useLocation() useSyncOnReconnect() + useAnalyticsIdentity() // Every route renders through this Outlet, so one listener here gives crash // reports the trail that led to the failure. @@ -142,6 +148,11 @@ function App() { registerCapacitorListeners(navigate) }, [navigate]) + useEffect(() => { + initializeAnalytics() + installGlobalErrorHandlers() + }, []) + return (- Favoro LLC ("we," "us," or "our") operates the Donetick application and - website (collectively, the "Service"). This Privacy Policy informs you - of our policies regarding the collection, use, and disclosure of - personal data when you use our Service and the choices you have - associated with that data. -
-- Personal Data: When you register for an account or use - the Service, we may collect certain personally identifiable information, - such as your name and email address. + How Donetick handles your data
- Usage Data: We collect information on how you use the - Service, such as your IP address, browser type, pages visited, and the - time and date of your visit. + Last updated August 12, 2026 +
+ ++ Donetick is made by Favoro LLC ("we", "us", "our"). This policy covers + the Donetick Cloud Service and our website. If you run Donetick + yourself, your data lives on your own server and you control it. Only + the analytics section below applies, and only if you opt in. +
+ ++ • Account info: your email, your name, and the circle + (household or group) you belong to.
- Task Data: We store the tasks and chores you create - within the app, including their details and any assigned users. -
-- Provide and Maintain the Service: We use your - information to operate, maintain, and improve the Service. + • Your content: the tasks, chores, labels, notes, and + schedules you create.
- Communicate with You: We may use your email address to - send you notifications, updates, and promotional materials related to - the Service. + • Usage data: basic, privacy-respecting information + about how the app is used so we can keep it working and improve it.
- Analyze Usage: We analyze usage data to understand how - the Service is used and to make improvements. + • Billing: if you upgrade, payment is handled by + Stripe. We never see or store your full card details.
-- With Your Consent: We will not share your personal data - with third parties without your consent, except as described in this - Privacy Policy. + To run the core product: store and sync your tasks across your devices, + remind you when things are due, share chores with your circle, and + provide support. We use your content to operate the Service for you, not + to build advertising profiles, and never to train AI models. +
+ ++ We use{' '} + + PostHog + {' '} + to see which features are used and to catch crashes. Every event is + checked against a fixed list of allowed properties before it is sent, so + only this can leave your device: feature usage (for example, that a task + was created and whether it had a due date), technical details about your + installation (platform, OS version, app version, Cloud or self-hosted), + whether your account is on a paid plan and how many people are in your + circle, and, for errors, status codes and stack traces.
- Service Providers: We may engage third-party companies - or individuals to perform services on our behalf (e.g., hosting, - analytics). These third parties have access to your personal data only - to perform these tasks and are obligated not to disclose or use it for - any other purpose. + We never send the content of your tasks, chores, notes, search queries, + circle names, or label names. We don't use session recording or + automatic click capture.
- Compliance with Law: We may disclose your personal data - if required to do so by law or in response to valid requests by public - authorities (e.g., a court or government agency). + For our public website we also use{' '} + + Plausible + + , a privacy-friendly analytics tool that counts page views without + cookies, without cross-site tracking, and without building a profile of + you.
-- We value your privacy and have implemented reasonable security measures - to protect your personal data from unauthorized access, disclosure, - alteration, or destruction. However, no method of transmission over the - Internet or electronic storage is 100% secure, and we cannot guarantee - absolute security. + Product analytics and crash reporting are two separate switches under{' '} + Settings → Privacy & Analytics. On the Cloud + Service they're on by default and you can turn them off at any time. On + self-hosted installations they're off by default and require you to opt + in. Turning one off stops new data of that type immediately.
-- Account Information: You can update or correct your - account information at any time. + Cloud Service data is stored on our hosted infrastructure, scoped so + that only you and the circle you share with can access it. Files you + upload, like task photos and attachments, are kept in secure cloud + object storage provided by Cloudflare, and are served over private, + expiring links rather than public URLs. We use reasonable safeguards to + protect all of it, though no system on the internet is perfectly secure.
+ +- Marketing Communications: You can opt out of receiving - promotional emails by following the unsubscribe instructions included in - those emails. + We don't sell your data. We share it only with the service providers + that make Donetick work (Cloudflare for hosting and file storage, + PostHog and Plausible for analytics, Stripe for payments), or when + required by law.
-- Our Service is not intended for children under 13 years of age. We do - not knowingly collect personal data from children under 13. If you are a - parent or guardian and you are aware that your child has provided us - with personal data, please contact us. + You can update your profile and permanently delete your account at any + time from Settings. Deleting your account removes your + content from our systems. You can also opt out of promotional emails + using the unsubscribe link in any of them.
-- We may update our Privacy Policy from time to time. We will notify you - of any changes by posting the new Privacy Policy on this page and - updating the "Effective Date" at the top of this Privacy Policy. + We keep your data while your account is active. When you delete your + account, we delete your content (backups age out on a rolling basis).
-- If you have any questions about this Privacy Policy, please contact us - at: + Donetick isn't directed to children under 13, and we don't knowingly + collect their data. Child accounts created by a parent inside a circle + are managed by that parent.
+ ++ We'll post any updates here with a new date, and flag material changes + with a one-time notice inside the app. +
+ ++ Privacy questions or requests? Email{' '} + support@donetick.com. +
+ +Favoro LLC
Effective Date: January 1, 2024
-- These Terms of Service ("Terms") govern your access to and use of the - Donetick task management platform provided by Favoro LLC ("Donetick", "we", "us", or "our"). - By accessing or using our services, you agree to be bound by these Terms. If you do not agree to - these Terms, you may not access or use our services. + Last updated August 12, 2026
-- Donetick is available in two deployment models with different terms and responsibilities: -
-Please review the applicable sections carefully as they define different rights and obligations.
-This section applies only to users of the Cloud Service at donetick.com
- -This section applies to users deploying Donetick on their own infrastructure
- -- Applies to both Cloud and Self-Hosted Services + By creating an account or using Donetick Cloud at donetick.com (the + "Service"), you agree to these Terms. If you don't agree, don't use the + Service. Donetick is made by Favoro LLC ("we", "us", "our").
-Donetick comes in two flavors, and they're governed differently.
- Our services are provided "as is" and "as available" without any warranty of any kind, - express or implied, including but not limited to merchantability, fitness for a particular purpose, - or non-infringement. + Donetick Cloud is the hosted service we run at + donetick.com. These Terms cover it. +
++ Self-hosted Donetick is the open-source software you + run on your own server. It's licensed under the GNU Affero General + Public License v3 (AGPLv3), and that license, not these Terms, governs + your rights to use, modify, and share it. Nothing here is meant to + restrict anything the AGPLv3 grants you. +
++ If you run Donetick yourself, hosting, security, backups, updates, and + data-protection obligations are yours, not ours. We don't provide + support guarantees for self-hosted installs unless we've agreed to that + separately in writing.
-- You agree to indemnify and hold us harmless from any claims, damages, or expenses - arising from your use of our services, violation of these Terms, or infringement of any rights. + Donetick helps you and the people in your circle track tasks, chores, + and recurring schedules. It's a productivity tool, not professional, + legal, financial, or medical advice. +
+ ++ You're responsible for your account and for keeping your login secure. + You must be at least 13 years old (or the age of digital consent where + you live) to use the Service. Provide accurate information, and tell us + promptly if you think someone else has gotten into your account. +
+ ++ Don't use Donetick to break the law, infringe anyone's rights, upload + malware, harass other users, send spam, gain unauthorized access to our + systems or anyone's account, circumvent usage limits, or overload the + Service. +
++ Don't use Donetick Cloud to run a competing hosted service without our + written permission. This applies only to our Cloud Service and takes + nothing away from your AGPLv3 rights to the self-hosted software. +
+ ++ You own everything you put into Donetick: your tasks, chores, labels, + comments, and files. You grant us a limited license to host, store, + process, and transmit it solely to operate, secure, troubleshoot, and + improve the Service for you. We don't sell your content, and we don't + use it to train AI models. +
++ You're responsible for having the right to upload what you upload, and + for managing who you share circles and content with. We may remove + content when reasonably necessary to enforce these Terms, comply with + the law, or address a security issue, and we'll give you notice first + where that's practical. +
++ How we handle personal information is covered in our{' '} + Privacy Policy, including what our analytics and + error reporting do and don't collect, and how to turn them off. +
+ ++ We aim for roughly 99% monthly availability on the Cloud Service, but + that's a goal, not a service-level agreement, and there are no downtime + credits. We may pause parts of the Service for maintenance, security, or + emergencies, and we'll announce planned work ahead of time when we + reasonably can. Storage limits, API rate limits, and fair-use limits may + apply. +
+ ++ Donetick offers a free plan and paid plans billed monthly or annually in + advance through Stripe. Paid plans renew automatically until you cancel. +
++ You can cancel any time from your settings. Cancelling stops future + renewals, and you keep paid features through the end of the current + billing period. If a payment fails we may retry it or limit paid + features until it goes through. We'll give you reasonable notice before + a price change applies to your subscription. Refunds are handled + case-by-case at our discretion, except where the law requires one. + Nothing here limits your consumer-protection rights. +
+ ++ Where export tools are available, you can export your content; formats + and availability may change over time. If we ever discontinue the Cloud + Service, we'll give reasonable advance notice and, where possible, a + chance to export first. +
+ ++ Donetick connects to outside services like messaging platforms, + authentication providers, and webhooks. Those are governed by their own + terms and privacy policies, and we're not responsible for services we + don't control. Make sure you're authorized to connect whatever you + connect. +
+ ++ You own your content; we own the Donetick name, logo, branding, and the + proprietary parts of the Cloud Service. The AGPLv3 covers the software, + not our trademarks, so please don't use our name or logo in a way that + suggests we endorse or sponsor you.
- If you have questions about these Terms, please contact us: + You can stop using Donetick and delete your account at any time from + Settings. We may suspend or end access if you violate these Terms, or + where we need to address abuse, fraud, a security incident, a legal + obligation, or unpaid fees. Where it's practical, we'll give you notice + and a chance to fix the problem first. After termination we delete or + anonymize your content per our retention practices, though limited + records may stick around for legal, security, or accounting reasons.
-
- Last Updated: January 1, 2024
- These Terms of Service are effective immediately for new users and will become effective
- for existing users 30 days after posting.
+
+ Found a vulnerability? Please email{' '} + support@donetick.com and give + us a reasonable chance to fix it before disclosing it publicly.
+ ++ + THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE," WITHOUT WARRANTIES + OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT. WE DO NOT + WARRANT THAT THE SERVICE WILL BE UNINTERRUPTED, ERROR-FREE, OR SECURE, + OR THAT DATA WILL NEVER BE LOST OR CORRUPTED. + +
++ + TO THE MAXIMUM EXTENT PERMITTED BY LAW, FAVORO LLC IS NOT LIABLE FOR + INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE + DAMAGES, OR FOR LOST PROFITS, REVENUE, BUSINESS, GOODWILL, OR DATA. + OUR TOTAL LIABILITY IS LIMITED TO THE GREATER OF WHAT YOU PAID US IN + THE 12 MONTHS BEFORE THE CLAIM, OR $100. + +
++ Self-hosted installs are covered by the AGPLv3's warranty disclaimer. + We're not responsible for problems arising from your own infrastructure, + configuration, modifications, or hosting provider. Nothing in these + Terms excludes liability that can't legally be excluded. +
+ ++ To the extent the law allows, you agree to cover Favoro LLC against + third-party claims arising from your violation of these Terms, your + unlawful use of the Service, your infringement of someone else's rights, + or content you submitted. We'll give you reasonable notice of any such + claim and cooperate in the defense. +
+ ++ Before filing anything, please email{' '} + support@donetick.com and give + us a fair chance to sort it out. These Terms are governed by the laws of + the State of Delaware, USA, without regard to conflict-of-laws rules, + and disputes go to the state or federal courts in Delaware. Either of us + can still bring an eligible claim in small claims court, and none of + this waives rights that can't legally be waived. +
+ ++ We may update these Terms. We'll post the new version with an updated + date, and for material changes we'll give reasonable advance notice by + email or in-app notice (and get your consent where the law requires it). + Continuing to use the Service after non-material changes means you + accept them. If you don't agree to a material change, cancel before it + takes effect. +
+ ++ These Terms plus our Privacy Policy are the whole agreement between us. + If one provision turns out to be unenforceable, it gets trimmed to the + minimum necessary and the rest stands. Not enforcing something once + doesn't waive it later. You can't transfer these Terms without our + consent; we may transfer them in a merger, acquisition, or sale of + assets. These Terms don't create a partnership, employment, or agency + relationship. Neither of us is liable for delays caused by things + outside our reasonable control. Provisions that should survive + termination do, including those on content, intellectual property, + disclaimers, liability, indemnification, and disputes. +
+ ++ Questions about these Terms? Email{' '} + support@donetick.com. +
+ +Favoro LLC