diff --git a/worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql b/worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql new file mode 100644 index 000000000..e984fde46 --- /dev/null +++ b/worklenz-backend/database/migrations/20260517000000-add-zh-tw-language-type.sql @@ -0,0 +1 @@ +ALTER TYPE language_type ADD VALUE IF NOT EXISTS 'zh_tw' AFTER 'zh_cn'; diff --git a/worklenz-backend/database/sql/1_tables.sql b/worklenz-backend/database/sql/1_tables.sql index 36ccca006..f29e7d6c2 100644 --- a/worklenz-backend/database/sql/1_tables.sql +++ b/worklenz-backend/database/sql/1_tables.sql @@ -12,7 +12,7 @@ CREATE TYPE DEPENDENCY_TYPE AS ENUM ('blocked_by'); CREATE TYPE SCHEDULE_TYPE AS ENUM ('daily', 'weekly', 'yearly', 'monthly', 'every_x_days', 'every_x_weeks', 'every_x_months'); -CREATE TYPE LANGUAGE_TYPE AS ENUM ('en', 'es', 'pt', 'alb', 'de', 'zh_cn', 'ko'); +CREATE TYPE LANGUAGE_TYPE AS ENUM ('en', 'es', 'pt', 'alb', 'de', 'zh_cn', 'zh_tw', 'ko'); CREATE TYPE PROGRESS_MODE_TYPE AS ENUM ('manual', 'weighted', 'time', 'default'); diff --git a/worklenz-frontend/public/locales/alb/account-setup.json b/worklenz-frontend/public/locales/alb/account-setup.json index 3d2e785b6..29aa6bc11 100644 --- a/worklenz-frontend/public/locales/alb/account-setup.json +++ b/worklenz-frontend/public/locales/alb/account-setup.json @@ -166,7 +166,8 @@ "pt": "Portugalisht", "de": "Gjermanisht", "alb": "Shqip", - "zh": "Kinezçe" + "zh": "Kinezçe", + "zh_tw": "Kinezçe Tradicionale (Tajvaneze)" }, "orgSuggestions": { diff --git a/worklenz-frontend/public/locales/de/account-setup.json b/worklenz-frontend/public/locales/de/account-setup.json index d496d75c2..d45f26357 100644 --- a/worklenz-frontend/public/locales/de/account-setup.json +++ b/worklenz-frontend/public/locales/de/account-setup.json @@ -186,7 +186,8 @@ "pt": "Português", "de": "Deutsch", "alb": "Shqip", - "zh": "简体中文" + "zh": "简体中文", + "zh_tw": "繁體中文(台灣)" }, "orgSuggestions": { diff --git a/worklenz-frontend/public/locales/en/account-setup.json b/worklenz-frontend/public/locales/en/account-setup.json index 1d960237f..34648be5c 100644 --- a/worklenz-frontend/public/locales/en/account-setup.json +++ b/worklenz-frontend/public/locales/en/account-setup.json @@ -184,7 +184,8 @@ "pt": "Português", "de": "Deutsch", "alb": "Shqip", - "zh": "简体中文" + "zh": "简体中文", + "zh_tw": "繁體中文(台灣)" }, "orgSuggestions": { diff --git a/worklenz-frontend/public/locales/es/account-setup.json b/worklenz-frontend/public/locales/es/account-setup.json index 98694ba3d..3170349f4 100644 --- a/worklenz-frontend/public/locales/es/account-setup.json +++ b/worklenz-frontend/public/locales/es/account-setup.json @@ -187,7 +187,8 @@ "pt": "Português", "de": "Deutsch", "alb": "Shqip", - "zh": "简体中文" + "zh": "简体中文", + "zh_tw": "繁體中文(台灣)" }, "orgSuggestions": { diff --git a/worklenz-frontend/public/locales/pt/account-setup.json b/worklenz-frontend/public/locales/pt/account-setup.json index 24e7cbe6f..0f3f235fd 100644 --- a/worklenz-frontend/public/locales/pt/account-setup.json +++ b/worklenz-frontend/public/locales/pt/account-setup.json @@ -187,7 +187,8 @@ "pt": "Português", "de": "Deutsch", "alb": "Shqip", - "zh": "简体中文" + "zh": "简体中文", + "zh_tw": "繁體中文(台灣)" }, "orgSuggestions": { diff --git a/worklenz-frontend/public/locales/zh/account-setup.json b/worklenz-frontend/public/locales/zh/account-setup.json index b9a0e46ed..64bb00ad8 100644 --- a/worklenz-frontend/public/locales/zh/account-setup.json +++ b/worklenz-frontend/public/locales/zh/account-setup.json @@ -185,7 +185,8 @@ "pt": "Português", "de": "Deutsch", "alb": "Shqip", - "zh": "简体中文" + "zh": "简体中文", + "zh_tw": "繁體中文(台灣)" }, "orgSuggestions": { diff --git a/worklenz-frontend/public/locales/zh_tw/404-page.json b/worklenz-frontend/public/locales/zh_tw/404-page.json new file mode 100644 index 000000000..1c757c134 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/404-page.json @@ -0,0 +1,4 @@ +{ + "doesNotExistText": "抱歉,您造訪的頁面不存在。", + "backHomeButton": "回到首頁" +} diff --git a/worklenz-frontend/public/locales/zh_tw/account-setup.json b/worklenz-frontend/public/locales/zh_tw/account-setup.json new file mode 100644 index 000000000..64fd80c93 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/account-setup.json @@ -0,0 +1,213 @@ +{ + "continue": "繼續", + + "setupYourAccount": "設定帳戶", + "organizationStepTitle": "為組織命名", + "organizationStepWelcome": "歡迎使用 Worklenz!", + "organizationStepDescription": "讓我們先建立您的組織,這將成為團隊的主要工作區。", + "organizationStepLabel": "組織名稱", + "organizationStepPlaceholder": "例如:Acme Corporation", + "organizationStepTooltip": "此名稱會顯示在工作區中,之後可在設定中變更。", + "organizationStepNeedIdeas": "需要靈感嗎?", + "organizationStepUseDetected": "使用偵測到的名稱:", + "organizationStepCharacters": "個字元", + "organizationStepGoodLength": "長度適當", + "organizationStepTooShort": "太短", + "organizationStepNamingTips": "命名小技巧", + "organizationStepTip1": "簡潔好記", + "organizationStepTip2": "反映您的產業或價值觀", + "organizationStepTip3": "考量未來成長性", + "organizationStepTip4": "獨特且具品牌辨識度", + "organizationStepSuggestionsTitle": "名稱建議", + "organizationStepCategory1": "科技公司", + "organizationStepCategory2": "創意工作室", + "organizationStepCategory3": "顧問業", + "organizationStepCategory4": "新創公司", + "organizationStepSuggestionsNote": "這些僅為參考範例,請選擇能代表您組織的名稱。", + "organizationStepPrivacyNote": "組織名稱為私人資訊,僅團隊成員可見。", + + "projectStepTitle": "建立第一個專案", + "projectStepLabel": "您目前正在進行什麼專案?", + "projectStepPlaceholder": "例如:行銷計畫", + + "tasksStepLabel": "輸入幾項您準備在此專案中進行的任務", + "tasksStepAddAnother": "再新增一項", + + "emailPlaceholder": "電子郵件地址", + "invalidEmail": "請輸入有效的電子郵件地址", + "or": "或", + "templateButton": "從範本匯入", + "goBack": "回到上一步", + "cancel": "取消", + "create": "建立", + "templateDrawerTitle": "從範本中選擇", + "step3InputLabel": "以電子郵件邀請", + "addAnother": "再新增一項", + "skipForNow": "暫時略過", + "skipping": "略過中…", + "formTitle": "建立第一個任務", + "step3Title": "邀請團隊一同協作", + "maxMembers": "(最多可邀請 5 位成員)", + "maxTasks": "(最多可建立 5 項任務)", + + "membersStepTitle": "邀請團隊成員", + "membersStepDescription": "將團隊成員加入「{{organizationName}}」,開始協同合作", + "memberPlaceholder": "團隊成員 {{index}} - 請輸入電子郵件地址", + "validEmailAddress": "有效的電子郵件地址", + "addAnotherTeamMember": "再新增一位團隊成員 ({{current}}/{{max}})", + "canInviteLater": "之後也可以隨時邀請團隊成員", + "skipStepDescription": "尚未準備好電子郵件地址?沒關係!您可以略過此步驟,稍後再從專案儀錶板邀請團隊成員。", + + "orgCategoryTech": "科技公司", + "orgCategoryCreative": "創意工作室", + "orgCategoryConsulting": "顧問業", + "orgCategoryStartups": "新創公司", + "namingTip1": "簡潔好記", + "namingTip2": "反映您的產業或價值觀", + "namingTip3": "考量未來成長性", + "namingTip4": "獨特且具品牌辨識度", + + "aboutYouTitle": "自我介紹", + "aboutYouDescription": "協助我們為您打造個人化體驗", + "orgTypeQuestion": "哪個選項最能描述您的組織?", + "userRoleQuestion": "您的角色是什麼?", + + "yourNeedsTitle": "您的主要需求是什麼?", + "yourNeedsDescription": "勾選所有適用選項,以便我們為您設定工作區", + "yourNeedsQuestion": "您主要會如何使用 Worklenz?", + "useCaseTaskOrg": "組織並追蹤任務", + "useCaseTeamCollab": "順暢地團隊協作", + "useCaseResourceMgmt": "管理時間與資源", + "useCaseClientComm": "與客戶保持聯絡", + "useCaseTimeTrack": "監控專案工時", + "useCaseOther": "其他用途", + "selectedText": "已選取", + "previousToolsQuestion": "之前使用過哪些工具?(選填)", + + "discoveryTitle": "最後一個問題…", + "discoveryDescription": "讓我們了解您是如何發現 Worklenz 的", + "discoveryQuestion": "您是如何得知我們的?", + "allSetTitle": "一切就緒!", + "allSetDescription": "讓我們建立第一個專案,開始使用 Worklenz 吧", + "surveyCompleteTitle": "感謝您!", + "surveyCompleteDescription": "您的意見回饋有助於我們為所有人改善 Worklenz", + "aboutYouStepName": "關於您", + "yourNeedsStepName": "您的需求", + "discoveryStepName": "探索", + "stepProgress": "步驟 {step} / 3:{title}", + + "projectStepHeader": "讓我們來建立第一個專案", + "projectStepSubheader": "從零開始,或使用範本更快上手", + "startFromScratch": "從零開始", + "templateSelected": "已選取以下範本", + "quickSuggestions": "快速建議:", + "orText": "或", + "startWithTemplate": "使用範本開始", + "clearToSelectTemplate": "請先清除上方的專案名稱以選取範本", + "templateHeadStart": "使用預建的專案架構快速起步", + "browseAllTemplates": "瀏覽所有範本", + "templatesAvailable": "超過 15 個產業專用範本可供選擇", + "chooseTemplate": "選擇符合您專案類型的範本", + "createProject": "建立專案", + + "templateSoftwareDev": "軟體開發", + "templateSoftwareDesc": "敏捷衝刺、錯誤追蹤、版本釋出", + "templateMarketing": "行銷活動", + "templateMarketingDesc": "活動規劃、內容行事曆", + "templateConstruction": "營建專案", + "templateConstructionDesc": "階段、許可、承包商", + "templateStartup": "新創上線", + "templateStartupDesc": "MVP 開發、募資、成長", + + "tasksStepTitle": "新增第一批任務", + "tasksStepDescription": "將「{{projectName}}」拆解為可執行的任務,馬上開始吧", + "taskPlaceholder": "任務 {{index}} - 例如:需要完成什麼?", + "addAnotherTask": "再新增一項任務 ({{current}}/{{max}})", + + "surveyStepTitle": "自我介紹", + "surveyStepLabel": "回答幾個簡短的問題,協助我們為您打造個人化的 Worklenz 體驗。", + + "organizationType": "哪個選項最能描述您的組織?", + "organizationTypeFreelancer": "自由工作者", + "organizationTypeStartup": "新創公司", + "organizationTypeSmallMediumBusiness": "中小型企業", + "organizationTypeAgency": "代理商", + "organizationTypeEnterprise": "企業", + "organizationTypeOther": "其他", + + "userRole": "您的角色是什麼?", + "userRoleFounderCeo": "創辦人 / 執行長", + "userRoleProjectManager": "專案經理", + "userRoleSoftwareDeveloper": "軟體開發人員", + "userRoleDesigner": "設計師", + "userRoleOperations": "營運", + "userRoleOther": "其他", + + "mainUseCases": "您主要會將 Worklenz 用於什麼?", + "mainUseCasesTaskManagement": "任務管理", + "mainUseCasesTeamCollaboration": "團隊協作", + "mainUseCasesResourcePlanning": "資源規劃", + "mainUseCasesClientCommunication": "客戶溝通與報表", + "mainUseCasesTimeTracking": "時間追蹤", + "mainUseCasesOther": "其他", + + "previousTools": "在使用 Worklenz 之前,您使用過哪些工具?", + "previousToolsPlaceholder": "例如:Trello、Asana、Monday.com", + + "howHeardAbout": "您是如何得知 Worklenz 的?", + "howHeardAboutGoogleSearch": "Google 搜尋", + "howHeardAboutTwitter": "Twitter", + "howHeardAboutLinkedin": "LinkedIn", + "howHeardAboutFriendColleague": "朋友或同事介紹", + "howHeardAboutBlogArticle": "部落格或文章", + "howHeardAboutOther": "其他", + + "aboutYouStepTitle": "自我介紹", + "aboutYouStepDescription": "協助我們為您打造個人化體驗", + "yourNeedsStepTitle": "您的主要需求是什麼?", + "yourNeedsStepDescription": "勾選所有適用選項,以便我們為您設定工作區", + "selected": "已選取", + "previousToolsLabel": "之前使用過哪些工具?(選填)", + + "roleSuggestions": { + "designer": "UI/UX、平面設計、創意", + "developer": "前端、後端、全端", + "projectManager": "規劃、協調", + "marketing": "內容、社群媒體、成長", + "sales": "業務開發、客戶關係", + "operations": "行政、人資、財務" + }, + + "languages": { + "en": "English", + "es": "Español", + "pt": "Português", + "de": "Deutsch", + "alb": "Shqip", + "zh": "簡體中文", + "zh_tw": "繁體中文(台灣)" + }, + + "orgSuggestions": { + "tech": ["TechCorp", "DevStudio", "CodeCraft", "PixelForge"], + "creative": ["Creative Hub", "Design Studio", "Brand Works", "Visual Arts"], + "consulting": ["Strategy Group", "Business Solutions", "Expert Advisors", "Growth Partners"], + "startup": ["Innovation Labs", "Future Works", "Venture Co", "Next Gen"] + }, + + "projectSuggestions": { + "freelancer": ["客戶專案", "作品集更新", "個人品牌"], + "startup": ["MVP 開發", "產品上線", "市場調查"], + "agency": ["客戶活動", "品牌策略", "網站重新設計"], + "enterprise": ["系統遷移", "流程最佳化", "團隊訓練"] + }, + + "useCaseDescriptions": { + "taskManagement": "組織並追蹤任務", + "teamCollaboration": "順暢地團隊協作", + "resourcePlanning": "管理時間與資源", + "clientCommunication": "與客戶保持聯絡", + "timeTracking": "監控專案工時", + "other": "其他用途" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/configuration.json b/worklenz-frontend/public/locales/zh_tw/admin-center/configuration.json new file mode 100644 index 000000000..740a7d585 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/configuration.json @@ -0,0 +1,26 @@ +{ + "billingDetails": "帳單資訊", + "name": "名稱", + "namePlaceholder": "名稱", + "emailAddress": "電子郵件地址", + "emailPlaceholder": "電子郵件地址", + "contactNumber": "聯絡電話", + "phoneNumberPlaceholder": "電話號碼", + "phoneValidationError": "電話號碼必須為 10 位數字", + "companyDetails": "公司資訊", + "companyName": "公司名稱", + "companyNamePlaceholder": "公司名稱", + "addressLine01": "地址第一行", + "addressLine01Placeholder": "地址第一行", + "addressLine02": "地址第二行", + "addressLine02Placeholder": "地址第二行", + "country": "國家", + "countryPlaceholder": "國家", + "city": "城市", + "cityPlaceholder": "城市", + "state": "州/省", + "statePlaceholder": "州/省", + "postalCode": "郵遞區號", + "postalCodePlaceholder": "郵遞區號", + "save": "儲存" +} \ No newline at end of file diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json b/worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json new file mode 100644 index 000000000..9ff0a7f19 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/current-bill.json @@ -0,0 +1,143 @@ +{ + "title": "帳務", + "currentBill": "目前帳單", + "configuration": "帳單設定", + "currentPlanDetails": "目前方案詳細資料", + "upgradePlan": "升級方案", + "cardBodyText01": "免費試用", + "cardBodyText02": "(您的試用方案將在 1 個月 19 天後到期)", + "redeemCode": "兌換碼", + "accountStorage": "帳戶儲存空間", + "used": "已使用:", + "remaining": "剩餘:", + "charges": "費用", + "tooltip": "目前計費週期的費用", + "description": "描述", + "billingPeriod": "計費週期", + "billStatus": "帳單狀態", + "perUserValue": "每位使用者金額", + "users": "使用者", + + "amount": "金額", + "invoices": "發票", + "transactionId": "交易編號", + "transactionDate": "交易日期", + "paymentMethod": "付款方式", + "status": "狀態", + "ltdUsers": "最多可新增 {{ltd_users}} 位使用者。", + + "totalSeats": "總席位數", + "availableSeats": "可用席位數", + "addMoreSeats": "新增更多席位", + + "drawerTitle": "兌換碼", + "label": "兌換碼", + "drawerPlaceholder": "請輸入兌換碼", + "redeemSubmit": "提交", + + "modalTitle": "選擇最適合團隊的方案", + "seatLabel": "席位數量", + "freePlan": "免費方案", + "startup": "新創方案", + "business": "商務方案", + "tag": "最受歡迎", + "enterprise": "企業方案", + + "freeSubtitle": "永久免費", + "freeUsers": "最適合個人使用", + "freeText01": "100MB 儲存空間", + "freeText02": "3 個專案", + "freeText03": "5 位團隊成員", + + "startupSubtitle": "固定費率 / 月", + "startupUsers": "最多 15 位使用者", + "startupText01": "25GB 儲存空間", + "startupText02": "無限進行中專案", + "startupText03": "排程", + "startupText04": "報表", + "startupText05": "訂閱專案", + + "businessSubtitle": "使用者 / 月", + "businessUsers": "16 - 200 位使用者", + + "enterpriseUsers": "200 - 500+ 位使用者", + + "footerTitle": "請提供聯絡電話,以便我們與您聯絡。", + "footerLabel": "聯絡電話", + "footerButton": "聯絡我們", + + "redeemCodePlaceHolder": "請輸入兌換碼", + "submit": "提交", + + "trialPlan": "免費試用", + "trialExpireDate": "有效期至 {{trial_expire_date}}", + "trialExpired": "免費試用已於 {{trial_expire_string}} 到期", + "trialInProgress": "免費試用將於 {{trial_expire_string}} 到期", + + "required": "此欄位為必填", + "invalidCode": "無效的兌換碼", + + "selectPlan": "選擇最適合團隊的方案", + "changeSubscriptionPlan": "變更訂閱方案", + "noOfSeats": "席位數量", + "annualPlan": "Pro - 年繳", + "monthlyPlan": "Pro - 月繳", + "freeForever": "永久免費", + "bestForPersonalUse": "最適合個人使用", + "storage": "儲存空間", + "projects": "專案", + "teamMembers": "團隊成員", + "unlimitedTeamMembers": "不限團隊成員數", + "unlimitedActiveProjects": "不限進行中專案數", + "schedule": "排程", + "reporting": "報表", + "subscribeToProjects": "訂閱專案", + "billedAnnually": "按年計費", + "billedMonthly": "按月計費", + + "pausePlan": "暫停方案", + "resumePlan": "恢復方案", + "changePlan": "變更方案", + "cancelPlan": "取消方案", + + "perMonthPerUser": "每位使用者 / 月", + "viewInvoice": "檢視發票", + "switchToFreePlan": "切換至免費方案", + + "expirestoday": "今天", + "expirestomorrow": "明天", + "expiredDayAgo": "{{days}} 天前", + "expiredDaysAgo": "{{days}} 天前", + + "continueWith": "繼續使用 {{plan}}", + "changeToPlan": "變更為 {{plan}}", + "creditPlan": "點數方案", + "customPlan": "自訂方案", + "planValidTill": "方案有效期至 {{date}}", + "purchaseSeatsText": "如需繼續,請購買額外席位。", + "currentSeatsText": "目前有 {{seats}} 個可用席位。", + "selectSeatsText": "請選擇要購買的額外席位數量。", + "purchase": "購買", + "contactSales": "聯絡業務", + "submitSuccess": "兌換碼使用成功!", + "submitSuccessDescription": "帳戶已更新為新的點數。", + "percentUsed": "% 已使用", + "sizeUnits": { + "bytes": "Bytes", + "kb": "KB", + "mb": "MB", + "gb": "GB", + "tb": "TB" + }, + "seatPerMonth": "席位 / 月", + "totalPrice": "合計 $", + "tryForFree": "免費試用", + "subscriptionUpdateSuccess": "訂閱方案更新成功!", + "paymentProcessorError": "付款處理器載入失敗", + "seatsLabel": "席位:", + "requiredField": "*", + "purchaseSeatsTextSingle": "如需繼續,請購買一個額外席位。", + "singleUserNote": "目前有 1 個可用席位。", + "selectSeatsTextSingle": "請選擇要購買的額外席位數量。", + "phoneNumberPattern": "07xxxxxxxx" +} diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/overview.json b/worklenz-frontend/public/locales/zh_tw/admin-center/overview.json new file mode 100644 index 000000000..5b19c17ee --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/overview.json @@ -0,0 +1,11 @@ +{ + "overview": "總覽", + "name": "組織名稱", + "owner": "組織擁有者", + "admins": "組織管理員", + "contactNumber": "新增聯絡電話", + "edit": "編輯", + "emailAddress": "電子郵件地址", + "enterOrganizationName": "請輸入組織名稱", + "ownerSuffix": "(擁有者)" +} diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/projects.json b/worklenz-frontend/public/locales/zh_tw/admin-center/projects.json new file mode 100644 index 000000000..03a90b2b5 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/projects.json @@ -0,0 +1,12 @@ +{ + "membersCount": "成員數量", + "createdAt": "建立時間", + "projectName": "專案名稱", + "teamName": "團隊名稱", + "refreshProjects": "重新整理專案", + "searchPlaceholder": "依專案名稱搜尋", + "deleteProject": "確定要刪除此專案嗎?", + "confirm": "確認", + "cancel": "取消", + "delete": "刪除專案" +} diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/sidebar.json b/worklenz-frontend/public/locales/zh_tw/admin-center/sidebar.json new file mode 100644 index 000000000..2bc48a309 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/sidebar.json @@ -0,0 +1,8 @@ +{ + "overview": "總覽", + "users": "使用者", + "teams": "團隊", + "billing": "帳務", + "projects": "專案", + "adminCenter": "管理中心" +} diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/teams.json b/worklenz-frontend/public/locales/zh_tw/admin-center/teams.json new file mode 100644 index 000000000..e53348bdb --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/teams.json @@ -0,0 +1,35 @@ +{ + "title": "團隊", + "subtitle": "個團隊", + "tooltip": "重新整理團隊", + "placeholder": "依名稱搜尋", + "addTeam": "新增團隊", + "team": "團隊", + "membersCount": "成員數量", + "members": "成員", + "drawerTitle": "建立新團隊", + "label": "團隊名稱", + "drawerPlaceholder": "名稱", + "create": "建立", + "delete": "刪除", + "settings": "設定", + "popTitle": "確定要執行嗎?", + "message": "請輸入名稱", + "teamSettings": "團隊設定", + "teamName": "團隊名稱", + "teamDescription": "團隊描述", + "teamMembers": "團隊成員", + "teamMembersCount": "團隊成員數量", + "teamMembersPlaceholder": "依名稱搜尋", + "addMember": "新增成員", + "add": "新增", + "update": "更新", + "teamNamePlaceholder": "團隊名稱", + "user": "使用者", + "role": "角色", + "owner": "擁有者", + "admin": "管理員", + "member": "成員", + "cannotChangeOwnerRole": "無法變更擁有者的角色", + "pendingInvitation": "待接受邀請" +} diff --git a/worklenz-frontend/public/locales/zh_tw/admin-center/users.json b/worklenz-frontend/public/locales/zh_tw/admin-center/users.json new file mode 100644 index 000000000..7ad0b10dc --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/admin-center/users.json @@ -0,0 +1,10 @@ +{ + "title": "使用者", + "subTitle": "位使用者", + "placeholder": "依名稱搜尋", + "user": "使用者", + "email": "電子郵件", + "lastActivity": "最近活動", + "refresh": "重新整理使用者", + "name": "名稱" +} diff --git a/worklenz-frontend/public/locales/zh_tw/all-project-list.json b/worklenz-frontend/public/locales/zh_tw/all-project-list.json new file mode 100644 index 000000000..308f074ba --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/all-project-list.json @@ -0,0 +1,34 @@ +{ + "name": "名稱", + "client": "客戶", + "category": "類別", + "status": "狀態", + "tasksProgress": "任務進度", + "updated_at": "最後更新", + "members": "成員", + "setting": "設定", + "projects": "專案", + "refreshProjects": "重新整理專案", + "all": "全部", + "favorites": "我的最愛", + "archived": "已封存", + "placeholder": "依名稱搜尋", + "archive": "封存", + "unarchive": "解除封存", + "archiveConfirm": "確定要封存此專案嗎?", + "unarchiveConfirm": "確定要解除封存此專案嗎?", + "yes": "是", + "no": "否", + "clickToFilter": "按一下以篩選", + "noProjects": "找不到任何專案", + "addToFavourites": "加入我的最愛", + "list": "清單", + "group": "群組", + "listView": "清單檢視", + "groupView": "群組檢視", + "groupBy": { + "category": "類別", + "client": "客戶" + }, + "noPermission": "您沒有權限執行此操作" +} diff --git a/worklenz-frontend/public/locales/zh_tw/auth/auth-common.json b/worklenz-frontend/public/locales/zh_tw/auth/auth-common.json new file mode 100644 index 000000000..962374e43 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/auth/auth-common.json @@ -0,0 +1,5 @@ +{ + "loggingOut": "正在登出…", + "authenticating": "正在驗證…", + "gettingThingsReady": "正在為您準備一切…" +} diff --git a/worklenz-frontend/public/locales/zh_tw/auth/forgot-password.json b/worklenz-frontend/public/locales/zh_tw/auth/forgot-password.json new file mode 100644 index 000000000..9cfb333ed --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/auth/forgot-password.json @@ -0,0 +1,12 @@ +{ + "headerDescription": "重設密碼", + "emailLabel": "電子郵件", + "emailPlaceholder": "請輸入電子郵件", + "emailRequired": "請輸入電子郵件!", + "resetPasswordButton": "重設密碼", + "returnToLoginButton": "回到登入頁面", + "passwordResetSuccessMessage": "密碼重設連結已寄至您的電子郵件。", + "orText": "或", + "successTitle": "重設說明已寄出!", + "successMessage": "重設資訊已寄至您的電子郵件,請查收。" +} diff --git a/worklenz-frontend/public/locales/zh_tw/auth/login.json b/worklenz-frontend/public/locales/zh_tw/auth/login.json new file mode 100644 index 000000000..7bbd72752 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/auth/login.json @@ -0,0 +1,27 @@ +{ + "headerDescription": "登入帳戶", + "emailLabel": "電子郵件", + "emailPlaceholder": "請輸入電子郵件", + "emailRequired": "請輸入電子郵件!", + "passwordLabel": "密碼", + "passwordPlaceholder": "請輸入密碼", + "passwordRequired": "請輸入密碼!", + "rememberMe": "記住我", + "loginButton": "登入", + "signupButton": "註冊", + "forgotPasswordButton": "忘記密碼?", + "signInWithGoogleButton": "使用 Google 登入", + "dontHaveAccountText": "還沒有帳戶?", + "orText": "或", + "successMessage": "登入成功!", + "loginError": "登入失敗", + "googleLoginError": "Google 登入失敗", + "validationMessages": { + "email": "請輸入有效的電子郵件地址", + "password": "密碼長度至少需 8 個字元" + }, + "errorMessages": { + "loginErrorTitle": "登入失敗", + "loginErrorMessage": "請檢查電子郵件和密碼後重試" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/auth/signup.json b/worklenz-frontend/public/locales/zh_tw/auth/signup.json new file mode 100644 index 000000000..f572e2105 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/auth/signup.json @@ -0,0 +1,31 @@ +{ + "headerDescription": "註冊以開始使用", + "nameLabel": "全名", + "namePlaceholder": "請輸入全名", + "nameRequired": "請輸入全名!", + "nameMinCharacterRequired": "全名至少需 4 個字元!", + "emailLabel": "電子郵件", + "emailPlaceholder": "請輸入電子郵件", + "emailRequired": "請輸入電子郵件!", + "passwordLabel": "密碼", + "passwordGuideline": "密碼長度至少 8 個字元,須包含大小寫字母、數字及特殊符號。", + "passwordPlaceholder": "請輸入密碼", + "passwordRequired": "請輸入密碼!", + "passwordMinCharacterRequired": "密碼長度至少需 8 個字元!", + "passwordMaxCharacterRequired": "密碼長度最多 32 個字元!", + "passwordPatternRequired": "密碼不符合要求!", + "strongPasswordPlaceholder": "請輸入更強的密碼", + "passwordValidationAltText": "密碼至少需 8 個字元,並包含大小寫字母、數字及符號。", + "signupSuccessMessage": "註冊成功!", + "privacyPolicyLink": "隱私權政策", + "termsOfUseLink": "使用條款", + "bySigningUpText": "註冊即表示您同意我們的", + "andText": "和", + "signupButton": "註冊", + "signInWithGoogleButton": "使用 Google 登入", + "alreadyHaveAccountText": "已經有帳戶了?", + "loginButton": "登入", + "orText": "或", + "reCAPTCHAVerificationError": "reCAPTCHA 驗證錯誤", + "reCAPTCHAVerificationErrorMessage": "無法完成 reCAPTCHA 驗證,請再試一次。" +} diff --git a/worklenz-frontend/public/locales/zh_tw/auth/verify-reset-email.json b/worklenz-frontend/public/locales/zh_tw/auth/verify-reset-email.json new file mode 100644 index 000000000..0048930bb --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/auth/verify-reset-email.json @@ -0,0 +1,14 @@ +{ + "title": "驗證重設電子郵件", + "description": "請輸入新密碼", + "placeholder": "請輸入新密碼", + "confirmPasswordPlaceholder": "請確認新密碼", + "passwordHint": "密碼至少 8 個字元,須包含大小寫字母、數字及符號。", + "resetPasswordButton": "重設密碼", + "orText": "或", + "resendResetEmail": "重新寄送重設信件", + "passwordRequired": "請輸入新密碼", + "returnToLoginButton": "回到登入頁面", + "confirmPasswordRequired": "請確認新密碼", + "passwordMismatch": "兩次輸入的密碼不一致" +} diff --git a/worklenz-frontend/public/locales/zh_tw/common.json b/worklenz-frontend/public/locales/zh_tw/common.json new file mode 100644 index 000000000..6e2080a4c --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/common.json @@ -0,0 +1,60 @@ +{ + "login-success": "登入成功!", + "login-failed": "登入失敗,請檢查您的帳號密碼後重試。", + "signup-success": "註冊成功!歡迎加入。", + "signup-failed": "註冊失敗,請確認所有必填欄位皆已填寫後重試。", + "reconnecting": "已與伺服器中斷連線。", + "connection-lost": "無法連線至伺服器,請檢查網路連線。", + "connection-restored": "已成功連線至伺服器", + "cancel": "取消", + "update-available": "Worklenz 已更新!", + "update-description": "新版 Worklenz 已推出,包含最新功能與改進。", + "update-instruction": "為獲得最佳體驗,請重新載入頁面以套用更新。", + "update-whats-new": "💡 <1>更新內容:效能提升、錯誤修正,以及更優質的使用體驗", + "update-now": "立即更新", + "update-later": "稍後再說", + "updating": "更新中…", + "license-expired-title": "訂閱已到期", + "license-expired-subtitle": "您的 Worklenz 訂閱已到期,請續訂以繼續使用所有功能。", + "license-expired-trial-title": "試用期已結束", + "license-expired-trial-subtitle": "您的 Worklenz 試用期已結束,請升級以繼續使用所有功能。", + "license-expired-custom-title": "自訂方案已到期", + "license-expired-custom-subtitle": "您的自訂方案已到期,請聯絡客服或續訂以繼續使用。", + "license-expired-features": "立即續訂以繼續享有:", + "license-expired-trial-features": "立即升級以解鎖:", + "license-expired-custom-features": "聯絡客服以繼續使用:", + "license-expired-feature-1": "✓ 無限專案與任務", + "license-expired-feature-2": "✓ 進階報表與分析", + "license-expired-feature-3": "✓ 團隊協作功能", + "license-expired-feature-4": "✓ 優先客服支援", + "license-expired-upgrade": "立即續訂", + "license-expired-trial-upgrade": "立即升級", + "license-expired-custom-upgrade": "聯絡客服", + "license-expired-contacting-support": "正在聯絡客服…", + "license-expired-message-sent": "訊息已送出 ✓", + "license-expired-days-remaining": "試用期剩餘 {{days}} 天", + "trial-expiring-soon": "試用期將在 {{days}} 天後到期", + "trial-expiring-soon_plural": "試用期將在 {{days}} 天後到期", + "trial-expiring-today": "試用期今天到期!", + "trial-expiring-upgrade": "立即升級以保留所有資料並繼續使用,不中斷服務", + "trial-badge-days": "剩餘 {{days}} 天", + "trial-badge-today": "最後一天!", + "trial-badge-hours": "剩餘 {{hours}} 小時", + "trial-alert-admin-note": "您仍可進入管理中心來管理訂閱方案", + "trial-alert-dismiss": "今天不再顯示", + "switch-team-to-continue": "切換團隊以繼續", + "current-team": "目前團隊", + "select-team": "選擇團隊", + "owned-by": "擁有者", + "switch-team-active-subscription": "請切換至擁有有效訂閱的團隊以繼續使用", + "or": "或", + "license-expiring-soon": "授權將在 {{days}} 天後到期", + "license-expiring-soon_plural": "授權將在 {{days}} 天後到期", + "license-expiring-today": "授權今天到期!", + "license-expired-grace-period": "授權已到期,寬限期剩餘 {{days}} 天", + "license-expired-grace-period_plural": "授權已到期,寬限期剩餘 {{days}} 天", + "license-expiring-upgrade": "立即續訂以保留所有資料並繼續使用,不中斷服務", + "license-badge-days": "剩餘 {{days}} 天", + "license-badge-today": "最後一天!", + "license-badge-hours": "剩餘 {{hours}} 小時" +} diff --git a/worklenz-frontend/public/locales/zh_tw/create-first-project-form.json b/worklenz-frontend/public/locales/zh_tw/create-first-project-form.json new file mode 100644 index 000000000..9a867c4b6 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/create-first-project-form.json @@ -0,0 +1,13 @@ +{ + "formTitle": "建立第一個專案", + "inputLabel": "您目前正在進行什麼專案?", + "or": "或", + "templateButton": "從範本匯入", + "createFromTemplate": "從範本建立", + "goBack": "回到上一步", + "continue": "繼續", + "cancel": "取消", + "create": "建立", + "templateDrawerTitle": "從範本中選擇", + "createProject": "建立專案" +} diff --git a/worklenz-frontend/public/locales/zh_tw/create-first-tasks.json b/worklenz-frontend/public/locales/zh_tw/create-first-tasks.json new file mode 100644 index 000000000..e3e8605b5 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/create-first-tasks.json @@ -0,0 +1,7 @@ +{ + "formTitle": "建立第一個任務", + "inputLable": "輸入幾項您準備在此專案中進行的任務", + "addAnother": "再新增一項", + "goBack": "回到上一步", + "continue": "繼續" +} diff --git a/worklenz-frontend/public/locales/zh_tw/home.json b/worklenz-frontend/public/locales/zh_tw/home.json new file mode 100644 index 000000000..040166eea --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/home.json @@ -0,0 +1,62 @@ +{ + "todoList": { + "title": "待辦清單", + "refreshTasks": "重新整理任務", + "addTask": "+ 新增任務", + "noTasks": "沒有任務", + "pressEnter": "按下", + "toCreate": "即可建立。", + "markAsDone": "標記為完成" + }, + "projects": { + "title": "專案", + "refreshProjects": "重新整理專案", + "noRecentProjects": "您目前未被指派至任何專案。", + "noFavouriteProjects": "尚未有任何專案被加入我的最愛。", + "recent": "最近", + "favourites": "我的最愛" + }, + "tasks": { + "assignedToMe": "指派給我", + "assignedByMe": "由我指派", + "all": "全部", + "today": "今天", + "upcoming": "即將到期", + "overdue": "逾期", + "noDueDate": "無到期日", + "noTasks": "沒有可顯示的任務。", + "addTask": "+ 新增任務", + "name": "名稱", + "project": "專案", + "status": "狀態", + "dueDate": "到期日", + "dueDatePlaceholder": "設定到期日", + "tomorrow": "明天", + "nextWeek": "下週", + "nextMonth": "下個月", + "projectRequired": "請選擇一個專案", + "pressTabToSelectDueDateAndProject": "按 Tab 鍵選擇到期日和專案", + "dueOn": "到期日為", + "taskRequired": "請新增一項任務", + "list": "清單", + "calendar": "日曆", + "tasks": "任務", + "refresh": "重新整理", + "recentActivity": "近期活動", + "recentTasks": "近期任務", + "recentTasksSegment": "近期任務", + "timeLogged": "已記錄時間", + "timeLoggedSegment": "已記錄時間", + "noRecentTasks": "沒有近期任務", + "noTimeLoggedTasks": "沒有已記錄時間的任務", + "activityTag": "活動", + "timeLogTag": "時間記錄", + "timerTag": "計時器", + "activitySingular": "項活動", + "activityPlural": "項活動", + "recentTaskAriaLabel": "近期任務:", + "timeLoggedTaskAriaLabel": "已記錄時間的任務:", + "errorLoadingRecentTasks": "載入近期任務時發生錯誤", + "errorLoadingTimeLoggedTasks": "載入已記錄時間的任務時發生錯誤" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json b/worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json new file mode 100644 index 000000000..96686cf4f --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/invite-initial-team-members.json @@ -0,0 +1,8 @@ +{ + "formTitle": "邀請團隊一同協作", + "inputLable": "以電子郵件邀請", + "addAnother": "再新增一項", + "goBack": "回到上一步", + "continue": "繼續", + "skipForNow": "暫時略過" +} diff --git a/worklenz-frontend/public/locales/zh_tw/kanban-board.json b/worklenz-frontend/public/locales/zh_tw/kanban-board.json new file mode 100644 index 000000000..65de8e05d --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/kanban-board.json @@ -0,0 +1,53 @@ +{ + "rename": "重新命名", + "delete": "刪除", + "addTask": "新增任務", + "addSectionButton": "新增區段", + "changeCategory": "變更類別", + + "deleteTooltip": "刪除", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + + "deleteTaskTitle": "刪除任務", + "deleteTaskContent": "確定要刪除此任務嗎?此操作無法復原。", + "deleteTaskConfirm": "刪除", + "deleteTaskCancel": "取消", + + "deleteStatusTitle": "刪除狀態", + "deleteStatusContent": "確定要刪除此狀態嗎?此操作無法復原。", + + "deletePhaseTitle": "刪除階段", + "deletePhaseContent": "確定要刪除此階段嗎?此操作無法復原。", + + "dueDate": "到期日", + "cancel": "取消", + + "today": "今天", + "tomorrow": "明天", + "assignToMe": "指派給我", + "archive": "封存", + + "newTaskNamePlaceholder": "請輸入任務名稱", + "newSubtaskNamePlaceholder": "請輸入子任務名稱", + "untitledSection": "未命名區段", + "unmapped": "未對應", + "clickToChangeDate": "按一下以變更日期", + "noDueDate": "無到期日", + "save": "儲存", + "clear": "清除", + "nextWeek": "下週", + "noSubtasks": "沒有子任務", + "showSubtasks": "顯示子任務", + "hideSubtasks": "隱藏子任務", + + "errorLoadingTasks": "載入任務時發生錯誤", + "noTasksFound": "找不到任何任務", + "loadingFilters": "正在載入篩選條件…", + "failedToUpdateColumnOrder": "無法更新欄位順序", + "failedToUpdatePhaseOrder": "無法更新階段順序", + "pleaseTryAgain": "請再試一次", + "taskNotCompleted": "任務尚未完成", + "completeTaskDependencies": "請先完成相依任務再繼續" +} diff --git a/worklenz-frontend/public/locales/zh_tw/license-expired.json b/worklenz-frontend/public/locales/zh_tw/license-expired.json new file mode 100644 index 000000000..231caeb58 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/license-expired.json @@ -0,0 +1,6 @@ +{ + "title": "您的 Worklenz 試用期已結束!", + "subtitle": "請立即升級。", + "button": "立即升級", + "checking": "正在檢查訂閱狀態…" +} diff --git a/worklenz-frontend/public/locales/zh_tw/navbar.json b/worklenz-frontend/public/locales/zh_tw/navbar.json new file mode 100644 index 000000000..f35966d8d --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/navbar.json @@ -0,0 +1,32 @@ +{ + "logoAlt": "Worklenz 圖示", + "home": "首頁", + "projects": "專案", + "schedule": "排程", + "reporting": "報表", + "clients": "客戶", + "teams": "團隊", + "labels": "標籤", + "jobTitles": "職稱", + "upgradePlan": "升級方案", + "upgradePlanTooltip": "升級方案", + "invite": "邀請", + "inviteTooltip": "邀請團隊成員加入", + "switchTeamTooltip": "切換團隊", + "help": "說明", + "notificationTooltip": "檢視通知", + "profileTooltip": "檢視個人資料", + "adminCenter": "管理中心", + "settings": "設定", + "deleteAccount": "刪除帳戶", + "logOut": "登出", + "notificationsDrawer": { + "read": "已讀通知", + "unread": "未讀通知", + "markAsRead": "標記為已讀", + "readAndJoin": "已讀並加入", + "accept": "接受", + "acceptAndJoin": "接受並加入", + "noNotifications": "沒有通知" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/organization-name-form.json b/worklenz-frontend/public/locales/zh_tw/organization-name-form.json new file mode 100644 index 000000000..bee0df8c8 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/organization-name-form.json @@ -0,0 +1,5 @@ +{ + "nameYourOrganization": "為組織命名。", + "worklenzAccountTitle": "為 Worklenz 帳戶選擇一個名稱。", + "continue": "繼續" +} diff --git a/worklenz-frontend/public/locales/zh_tw/phases-drawer.json b/worklenz-frontend/public/locales/zh_tw/phases-drawer.json new file mode 100644 index 000000000..c98675491 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/phases-drawer.json @@ -0,0 +1,24 @@ +{ + "configurePhases": "設定階段", + "configure": "設定", + "phaseLabel": "階段標籤", + "enterPhaseName": "請輸入階段名稱", + "addOption": "新增選項", + "phaseOptions": "階段選項", + "optionsText": "選項", + "dragToReorderPhases": "拖曳階段以重新排序,每個階段可設定不同顏色。", + "enterNewPhaseName": "請輸入新的階段名稱…", + "addPhase": "新增階段", + "noPhasesFound": "找不到任何階段", + "no": "無", + "found": "找到", + "deletePhase": "刪除階段", + "deletePhaseConfirm": "確定要刪除此階段嗎?此操作無法復原。", + "rename": "重新命名", + "delete": "刪除", + "create": "建立", + "cancel": "取消", + "selectColor": "選擇顏色", + "managePhases": "管理階段", + "close": "關閉" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-drawer.json b/worklenz-frontend/public/locales/zh_tw/project-drawer.json new file mode 100644 index 000000000..197c97d95 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-drawer.json @@ -0,0 +1,52 @@ +{ + "createProject": "建立專案", + "editProject": "編輯專案", + "enterCategoryName": "請輸入類別名稱", + "hitEnterToCreate": "按 Enter 鍵即可建立!", + "enterNotes": "備註", + "youCanManageClientsUnderSettings": "可在 [設定] 中管理客戶", + "addCategory": "為專案新增類別", + "newCategory": "新類別", + "notes": "備註", + "startDate": "開始日期", + "endDate": "結束日期", + "estimateWorkingDays": "估計工作天數", + "estimateManDays": "估計人天數", + "hoursPerDay": "每日時數", + "create": "建立", + "update": "更新", + "delete": "刪除", + "typeToSearchClients": "輸入以搜尋客戶", + "projectColor": "專案顏色", + "pleaseEnterAName": "請輸入名稱", + "enterProjectName": "請輸入專案名稱", + "name": "名稱", + "status": "狀態", + "health": "健康度", + "category": "類別", + "projectManager": "專案經理", + "client": "客戶", + "deleteConfirmation": "確定要刪除嗎?", + "deleteConfirmationDescription": "這將移除所有相關資料,且無法復原。", + "yes": "是", + "no": "否", + "createdAt": "建立時間", + "updatedAt": "更新時間", + "by": "由", + "add": "新增", + "asClient": "作為客戶", + "createClient": "建立客戶", + "searchInputPlaceholder": "依名稱或電子郵件搜尋", + "hoursPerDayValidationMessage": "每日時數必須為 1 到 24 之間的數字", + "workingDaysValidationMessage": "工作天數必須為正整數", + "manDaysValidationMessage": "人天數必須為正整數", + "noPermission": "沒有權限", + "progressSettings": "進度設定", + "manualProgress": "手動進度", + "manualProgressTooltip": "允許對無子任務的任務手動更新進度", + "weightedProgress": "加權進度", + "weightedProgressTooltip": "依據子任務權重計算進度", + "timeProgress": "時間進度", + "timeProgressTooltip": "依據估計時間計算進度", + "enterProjectKey": "請輸入專案代號" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view-files.json b/worklenz-frontend/public/locales/zh_tw/project-view-files.json new file mode 100644 index 000000000..d6706bce5 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view-files.json @@ -0,0 +1,14 @@ +{ + "nameColumn": "名稱", + "attachedTaskColumn": "關聯任務", + "sizeColumn": "大小", + "uploadedByColumn": "上傳者", + "uploadedAtColumn": "上傳時間", + "fileIconAlt": "檔案圖示", + "titleDescriptionText": "此專案中所有任務的附件都會顯示在這裡。", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + "segmentedTooltip": "即將推出!可切換清單檢視與縮圖檢視。", + "emptyText": "此專案中沒有任何附件。" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view-insights.json b/worklenz-frontend/public/locales/zh_tw/project-view-insights.json new file mode 100644 index 000000000..b2a774429 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view-insights.json @@ -0,0 +1,41 @@ +{ + "overview": { + "title": "總覽", + "statusOverview": "狀態總覽", + "priorityOverview": "優先順序總覽", + "lastUpdatedTasks": "最近更新的任務" + }, + "members": { + "title": "成員", + "tooltip": "成員", + "tasksByMembers": "各成員任務", + "tasksByMembersTooltip": "各成員任務", + "name": "名稱", + "taskCount": "任務數量", + "contribution": "貢獻度", + "completed": "已完成", + "incomplete": "未完成", + "overdue": "逾期", + "progress": "進度" + }, + "tasks": { + "overdueTasks": "逾期任務", + "overLoggedTasks": "超時記錄的任務", + "tasksCompletedEarly": "提前完成的任務", + "tasksCompletedLate": "延遲完成的任務", + "overLoggedTasksTooltip": "已記錄的時間超過估計時間的任務", + "overdueTasksTooltip": "已超過到期日的任務" + }, + "common": { + "seeAll": "檢視全部", + "totalLoggedHours": "總記錄時數", + "totalEstimation": "總估計時數", + "completedTasks": "已完成任務", + "incompleteTasks": "未完成任務", + "overdueTasks": "逾期任務", + "overdueTasksTooltip": "已超過到期日的任務", + "totalLoggedHoursTooltip": "任務的估計時間與已記錄時間。", + "includeArchivedTasks": "包含已封存任務", + "export": "匯出" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view-members.json b/worklenz-frontend/public/locales/zh_tw/project-view-members.json new file mode 100644 index 000000000..478a2a0dc --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view-members.json @@ -0,0 +1,18 @@ +{ + "nameColumn": "名稱", + "jobTitleColumn": "職稱", + "emailColumn": "電子郵件", + "tasksColumn": "任務", + "taskProgressColumn": "任務進度", + "accessColumn": "存取權限", + "fileIconAlt": "檔案圖示", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + "refreshButtonTooltip": "重新整理成員", + "deleteButtonTooltip": "從專案中移除", + "memberCount": "位成員", + "membersCountPlural": "位成員", + "emptyText": "此專案中沒有任何成員。", + "searchPlaceholder": "搜尋成員" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view-updates.json b/worklenz-frontend/public/locales/zh_tw/project-view-updates.json new file mode 100644 index 000000000..df26ba2e8 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view-updates.json @@ -0,0 +1,6 @@ +{ + "inputPlaceholder": "新增留言…", + "addButton": "新增", + "cancelButton": "取消", + "deleteButton": "刪除" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view.json b/worklenz-frontend/public/locales/zh_tw/project-view.json new file mode 100644 index 000000000..f8d82f28b --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view.json @@ -0,0 +1,14 @@ +{ + "taskList": "任務清單", + "board": "看板", + "insights": "分析", + "files": "檔案", + "members": "成員", + "updates": "動態", + "projectView": "專案檢視", + "loading": "正在載入專案…", + "error": "載入專案時發生錯誤", + "pinnedTab": "已釘選為預設分頁", + "pinTab": "釘選為預設分頁", + "unpinTab": "取消釘選預設分頁" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view/import-task-templates.json b/worklenz-frontend/public/locales/zh_tw/project-view/import-task-templates.json new file mode 100644 index 000000000..4408341d4 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view/import-task-templates.json @@ -0,0 +1,11 @@ +{ + "importTaskTemplate": "匯入任務範本", + "templateName": "範本名稱", + "templateDescription": "範本描述", + "selectedTasks": "已選取任務", + "tasks": "任務", + "templates": "範本", + "remove": "移除", + "cancel": "取消", + "import": "匯入" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view/project-member-drawer.json b/worklenz-frontend/public/locales/zh_tw/project-view/project-member-drawer.json new file mode 100644 index 000000000..99c450e97 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view/project-member-drawer.json @@ -0,0 +1,11 @@ +{ + "title": "分享專案", + "searchLabel": "輸入名稱或電子郵件以新增成員", + "searchPlaceholder": "請輸入名稱或電子郵件", + "inviteAsAMember": "以成員身分邀請", + "inviteNewMemberByEmail": "以電子郵件邀請新成員", + "members": "成員", + "copyProjectLink": "複製專案連結", + "inviteMember": "邀請成員", + "alsoInviteToProject": "同時邀請至專案" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view/project-view-header.json b/worklenz-frontend/public/locales/zh_tw/project-view/project-view-header.json new file mode 100644 index 000000000..26e99ffd4 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view/project-view-header.json @@ -0,0 +1,31 @@ +{ + "importTasks": "匯入任務", + "importTask": "匯入任務", + "createTask": "建立任務", + "settings": "設定", + "subscribe": "訂閱", + "unsubscribe": "取消訂閱", + "deleteProject": "刪除專案", + "startDate": "開始日期", + "endDate": "結束日期", + "projectSettings": "專案設定", + "projectSummary": "專案摘要", + "receiveProjectSummary": "每天傍晚接收專案摘要。", + "refreshProject": "重新整理專案", + "saveAsTemplate": "儲存為範本", + "invite": "邀請", + "share": "分享", + "subscribeTooltip": "訂閱專案通知", + "unsubscribeTooltip": "取消訂閱專案通知", + "refreshTooltip": "重新整理專案資料", + "settingsTooltip": "開啟專案設定", + "saveAsTemplateTooltip": "將此專案儲存為範本", + "inviteTooltip": "邀請團隊成員加入此專案", + "createTaskTooltip": "建立新任務", + "importTaskTooltip": "從範本匯入任務", + "navigateBackTooltip": "回到專案列表", + "projectStatusTooltip": "專案狀態", + "projectDatesInfo": "專案時程資訊", + "projectCategoryTooltip": "專案類別", + "defaultTaskName": "未命名任務" +} diff --git a/worklenz-frontend/public/locales/zh_tw/project-view/save-as-template.json b/worklenz-frontend/public/locales/zh_tw/project-view/save-as-template.json new file mode 100644 index 000000000..eb4c097ac --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/project-view/save-as-template.json @@ -0,0 +1,27 @@ +{ + "title": "儲存為範本", + "templateName": "範本名稱", + "includes": "應將專案中的哪些內容納入範本?", + "includesOptions": { + "statuses": "狀態", + "phases": "階段", + "labels": "標籤" + }, + "taskIncludes": "應將任務中的哪些內容納入範本?", + "taskIncludesOptions": { + "statuses": "狀態", + "phases": "階段", + "labels": "標籤", + "name": "名稱", + "priority": "優先順序", + "status": "狀態", + "phase": "階段", + "label": "標籤", + "timeEstimate": "時間估計", + "description": "描述", + "subTasks": "子任務" + }, + "cancel": "取消", + "save": "儲存", + "templateNamePlaceholder": "請輸入範本名稱" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-members-drawer.json b/worklenz-frontend/public/locales/zh_tw/reporting-members-drawer.json new file mode 100644 index 000000000..47016f1f7 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-members-drawer.json @@ -0,0 +1,90 @@ +{ + "exportButton": "匯出", + "timeLogsButton": "時間記錄", + "activityLogsButton": "活動記錄", + "tasksButton": "任務", + "searchByNameInputPlaceholder": "依名稱搜尋", + + "overviewTab": "總覽", + "timeLogsTab": "時間記錄", + "activityLogsTab": "活動記錄", + "tasksTab": "任務", + + "projectsText": "專案", + "totalTasksText": "總任務數", + "assignedTasksText": "已指派任務", + "completedTasksText": "已完成任務", + "ongoingTasksText": "進行中任務", + "overdueTasksText": "逾期任務", + "loggedHoursText": "已記錄時數", + + "tasksText": "任務", + "allText": "全部", + + "tasksByProjectsText": "依專案分類的任務", + "tasksByStatusText": "依狀態分類的任務", + "tasksByPriorityText": "依優先順序分類的任務", + + "todoText": "待辦", + "doingText": "進行中", + "doneText": "已完成", + "lowText": "低", + "mediumText": "中", + "highText": "高", + + "billableButton": "可計費", + "billableText": "可計費", + "nonBillableText": "不可計費", + + "timeLogsEmptyPlaceholder": "沒有可顯示的時間記錄", + "loggedText": "記錄了", + "forText": "用於", + "inText": "在", + "updatedText": "已更新", + "fromText": "從", + "toText": "至", + "withinText": "在…期間", + + "activityLogsEmptyPlaceholder": "沒有可顯示的活動記錄", + + "filterByText": "篩選條件:", + "selectProjectPlaceholder": "選擇專案", + + "taskColumn": "任務", + "nameColumn": "名稱", + "projectColumn": "專案", + "statusColumn": "狀態", + "priorityColumn": "優先順序", + "dueDateColumn": "到期日", + "completedDateColumn": "完成日期", + "estimatedTimeColumn": "估計時間", + "loggedTimeColumn": "已記錄時間", + "overloggedTimeColumn": "超時記錄", + "daysLeftColumn": "剩餘天數 / 逾期天數", + "startDateColumn": "開始日期", + "endDateColumn": "結束日期", + "actualTimeColumn": "實際時間", + "projectHealthColumn": "專案健康度", + "categoryColumn": "類別", + "projectManagerColumn": "專案經理", + + "tasksStatsOverviewDrawerTitle": " 的任務", + "projectsStatsOverviewDrawerTitle": " 的專案", + + "cancelledText": "已取消", + "blockedText": "受阻", + "onHoldText": "暫停", + "proposedText": "提議中", + "inPlanningText": "規劃中", + "inProgressText": "進行中", + "completedText": "已完成", + "continuousText": "持續進行", + + "daysLeftText": "天剩餘", + "daysOverdueText": "天逾期", + + "notSetText": "未設定", + "needsAttentionText": "需要關注", + "atRiskText": "有風險", + "goodText": "良好" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-members.json b/worklenz-frontend/public/locales/zh_tw/reporting-members.json new file mode 100644 index 000000000..d02d84a25 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-members.json @@ -0,0 +1,35 @@ +{ + "yesterdayText": "昨天", + "lastSevenDaysText": "過去 7 天", + "lastWeekText": "上週", + "lastThirtyDaysText": "過去 30 天", + "lastMonthText": "上個月", + "lastThreeMonthsText": "過去 3 個月", + "allTimeText": "所有時間", + "customRangeText": "自訂範圍", + "startDateInputPlaceholder": "開始日期", + "EndDateInputPlaceholder": "結束日期", + "filterButton": "篩選", + + "membersTitle": "成員", + "includeArchivedButton": "包含已封存專案", + "exportButton": "匯出", + "excelButton": "Excel", + "searchByNameInputPlaceholder": "依名稱搜尋", + + "memberColumn": "成員", + "tasksProgressColumn": "任務進度", + "tasksAssignedColumn": "已指派任務", + "completedTasksColumn": "已完成任務", + "overdueTasksColumn": "逾期任務", + "ongoingTasksColumn": "進行中任務", + + "tasksAssignedColumnTooltip": "在所選日期範圍內指派的任務", + "overdueTasksColumnTooltip": "在所選日期範圍結束時逾期的任務", + "completedTasksColumnTooltip": "在所選日期範圍內完成的任務", + "ongoingTasksColumnTooltip": "已開始但尚未完成的任務", + + "todoText": "待辦", + "doingText": "進行中", + "doneText": "已完成" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-overview-drawer.json b/worklenz-frontend/public/locales/zh_tw/reporting-overview-drawer.json new file mode 100644 index 000000000..6ea5350bb --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-overview-drawer.json @@ -0,0 +1,39 @@ +{ + "exportButton": "匯出", + "projectsButton": "專案", + "membersButton": "成員", + "searchByNameInputPlaceholder": "依名稱搜尋", + + "overviewTab": "總覽", + "projectsTab": "專案", + "membersTab": "成員", + + "projectsByStatusText": "依狀態分類的專案", + "projectsByCategoryText": "依類別分類的專案", + "projectsByHealthText": "依健康度分類的專案", + + "projectsText": "專案", + "allText": "全部", + + "cancelledText": "已取消", + "blockedText": "受阻", + "onHoldText": "暫停", + "proposedText": "提議中", + "inPlanningText": "規劃中", + "inProgressText": "進行中", + "completedText": "已完成", + "continuousText": "持續進行", + + "notSetText": "未設定", + "needsAttentionText": "需要關注", + "atRiskText": "有風險", + "goodText": "良好", + + "nameColumn": "名稱", + "emailColumn": "電子郵件", + "projectsColumn": "專案", + "tasksColumn": "任務", + "overdueTasksColumn": "逾期任務", + "completedTasksColumn": "已完成任務", + "ongoingTasksColumn": "進行中任務" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-overview.json b/worklenz-frontend/public/locales/zh_tw/reporting-overview.json new file mode 100644 index 000000000..4ed9efd84 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-overview.json @@ -0,0 +1,25 @@ +{ + "overviewTitle": "總覽", + "includeArchivedButton": "包含已封存專案", + + "teamCount": "個團隊", + "teamCountPlural": "個團隊", + "projectCount": "個專案", + "projectCountPlural": "個專案", + "memberCount": "位成員", + "memberCountPlural": "位成員", + "activeProjectCount": "個進行中專案", + "activeProjectCountPlural": "個進行中專案", + "overdueProjectCount": "個逾期專案", + "overdueProjectCountPlural": "個逾期專案", + "unassignedMemberCount": "位未指派成員", + "unassignedMemberCountPlural": "位未指派成員", + "memberWithOverdueTaskCount": "位成員有逾期任務", + "memberWithOverdueTaskCountPlural": "位成員有逾期任務", + + "teamsText": "團隊", + + "nameColumn": "名稱", + "projectsColumn": "專案", + "membersColumn": "成員" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-projects-drawer.json b/worklenz-frontend/public/locales/zh_tw/reporting-projects-drawer.json new file mode 100644 index 000000000..e81bfe7ad --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-projects-drawer.json @@ -0,0 +1,59 @@ +{ + "exportButton": "匯出", + "membersButton": "成員", + "tasksButton": "任務", + "searchByNameInputPlaceholder": "依名稱搜尋", + + "overviewTab": "總覽", + "membersTab": "成員", + "tasksTab": "任務", + + "completedTasksText": "已完成任務", + "incompleteTasksText": "未完成任務", + "overdueTasksText": "逾期任務", + "allocatedHoursText": "分配時數", + "loggedHoursText": "已記錄時數", + + "tasksText": "任務", + "allText": "全部", + + "tasksByStatusText": "依狀態分類的任務", + "tasksByPriorityText": "依優先順序分類的任務", + "tasksByDueDateText": "依到期日分類的任務", + + "todoText": "待辦", + "doingText": "進行中", + "doneText": "已完成", + "lowText": "低", + "mediumText": "中", + "highText": "高", + "completedText": "已完成", + "upcomingText": "即將到期", + "overdueText": "逾期", + "noDueDateText": "無到期日", + + "nameColumn": "名稱", + "tasksCountColumn": "任務數量", + "completedTasksColumn": "已完成任務", + "incompleteTasksColumn": "未完成任務", + "overdueTasksColumn": "逾期任務", + "contributionColumn": "貢獻度", + "progressColumn": "進度", + "loggedTimeColumn": "已記錄時間", + "taskColumn": "任務", + "projectColumn": "專案", + "statusColumn": "狀態", + "priorityColumn": "優先順序", + "phaseColumn": "階段", + "dueDateColumn": "到期日", + "completedDateColumn": "完成日期", + "estimatedTimeColumn": "估計時間", + "overloggedTimeColumn": "超時記錄", + "completedOnColumn": "完成於", + "daysOverdueColumn": "逾期天數", + + "groupByText": "群組依據:", + "statusText": "狀態", + "priorityText": "優先順序", + "phaseText": "階段" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-projects-filters.json b/worklenz-frontend/public/locales/zh_tw/reporting-projects-filters.json new file mode 100644 index 000000000..305da46f5 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-projects-filters.json @@ -0,0 +1,35 @@ +{ + "searchByNamePlaceholder": "依名稱搜尋", + "searchByCategoryPlaceholder": "依類別搜尋", + + "statusText": "狀態", + "healthText": "健康度", + "categoryText": "類別", + "projectManagerText": "專案經理", + "showFieldsText": "顯示欄位", + + "cancelledText": "已取消", + "blockedText": "受阻", + "onHoldText": "暫停", + "proposedText": "提議中", + "inPlanningText": "規劃中", + "inProgressText": "進行中", + "completedText": "已完成", + "continuousText": "持續進行", + + "notSetText": "未設定", + "needsAttentionText": "需要關注", + "atRiskText": "有風險", + "goodText": "良好", + + "nameText": "專案", + "estimatedVsActualText": "估計與實際", + "tasksProgressText": "任務進度", + "lastActivityText": "最近活動", + "datesText": "開始 / 結束日期", + "daysLeftText": "剩餘天數 / 逾期天數", + "projectHealthText": "專案健康度", + "projectUpdateText": "專案動態", + "clientText": "客戶", + "teamText": "團隊" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-projects.json b/worklenz-frontend/public/locales/zh_tw/reporting-projects.json new file mode 100644 index 000000000..6f008f253 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-projects.json @@ -0,0 +1,52 @@ +{ + "projectCount": "個專案", + "projectCountPlural": "個專案", + "includeArchivedButton": "包含已封存專案", + "exportButton": "匯出", + "excelButton": "Excel", + + "projectColumn": "專案", + "estimatedVsActualColumn": "估計與實際", + "tasksProgressColumn": "任務進度", + "lastActivityColumn": "最近活動", + "statusColumn": "狀態", + "datesColumn": "開始 / 結束日期", + "daysLeftColumn": "剩餘天數 / 逾期天數", + "projectHealthColumn": "專案健康度", + "categoryColumn": "類別", + "projectUpdateColumn": "專案動態", + "clientColumn": "客戶", + "teamColumn": "團隊", + "projectManagerColumn": "專案經理", + + "openButton": "開啟", + + "estimatedText": "估計", + "actualText": "實際", + + "todoText": "待辦", + "doingText": "進行中", + "doneText": "已完成", + + "cancelledText": "已取消", + "blockedText": "受阻", + "onHoldText": "暫停", + "proposedText": "提議中", + "inPlanningText": "規劃中", + "inProgressText": "進行中", + "completedText": "已完成", + "continuousText": "持續進行", + + "daysLeftText": "天剩餘", + "dayLeftText": "天剩餘", + "daysOverdueText": "天逾期", + + "notSetText": "未設定", + "needsAttentionText": "需要關注", + "atRiskText": "有風險", + "goodText": "良好", + + "setCategoryText": "設定類別", + "searchByNameInputPlaceholder": "依名稱搜尋", + "todayText": "今天" +} diff --git a/worklenz-frontend/public/locales/zh_tw/reporting-sidebar.json b/worklenz-frontend/public/locales/zh_tw/reporting-sidebar.json new file mode 100644 index 000000000..5d5ecd451 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/reporting-sidebar.json @@ -0,0 +1,8 @@ +{ + "overview": "總覽", + "projects": "專案", + "members": "成員", + "timeReports": "時間報表", + "estimateVsActual": "估計與實際", + "currentOrganizationTooltip": "目前組織" +} diff --git a/worklenz-frontend/public/locales/zh_tw/schedule.json b/worklenz-frontend/public/locales/zh_tw/schedule.json new file mode 100644 index 000000000..e70b734cc --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/schedule.json @@ -0,0 +1,39 @@ +{ + "today": "今天", + "week": "本週", + "month": "本月", + + "settings": "設定", + "workingDays": "工作天", + "monday": "星期一", + "tuesday": "星期二", + "wednesday": "星期三", + "thursday": "星期四", + "friday": "星期五", + "saturday": "星期六", + "sunday": "星期日", + "workingHours": "工作時數", + "hours": "小時", + "saveButton": "儲存", + + "totalAllocation": "總分配量", + "timeLogged": "已記錄時間", + "remainingTime": "剩餘時間", + "total": "合計", + "perDay": "每天", + "tasks": "項任務", + "startDate": "開始日期", + "endDate": "結束日期", + + "hoursPerDay": "每日時數", + "totalHours": "總時數", + "deleteButton": "刪除", + "cancelButton": "取消", + + "tabTitle": "無開始和結束日期的任務", + + "allocatedTime": "分配時間", + "totalLogged": "總記錄時間", + "loggedBillable": "已記錄可計費", + "loggedNonBillable": "已記錄不可計費" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/account-deletion.json b/worklenz-frontend/public/locales/zh_tw/settings/account-deletion.json new file mode 100644 index 000000000..cccf50cac --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/account-deletion.json @@ -0,0 +1,29 @@ +{ + "title": "刪除帳戶", + "dangerZone": "危險區域", + "warningTitle": "警告:此操作無法復原", + "warningDescription1": "刪除帳戶將會:", + "warningPoint1": "永久刪除所有個人資料", + "warningPoint2": "從所有團隊和專案中移除您", + "warningPoint3": "刪除所有您建立的內容和貢獻", + "warningPoint4": "取消所有有效的訂閱方案", + "warningDescription2": "提出請求後,所有資料將在 30 天內永久刪除。", + "beforeDeletion": "刪除帳戶前,請先:", + "exportData": "匯出所有您想保留的重要資料", + "cancelSubscription": "確認所有訂閱方案已取消", + "informTeam": "通知團隊成員您即將離開", + "deleteAccountButton": "刪除我的帳戶", + "confirmDeletionTitle": "確認刪除帳戶", + "finalWarning": "此操作為永久性,且無法復原!", + "confirmationInstructions": "請精確輸入下方文字以確認刪除:", + "typeDeleteToConfirm": "請輸入 DELETE 以確認", + "confirmDelete": "是的,刪除我的帳戶", + "cancel": "取消", + "invalidConfirmation": "請輸入 DELETE 以確認", + "deletionRequestSuccess": "帳戶刪除請求已提交成功", + "requestSubmitted": "請求已提交", + "deletionConfirmationMessage": "已收到帳戶刪除請求,帳戶及所有相關資料將在 30 天內永久刪除。系統即將為您登出。", + "deletionRequestFailed": "帳戶刪除請求提交失敗,請再試一次。", + "dataRetentionNotice": "資料保留聲明", + "dataRetentionDescription": "根據資料保留政策,帳戶及所有相關資料將在 30 天內永久刪除。此程序一旦啟動即無法復原。" +} \ No newline at end of file diff --git a/worklenz-frontend/public/locales/zh_tw/settings/appearance.json b/worklenz-frontend/public/locales/zh_tw/settings/appearance.json new file mode 100644 index 000000000..689f94512 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/appearance.json @@ -0,0 +1,5 @@ +{ + "title": "外觀", + "darkMode": "深色模式", + "darkModeDescription": "切換淺色和深色模式,自訂您的瀏覽體驗。" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/categories.json b/worklenz-frontend/public/locales/zh_tw/settings/categories.json new file mode 100644 index 000000000..40421be0b --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/categories.json @@ -0,0 +1,10 @@ +{ + "categoryColumn": "類別", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + "associatedTaskColumn": "關聯專案", + "searchPlaceholder": "依名稱搜尋", + "emptyText": "類別可在更新或建立專案時一併建立。", + "colorChangeTooltip": "按一下以變更顏色" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/change-password.json b/worklenz-frontend/public/locales/zh_tw/settings/change-password.json new file mode 100644 index 000000000..b237a5897 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/change-password.json @@ -0,0 +1,15 @@ +{ + "title": "變更密碼", + "currentPassword": "目前密碼", + "newPassword": "新密碼", + "confirmPassword": "確認密碼", + "currentPasswordPlaceholder": "請輸入目前的密碼", + "newPasswordPlaceholder": "新密碼", + "confirmPasswordPlaceholder": "確認密碼", + "currentPasswordRequired": "請輸入目前的密碼!", + "newPasswordRequired": "請輸入新密碼!", + "passwordValidationError": "密碼至少需 8 個字元,並包含大寫字母、數字及符號。", + "passwordMismatch": "兩次輸入的密碼不一致!", + "passwordRequirements": "新密碼至少需 8 個字元,並包含大寫字母、數字及符號。", + "updateButton": "更新密碼" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/clients.json b/worklenz-frontend/public/locales/zh_tw/settings/clients.json new file mode 100644 index 000000000..6f8d5021c --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/clients.json @@ -0,0 +1,22 @@ +{ + "nameColumn": "名稱", + "projectColumn": "專案", + "noProjectsAvailable": "沒有可用的專案", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + "searchPlaceholder": "依名稱搜尋", + "createClient": "建立客戶", + "pinTooltip": "按一下以釘選至主選單", + "createClientDrawerTitle": "建立客戶", + "updateClientDrawerTitle": "更新客戶", + "nameLabel": "名稱", + "namePlaceholder": "名稱", + "nameRequiredError": "請輸入名稱", + "createButton": "建立", + "updateButton": "更新", + "createClientSuccessMessage": "客戶建立成功!", + "createClientErrorMessage": "客戶建立失敗!", + "updateClientSuccessMessage": "客戶更新成功!", + "updateClientErrorMessage": "客戶更新失敗!" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/job-titles.json b/worklenz-frontend/public/locales/zh_tw/settings/job-titles.json new file mode 100644 index 000000000..75ed5d764 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/job-titles.json @@ -0,0 +1,20 @@ +{ + "nameColumn": "名稱", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + "searchPlaceholder": "依名稱搜尋", + "createJobTitleButton": "建立職稱", + "pinTooltip": "按一下以釘選至主選單", + "createJobTitleDrawerTitle": "建立職稱", + "updateJobTitleDrawerTitle": "更新職稱", + "nameLabel": "名稱", + "namePlaceholder": "名稱", + "nameRequiredError": "請輸入名稱", + "createButton": "建立", + "updateButton": "更新", + "createJobTitleSuccessMessage": "職稱建立成功!", + "createJobTitleErrorMessage": "職稱建立失敗!", + "updateJobTitleSuccessMessage": "職稱更新成功!", + "updateJobTitleErrorMessage": "職稱更新失敗!" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/labels.json b/worklenz-frontend/public/locales/zh_tw/settings/labels.json new file mode 100644 index 000000000..0ebcd6cf5 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/labels.json @@ -0,0 +1,15 @@ +{ + "labelColumn": "標籤", + "deleteConfirmationTitle": "確定要執行嗎?", + "deleteConfirmationOk": "是", + "deleteConfirmationCancel": "取消", + "associatedTaskColumn": "關聯任務數量", + "searchPlaceholder": "依名稱搜尋", + "emptyText": "標籤可在更新或建立任務時一併建立。", + "pinTooltip": "按一下以釘選至主選單", + "colorChangeTooltip": "按一下以變更顏色", + "pageTitle": "管理標籤", + "deleteConfirmTitle": "確定要刪除嗎?", + "deleteButton": "刪除", + "cancelButton": "取消" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/language.json b/worklenz-frontend/public/locales/zh_tw/settings/language.json new file mode 100644 index 000000000..1a3b447c9 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/language.json @@ -0,0 +1,7 @@ +{ + "language": "語言", + "language_required": "語言為必填", + "time_zone": "時區", + "time_zone_required": "時區為必填", + "save_changes": "儲存變更" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/notifications.json b/worklenz-frontend/public/locales/zh_tw/settings/notifications.json new file mode 100644 index 000000000..433b191f3 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/notifications.json @@ -0,0 +1,11 @@ +{ + "title": "通知設定", + "emailTitle": "寄送電子郵件通知給我", + "emailDescription": "包含新的任務指派通知", + "dailyDigestTitle": "寄送每日摘要給我", + "dailyDigestDescription": "每天傍晚會收到任務近期活動的摘要。", + "popupTitle": "Worklenz 開啟時在電腦上顯示彈出通知", + "popupDescription": "瀏覽器可能會停用彈出通知,請變更瀏覽器設定以允許顯示。", + "unreadItemsTitle": "顯示未讀數量", + "unreadItemsDescription": "會看到每則通知的計數。" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/profile.json b/worklenz-frontend/public/locales/zh_tw/settings/profile.json new file mode 100644 index 000000000..db7fbbd73 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/profile.json @@ -0,0 +1,14 @@ +{ + "uploadError": "僅可上傳 JPG/PNG 格式的檔案!", + "uploadSizeError": "圖片大小不可超過 2MB!", + "upload": "上傳", + "nameLabel": "名稱", + "nameRequiredError": "名稱為必填", + "emailLabel": "電子郵件", + "emailRequiredError": "電子郵件為必填", + "saveChanges": "儲存變更", + "profileJoinedText": "加入日期:{{date}}", + "profileLastUpdatedText": "最後更新:{{date}}", + "avatarTooltip": "按一下以上傳大頭照", + "title": "個人資料設定" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/project-templates.json b/worklenz-frontend/public/locales/zh_tw/settings/project-templates.json new file mode 100644 index 000000000..151905849 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/project-templates.json @@ -0,0 +1,8 @@ +{ + "nameColumn": "名稱", + "editToolTip": "編輯", + "deleteToolTip": "刪除", + "confirmText": "確定要執行嗎?", + "okText": "是", + "cancelText": "取消" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/sidebar.json b/worklenz-frontend/public/locales/zh_tw/settings/sidebar.json new file mode 100644 index 000000000..bc83ed1d4 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/sidebar.json @@ -0,0 +1,16 @@ +{ + "profile": "個人資料", + "notifications": "通知", + "clients": "客戶", + "job-titles": "職稱", + "labels": "標籤", + "categories": "類別", + "project-templates": "專案範本", + "task-templates": "任務範本", + "team-members": "團隊成員", + "teams": "團隊", + "change-password": "變更密碼", + "language-and-region": "語言與地區", + "appearance": "外觀", + "account-deletion": "刪除帳戶" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/task-templates.json b/worklenz-frontend/public/locales/zh_tw/settings/task-templates.json new file mode 100644 index 000000000..9fc6a18fc --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/task-templates.json @@ -0,0 +1,9 @@ +{ + "nameColumn": "名稱", + "createdColumn": "建立時間", + "editToolTip": "編輯", + "deleteToolTip": "刪除", + "confirmText": "確定要執行嗎?", + "okText": "是", + "cancelText": "取消" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/team-members.json b/worklenz-frontend/public/locales/zh_tw/settings/team-members.json new file mode 100644 index 000000000..d96da9823 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/team-members.json @@ -0,0 +1,48 @@ +{ + "title": "團隊成員", + "nameColumn": "名稱", + "projectsColumn": "專案", + "emailColumn": "電子郵件", + "teamAccessColumn": "團隊權限", + "memberCount": "位成員", + "membersCountPlural": "位成員", + "searchPlaceholder": "依名稱搜尋成員", + "pinTooltip": "重新整理成員清單", + "addMemberButton": "新增成員", + "editTooltip": "編輯成員", + "deactivateTooltip": "停用成員", + "activateTooltip": "啟用成員", + "deleteTooltip": "刪除成員", + "confirmDeleteTitle": "確定要刪除此成員嗎?", + "confirmActivateTitle": "確定要變更此成員的狀態嗎?", + "okText": "是,繼續", + "cancelText": "否,取消", + "deactivatedText": "(目前已停用)", + "pendingInvitationText": "(邀請待接受)", + "addMemberDrawerTitle": "邀請團隊成員", + "updateMemberDrawerTitle": "更新團隊成員", + "addMemberEmailHint": "無論邀請是否接受,成員都會被加入團隊", + "memberEmailLabel": "電子郵件", + "memberEmailPlaceholder": "請輸入團隊成員的電子郵件地址", + "memberEmailRequiredError": "請輸入有效的電子郵件地址", + "jobTitleLabel": "職稱", + "jobTitlePlaceholder": "選擇或搜尋職稱(選填)", + "memberAccessLabel": "存取權限等級", + "addToTeamButton": "送出邀請", + "updateButton": "儲存變更", + "resendInvitationButton": "重新寄送邀請信件", + "invitationSentSuccessMessage": "團隊邀請已成功寄出!", + "createMemberSuccessMessage": "新團隊成員新增成功!", + "createMemberErrorMessage": "新增團隊成員失敗,請再試一次。", + "updateMemberSuccessMessage": "團隊成員更新成功!", + "updateMemberErrorMessage": "更新團隊成員失敗,請再試一次。", + "memberText": "成員", + "adminText": "管理員", + "ownerText": "團隊擁有者", + "addedText": "已新增", + "updatedText": "已更新", + "noResultFound": "請輸入電子郵件地址並按 Enter 鍵…", + "jobTitlesFetchError": "載入職稱失敗", + "invitationResent": "邀請已重新寄出!", + "copyTeamLink": "複製團隊連結" +} diff --git a/worklenz-frontend/public/locales/zh_tw/settings/teams.json b/worklenz-frontend/public/locales/zh_tw/settings/teams.json new file mode 100644 index 000000000..60cd49ceb --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/settings/teams.json @@ -0,0 +1,16 @@ +{ + "title": "團隊", + "team": "團隊", + "teams": "團隊", + "name": "名稱", + "created": "建立時間", + "ownsBy": "擁有者", + "edit": "編輯", + "editTeam": "編輯團隊", + "pinTooltip": "按一下以釘選至主選單", + "editTeamName": "編輯團隊名稱", + "updateName": "更新名稱", + "namePlaceholder": "名稱", + "nameRequired": "請輸入名稱", + "updateFailed": "團隊名稱變更失敗!" +} diff --git a/worklenz-frontend/public/locales/zh_tw/survey.json b/worklenz-frontend/public/locales/zh_tw/survey.json new file mode 100644 index 000000000..aa826ec5e --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/survey.json @@ -0,0 +1,14 @@ +{ + "modalTitle": "協助我們改善您的使用體驗", + "skip": "暫時略過", + "previous": "上一步", + "next": "下一步", + "completeSurvey": "完成問卷", + "submitting": "正在提交回覆…", + "submitSuccessTitle": "感謝您!", + "submitSuccessSubtitle": "您的意見回饋有助於我們為所有人改善 Worklenz。", + "submitSuccessMessage": "感謝您完成問卷調查!", + "submitErrorMessage": "問卷提交失敗,請再試一次。", + "submitErrorLog": "問卷提交失敗", + "fetchErrorLog": "問卷載入失敗" +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-info-tab.json b/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-info-tab.json new file mode 100644 index 000000000..7b2d6d2b6 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-info-tab.json @@ -0,0 +1,30 @@ +{ + "details": { + "task-key": "任務編號", + "phase": "階段", + "assignees": "指派對象", + "due-date": "到期日", + "time-estimation": "時間估計", + "priority": "優先順序", + "labels": "標籤", + "billable": "可計費", + "notify": "通知", + "when-done-notify": "完成時通知", + "start-date": "開始日期", + "end-date": "結束日期", + "hide-start-date": "隱藏開始日期", + "show-start-date": "顯示開始日期", + "hours": "小時", + "minutes": "分鐘", + "recurring": "週期性" + }, + "description": { + "title": "描述", + "placeholder": "新增更詳細的描述…" + }, + "subTasks": { + "title": "子任務", + "add-sub-task": "新增子任務", + "refresh-sub-tasks": "重新整理子任務" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-recurring-config.json b/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-recurring-config.json new file mode 100644 index 000000000..709e681cd --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer-recurring-config.json @@ -0,0 +1,34 @@ +{ + "recurring": "週期性", + "recurringTaskConfiguration": "週期性任務設定", + "repeats": "重複", + "daily": "每日", + "weekly": "每週", + "everyXDays": "每 X 天", + "everyXWeeks": "每 X 週", + "everyXMonths": "每 X 個月", + "monthly": "每月", + "selectDaysOfWeek": "選擇星期幾", + "mon": "一", + "tue": "二", + "wed": "三", + "thu": "四", + "fri": "五", + "sat": "六", + "sun": "日", + "monthlyRepeatType": "每月重複方式", + "onSpecificDate": "在特定日期", + "onSpecificDay": "在特定星期", + "dateOfMonth": "月份中的日期", + "weekOfMonth": "月份中的週數", + "dayOfWeek": "星期幾", + "first": "第一", + "second": "第二", + "third": "第三", + "fourth": "第四", + "last": "最後", + "intervalDays": "間隔(天)", + "intervalWeeks": "間隔(週)", + "intervalMonths": "間隔(月)", + "saveChanges": "儲存變更" +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json b/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json new file mode 100644 index 000000000..d5a5556da --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-drawer/task-drawer.json @@ -0,0 +1,129 @@ +{ + "taskHeader": { + "taskNamePlaceholder": "請輸入任務名稱", + "deleteTask": "刪除任務", + "parentTask": "父任務", + "currentTask": "目前任務", + "back": "回到上一頁", + "backToParent": "回到父任務", + "toParentTask": "到父任務", + "loadingHierarchy": "正在載入層級結構…" + }, + "taskInfoTab": { + "title": "資訊", + "details": { + "title": "詳細資料", + "task-key": "任務編號", + "phase": "階段", + "assignees": "指派對象", + "due-date": "到期日", + "time-estimation": "時間估計", + "priority": "優先順序", + "labels": "標籤", + "billable": "可計費", + "notify": "通知", + "when-done-notify": "完成時通知", + "start-date": "開始日期", + "end-date": "結束日期", + "hide-start-date": "隱藏開始日期", + "show-start-date": "顯示開始日期", + "hours": "小時", + "minutes": "分鐘", + "progressValue": "進度值", + "progressValueTooltip": "設定進度百分比 (0-100%)", + "progressValueRequired": "請輸入進度值", + "progressValueRange": "進度必須介於 0 到 100 之間", + "taskWeight": "任務權重", + "taskWeightTooltip": "設定此子任務的權重(百分比)", + "taskWeightRequired": "請輸入任務權重", + "taskWeightRange": "權重必須介於 0 到 100 之間", + "recurring": "週期性" + }, + "labels": { + "labelInputPlaceholder": "搜尋或建立", + "labelsSelectorInputTip": "按 Enter 鍵即可建立" + }, + "description": { + "title": "描述", + "placeholder": "新增更詳細的描述…" + }, + "subTasks": { + "title": "子任務", + "addSubTask": "新增子任務", + "addSubTaskInputPlaceholder": "輸入任務名稱並按 Enter 鍵", + "refreshSubTasks": "重新整理子任務", + "edit": "編輯", + "delete": "刪除", + "confirmDeleteSubTask": "確定要刪除此子任務嗎?", + "deleteSubTask": "刪除子任務" + }, + "dependencies": { + "title": "相依任務", + "addDependency": "+ 新增相依任務", + "blockedBy": "被阻擋於", + "searchTask": "輸入以搜尋任務", + "noTasksFound": "找不到任何任務", + "confirmDeleteDependency": "確定要刪除嗎?" + }, + "attachments": { + "title": "附件", + "chooseOrDropFileToUpload": "選擇或拖放檔案以上傳", + "uploading": "上傳中…" + }, + "comments": { + "title": "留言", + "addComment": "+ 新增留言", + "noComments": "尚無留言,搶先留言吧!", + "delete": "刪除", + "confirmDeleteComment": "確定要刪除此留言嗎?", + "addCommentPlaceholder": "新增留言…", + "cancel": "取消", + "commentButton": "發表留言", + "attachFiles": "附加檔案", + "addMoreFiles": "新增更多檔案", + "selectedFiles": "已選取檔案(最大 25MB,最多 {{count}} 個)", + "maxFilesError": "最多只能上傳 {{count}} 個檔案", + "processFilesError": "處理檔案失敗", + "addCommentError": "請新增留言或附加檔案", + "createdBy": "由 {{user}} 於 {{time}} 建立", + "updatedTime": "更新於 {{time}}" + }, + "searchInputPlaceholder": "依名稱搜尋", + "pendingInvitation": "待接受邀請" + }, + "taskTimeLogTab": { + "title": "時間記錄", + "addTimeLog": "新增時間記錄", + "totalLogged": "總記錄時間", + "exportToExcel": "匯出至 Excel", + "noTimeLogsFound": "找不到任何時間記錄", + "timeLogForm": { + "date": "日期", + "startTime": "開始時間", + "endTime": "結束時間", + "workDescription": "工作描述", + "descriptionPlaceholder": "新增描述", + "logTime": "記錄時間", + "updateTime": "更新時間", + "cancel": "取消", + "selectDateError": "請選擇日期", + "selectStartTimeError": "請選擇開始時間", + "selectEndTimeError": "請選擇結束時間", + "endTimeAfterStartError": "結束時間必須晚於開始時間" + } + }, + "taskActivityLogTab": { + "title": "活動記錄", + "add": "新增", + "remove": "移除", + "none": "無", + "weight": "權重", + "createdTask": "建立了任務。" + }, + "taskProgress": { + "markAsDoneTitle": "將任務標記為完成?", + "confirmMarkAsDone": "是的,標記為完成", + "cancelMarkAsDone": "否,維持目前狀態", + "markAsDoneDescription": "進度已設為 100%,是否要將任務狀態更新為「已完成」?" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-list-filters.json b/worklenz-frontend/public/locales/zh_tw/task-list-filters.json new file mode 100644 index 000000000..86687a85a --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-list-filters.json @@ -0,0 +1,95 @@ +{ + "searchButton": "搜尋", + "resetButton": "重設", + "searchInputPlaceholder": "依名稱搜尋", + + "sortText": "排序", + "statusText": "狀態", + "phaseText": "階段", + "memberText": "成員", + "assigneesText": "指派對象", + "priorityText": "優先順序", + "labelsText": "標籤", + "membersText": "成員", + "groupByText": "群組依據", + "showArchivedText": "顯示已封存", + "showFieldsText": "顯示欄位", + "keyText": "編號", + "taskText": "任務", + "descriptionText": "描述", + "phasesText": "階段", + "listText": "清單", + "progressText": "進度", + "timeTrackingText": "時間追蹤", + "timetrackingText": "時間追蹤", + "estimationText": "估計", + "startDateText": "開始日期", + "startdateText": "開始日期", + "endDateText": "結束日期", + "dueDateText": "到期日", + "duedateText": "到期日", + "completedDateText": "完成日期", + "completeddateText": "完成日期", + "createdDateText": "建立日期", + "createddateText": "建立日期", + "lastUpdatedText": "最後更新", + "lastupdatedText": "最後更新", + "reporterText": "回報者", + "dueTimeText": "到期時間", + "duetimeText": "到期時間", + + "lowText": "低", + "mediumText": "中", + "highText": "高", + + "createStatusButtonTooltip": "狀態設定", + "configPhaseButtonTooltip": "階段設定", + "noLabelsFound": "找不到任何標籤", + + "addStatusButton": "新增狀態", + "addPhaseButton": "新增階段", + + "createStatus": "建立狀態", + "name": "名稱", + "category": "類別", + "selectCategory": "選擇類別", + "pleaseEnterAName": "請輸入名稱", + "pleaseSelectACategory": "請選擇類別", + "create": "建立", + + "searchTasks": "搜尋任務…", + "searchPlaceholder": "搜尋…", + "fieldsText": "欄位", + "loadingFilters": "正在載入篩選條件…", + "noOptionsFound": "找不到任何選項", + "filtersActive": "個篩選條件啟用中", + "filterActive": "個篩選條件啟用中", + "clearAll": "全部清除", + "clearing": "清除中…", + "cancel": "取消", + "search": "搜尋", + "groupedBy": "群組依據", + "manage": "管理", + "manageStatuses": "管理狀態", + "managePhases": "管理階段", + "dragToReorderStatuses": "狀態依類別組織,可在類別內拖曳以重新排序。按一下 [新增狀態] 在各類別中建立新狀態。", + "enterNewStatusName": "請輸入新的狀態名稱…", + "addStatus": "新增狀態", + "noStatusesFound": "此類別中沒有任何狀態", + "deleteStatus": "刪除狀態", + "deleteStatusConfirm": "確定要刪除此狀態嗎?此操作無法復原。", + "rename": "重新命名", + "delete": "刪除", + "enterStatusName": "請輸入狀態名稱", + "close": "關閉", + "cannotMoveStatus": "無法移動狀態", + "cannotMoveStatusMessage": "無法移動此狀態,因為這樣會使「{{categoryName}}」類別變為空的。每個類別至少需要有一個狀態。", + "ok": "確定", + "clearSort": "清除排序", + "sortAscending": "升冪排序", + "sortDescending": "降冪排序", + "sortByField": "依 {{field}} 排序", + "ascendingOrder": "升冪", + "descendingOrder": "降冪", + "currentSort": "目前排序:{{field}} {{order}}" +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-list-table.json b/worklenz-frontend/public/locales/zh_tw/task-list-table.json new file mode 100644 index 000000000..3c5da79f7 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-list-table.json @@ -0,0 +1,146 @@ +{ + "keyColumn": "編號", + "taskColumn": "任務", + "descriptionColumn": "描述", + "progressColumn": "進度", + "membersColumn": "成員", + "assigneesColumn": "指派對象", + "labelsColumn": "標籤", + "phasesColumn": "階段", + "phaseColumn": "階段", + "statusColumn": "狀態", + "priorityColumn": "優先順序", + "timeTrackingColumn": "時間追蹤", + "timetrackingColumn": "時間追蹤", + "estimationColumn": "估計", + "startDateColumn": "開始日期", + "startdateColumn": "開始日期", + "dueDateColumn": "到期日", + "duedateColumn": "到期日", + "completedDateColumn": "完成日期", + "completeddateColumn": "完成日期", + "createdDateColumn": "建立日期", + "createddateColumn": "建立日期", + "lastUpdatedColumn": "最後更新", + "lastupdatedColumn": "最後更新", + "reporterColumn": "回報者", + "dueTimeColumn": "到期時間", + "todoSelectorText": "待辦", + "doingSelectorText": "進行中", + "doneSelectorText": "已完成", + + "lowSelectorText": "低", + "mediumSelectorText": "中", + "highSelectorText": "高", + + "selectText": "選擇", + "labelsSelectorInputTip": "按 Enter 鍵即可建立!", + + "addTaskText": "新增任務", + "addSubTaskText": "新增子任務", + "addTaskInputPlaceholder": "輸入任務名稱並按 Enter 鍵", + "noTasksInGroup": "此群組中沒有任務", + "dropTaskHere": "將任務拖放到這裡", + + "openButton": "開啟", + "okButton": "確定", + + "noLabelsFound": "找不到任何標籤", + "searchInputPlaceholder": "搜尋或建立", + "assigneeSelectorInviteButton": "以電子郵件邀請新成員", + "labelInputPlaceholder": "搜尋或建立", + "searchLabelsPlaceholder": "搜尋標籤…", + "createLabelButton": "建立「{{name}}」", + "manageLabelsPath": "設定 → 標籤", + + "pendingInvitation": "待接受邀請", + + "contextMenu": { + "assignToMe": "指派給我", + "copyLink": "複製任務連結", + "linkCopied": "連結已複製到剪貼簿", + "linkCopyFailed": "複製連結失敗", + "moveTo": "移動至", + "unarchive": "解除封存", + "archive": "封存", + "convertToSubTask": "轉換為子任務", + "convertToTask": "轉換為任務", + "delete": "刪除", + "searchByNameInputPlaceholder": "依名稱搜尋" + }, + "setDueDate": "設定到期日", + "setStartDate": "設定開始日期", + "clearDueDate": "清除到期日", + "clearStartDate": "清除開始日期", + "dueDatePlaceholder": "到期日", + "startDatePlaceholder": "開始日期", + + "emptyStates": { + "noTaskGroups": "找不到任何任務群組", + "noTaskGroupsDescription": "當任務被建立或套用篩選條件時,任務會顯示在這裡。", + "errorPrefix": "錯誤:", + "dragTaskFallback": "任務" + }, + + "customColumns": { + "addCustomColumn": "新增自訂欄位", + "customColumnHeader": "自訂欄位", + "customColumnSettings": "自訂欄位設定", + "noCustomValue": "無值", + "peopleField": "人員欄位", + "noDate": "無日期", + "unsupportedField": "不支援的欄位類型", + + "modal": { + "addFieldTitle": "新增欄位", + "editFieldTitle": "編輯欄位", + "fieldTitle": "欄位標題", + "fieldTitleRequired": "欄位標題為必填", + "columnTitlePlaceholder": "欄位標題", + "type": "類型", + "deleteConfirmTitle": "確定要刪除此自訂欄位嗎?", + "deleteConfirmDescription": "此操作無法復原,與此欄位相關的所有資料將被永久刪除。", + "deleteButton": "刪除", + "cancelButton": "取消", + "createButton": "建立", + "updateButton": "更新", + "createSuccessMessage": "自訂欄位建立成功", + "updateSuccessMessage": "自訂欄位更新成功", + "deleteSuccessMessage": "自訂欄位刪除成功", + "deleteErrorMessage": "刪除自訂欄位失敗", + "createErrorMessage": "建立自訂欄位失敗", + "updateErrorMessage": "更新自訂欄位失敗" + }, + + "fieldTypes": { + "people": "人員", + "number": "數字", + "date": "日期", + "selection": "選擇", + "checkbox": "核取方塊", + "labels": "標籤", + "key": "編號", + "formula": "公式" + } + }, + + "indicators": { + "tooltips": { + "subtasks": "{{count}} 個子任務", + "subtasks_plural": "{{count}} 個子任務", + "comments": "{{count}} 則留言", + "comments_plural": "{{count}} 則留言", + "attachments": "{{count}} 個附件", + "attachments_plural": "{{count}} 個附件", + "subscribers": "此任務有訂閱者", + "dependencies": "此任務有相依任務", + "recurring": "週期性任務" + } + }, + + "timer": { + "conflictTitle": "計時器正在執行中", + "conflictMessage": "您在專案「{{projectName}}」的「{{taskName}}」有一個正在執行的計時器。是否要停止該計時器並為此任務啟動新的計時器?", + "stopAndStart": "停止並啟動新計時器" + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-management.json b/worklenz-frontend/public/locales/zh_tw/task-management.json new file mode 100644 index 000000000..de3a6ffc6 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-management.json @@ -0,0 +1,39 @@ +{ + "noTasksInGroup": "此群組中沒有任務", + "noTasksInGroupDescription": "新增一項任務以開始使用", + "addFirstTask": "新增第一項任務", + "openTask": "開啟", + "subtask": "個子任務", + "subtasks": "個子任務", + "comment": "則留言", + "comments": "則留言", + "attachment": "個附件", + "attachments": "個附件", + "enterSubtaskName": "請輸入子任務名稱…", + "add": "新增", + "cancel": "取消", + "renameGroup": "重新命名群組", + "renameStatus": "重新命名狀態", + "renamePhase": "重新命名階段", + "changeCategory": "變更類別", + "clickToEditGroupName": "按一下以編輯群組名稱", + "enterGroupName": "請輸入群組名稱", + "todo": "待辦", + "inProgress": "進行中", + "done": "已完成", + "defaultTaskName": "未命名任務", + + "indicators": { + "tooltips": { + "subtasks": "{{count}} 個子任務", + "subtasks_plural": "{{count}} 個子任務", + "comments": "{{count}} 則留言", + "comments_plural": "{{count}} 則留言", + "attachments": "{{count}} 個附件", + "attachments_plural": "{{count}} 個附件", + "subscribers": "此任務有訂閱者", + "dependencies": "此任務有相依任務", + "recurring": "週期性任務" + } + } +} diff --git a/worklenz-frontend/public/locales/zh_tw/task-template-drawer.json b/worklenz-frontend/public/locales/zh_tw/task-template-drawer.json new file mode 100644 index 000000000..2cc0d94b3 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/task-template-drawer.json @@ -0,0 +1,12 @@ +{ + "createTaskTemplate": "建立任務範本", + "editTaskTemplate": "編輯任務範本", + "cancelText": "取消", + "saveText": "儲存", + "templateNameText": "範本名稱", + "templateNameRequired": "範本名稱為必填", + "selectedTasks": "已選取任務", + "removeTask": "移除", + "cancelButton": "取消", + "saveButton": "儲存" +} diff --git a/worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json b/worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json new file mode 100644 index 000000000..ed6606d08 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/tasks/task-table-bulk-actions.json @@ -0,0 +1,41 @@ +{ + "taskSelected": "項任務已選取", + "tasksSelected": "項任務已選取", + "changeStatus": "變更狀態 / 優先順序 / 階段", + "changeLabel": "變更標籤", + "assignToMe": "指派給我", + "changeAssignees": "變更指派對象", + "archive": "封存", + "unarchive": "解除封存", + "delete": "刪除", + "moreOptions": "更多選項", + "deselectAll": "取消全選", + "status": "狀態", + "priority": "優先順序", + "phase": "階段", + "member": "成員", + "createTaskTemplate": "建立任務範本", + "apply": "套用", + "createLabel": "+ 建立標籤", + "searchOrCreateLabel": "搜尋或建立標籤…", + "hitEnterToCreate": "按 Enter 鍵即可建立", + "labelExists": "標籤已存在", + "pendingInvitation": "待接受邀請", + "noMatchingLabels": "找不到符合的標籤", + "noLabels": "沒有標籤", + "CHANGE_STATUS": "變更狀態", + "CHANGE_PRIORITY": "變更優先順序", + "CHANGE_PHASE": "變更階段", + "ADD_LABELS": "新增標籤", + "ASSIGN_TO_ME": "指派給我", + "ASSIGN_MEMBERS": "指派成員", + "ARCHIVE": "封存", + "DELETE": "刪除", + "CANCEL": "取消", + "CLEAR_SELECTION": "清除選取", + "TASKS_SELECTED": "已選取 {{count}} 項任務", + "TASKS_SELECTED_plural": "已選取 {{count}} 項任務", + "DELETE_TASKS_CONFIRM": "確定要刪除 {{count}} 項任務嗎?", + "DELETE_TASKS_CONFIRM_plural": "確定要刪除 {{count}} 項任務嗎?", + "DELETE_TASKS_WARNING": "此操作無法復原。" +} diff --git a/worklenz-frontend/public/locales/zh_tw/template-drawer.json b/worklenz-frontend/public/locales/zh_tw/template-drawer.json new file mode 100644 index 000000000..a345904cc --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/template-drawer.json @@ -0,0 +1,19 @@ +{ + "title": "編輯任務範本", + "cancelText": "取消", + "saveText": "儲存", + "templateNameText": "範本名稱", + "selectedTasks": "已選取任務", + "removeTask": "移除", + "description": "描述", + "phase": "階段", + "statuses": "狀態", + "priorities": "優先順序", + "labels": "標籤", + "tasks": "任務", + "noTemplateSelected": "尚未選取範本", + "noDescription": "無描述", + "worklenzTemplates": "Worklenz 範本", + "yourTemplatesLibrary": "我的範本庫", + "searchTemplates": "搜尋範本" +} diff --git a/worklenz-frontend/public/locales/zh_tw/templateDrawer.json b/worklenz-frontend/public/locales/zh_tw/templateDrawer.json new file mode 100644 index 000000000..a68d29177 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/templateDrawer.json @@ -0,0 +1,23 @@ +{ + "bugTracking": "錯誤追蹤", + "construction": "營建", + "designCreative": "設計與創意", + "education": "教育", + "finance": "財務", + "hrRecruiting": "人資與徵才", + "informationTechnology": "資訊科技", + "legal": "法律", + "manufacturing": "製造", + "marketing": "行銷", + "nonprofit": "非營利組織", + "personalUse": "個人用途", + "salesCRM": "銷售與 CRM", + "serviceConsulting": "服務與顧問", + "softwareDevelopment": "軟體開發", + "description": "描述", + "phase": "階段", + "statuses": "狀態", + "priorities": "優先順序", + "labels": "標籤", + "tasks": "任務" +} diff --git a/worklenz-frontend/public/locales/zh_tw/time-report.json b/worklenz-frontend/public/locales/zh_tw/time-report.json new file mode 100644 index 000000000..9901049f3 --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/time-report.json @@ -0,0 +1,57 @@ +{ + "includeArchivedProjects": "包含已封存專案", + "export": "匯出", + "timeSheet": "工時表", + + "searchByName": "依名稱搜尋", + "selectAll": "全選", + "teams": "團隊", + + "searchByProject": "依專案名稱搜尋", + "projects": "專案", + + "searchByCategory": "依類別名稱搜尋", + "categories": "類別", + + "billable": "可計費", + "nonBillable": "不可計費", + + "total": "合計", + + "projectsTimeSheet": "專案工時表", + + "loggedTime": "已記錄時間 (小時)", + + "exportToExcel": "匯出至 Excel", + "logged": "記錄了", + "for": "用於", + + "membersTimeSheet": "成員工時表", + "member": "成員", + + "estimatedVsActual": "估計與實際", + "workingDays": "工作天", + "manDays": "人天", + "days": "天", + "estimatedDays": "估計天數", + "actualDays": "實際天數", + + "noCategories": "找不到任何類別", + "noCategory": "無類別", + "noProjects": "找不到任何專案", + "noTeams": "找不到任何團隊", + "noData": "找不到任何資料", + + "groupBy": "群組依據", + "groupByCategory": "類別", + "groupByTeam": "團隊", + "groupByStatus": "狀態", + "groupByNone": "無", + "clearSearch": "清除搜尋", + "selectedProjects": "已選取專案", + "projectsSelected": "個專案已選取", + "showSelected": "僅顯示已選取", + "expandAll": "全部展開", + "collapseAll": "全部收合", + "ungrouped": "未分組" +} diff --git a/worklenz-frontend/public/locales/zh_tw/unauthorized.json b/worklenz-frontend/public/locales/zh_tw/unauthorized.json new file mode 100644 index 000000000..1a972ba0b --- /dev/null +++ b/worklenz-frontend/public/locales/zh_tw/unauthorized.json @@ -0,0 +1,5 @@ +{ + "title": "未經授權!", + "subtitle": "您沒有權限存取此頁面", + "button": "回到首頁" +} diff --git a/worklenz-frontend/src/components/account-setup/members-step.tsx b/worklenz-frontend/src/components/account-setup/members-step.tsx index a9759f335..48c306d4d 100644 --- a/worklenz-frontend/src/components/account-setup/members-step.tsx +++ b/worklenz-frontend/src/components/account-setup/members-step.tsx @@ -110,7 +110,8 @@ const MembersStep: React.FC = ({ isDarkMode, styles, token }) { key: 'pt', label: t('languages.pt'), flag: '🇵🇹' }, { key: 'de', label: t('languages.de'), flag: '🇩🇪' }, { key: 'alb', label: t('languages.alb'), flag: '🇦🇱' }, - { key: 'zh', label: t('languages.zh'), flag: '🇨🇳' } + { key: 'zh', label: t('languages.zh'), flag: '🇨🇳' }, + { key: 'zh_tw', label: t('languages.zh_tw'), flag: '🇹🇼' } ]; const handleLanguageChange = (languageKey: string) => { diff --git a/worklenz-frontend/src/features/i18n/LanguageSelector.tsx b/worklenz-frontend/src/features/i18n/LanguageSelector.tsx index 36c887579..16db15539 100644 --- a/worklenz-frontend/src/features/i18n/LanguageSelector.tsx +++ b/worklenz-frontend/src/features/i18n/LanguageSelector.tsx @@ -18,15 +18,17 @@ const LanguageSelector = () => { { key: 'alb', label: 'Shqip' }, { key: 'de', label: 'Deutsch' }, { key: 'zh_cn', label: '简体中文' }, + { key: 'zh_tw', label: '繁體中文(台灣)' }, ]; - const languageLabels = { + const languageLabels: Record = { en: 'En', es: 'Es', pt: 'Pt', alb: 'Sq', de: 'de', zh_cn: 'zh_cn', + zh_tw: 'zh_tw', }; return ( diff --git a/worklenz-frontend/src/features/i18n/localesSlice.ts b/worklenz-frontend/src/features/i18n/localesSlice.ts index 74644183e..f08ce8246 100644 --- a/worklenz-frontend/src/features/i18n/localesSlice.ts +++ b/worklenz-frontend/src/features/i18n/localesSlice.ts @@ -8,6 +8,7 @@ export enum Language { ALB = 'alb', DE = 'de', ZH_CN = 'zh_cn', + ZH_TW = 'zh_tw', KO = 'ko', } @@ -18,17 +19,31 @@ type LocalesState = { }; const STORAGE_KEY = 'i18nextLng'; +const ZH_TW_ALIASES = ['zh-tw', 'zh_tw', 'zh-hant', 'zh_hant', 'zh-hant-tw', 'zh_hant_tw']; + +const normalizeLanguage = (language?: string | null): ILanguageType | null => { + const normalizedLanguage = language?.trim().toLowerCase(); + + if (!normalizedLanguage) return null; + if (ZH_TW_ALIASES.includes(normalizedLanguage)) return Language.ZH_TW; + if (Object.values(Language).includes(normalizedLanguage as Language)) { + return normalizedLanguage as ILanguageType; + } + + const browserLang = normalizedLanguage.split('-')[0]; + if (Object.values(Language).includes(browserLang as Language)) { + return browserLang as ILanguageType; + } + + return null; +}; /** * Gets the user's browser language and returns it if supported, otherwise returns English * @returns The detected supported language or English as fallback */ const getDefaultLanguage = (): ILanguageType => { - const browserLang = navigator.language.split('-')[0]; - if (Object.values(Language).includes(browserLang as Language)) { - return browserLang as ILanguageType; - } - return Language.EN; + return normalizeLanguage(navigator.language) || Language.EN; }; const DEFAULT_LANGUAGE: ILanguageType = getDefaultLanguage(); diff --git a/worklenz-frontend/src/i18n.ts b/worklenz-frontend/src/i18n.ts index f70b2f5c7..fdbc73883 100644 --- a/worklenz-frontend/src/i18n.ts +++ b/worklenz-frontend/src/i18n.ts @@ -3,6 +3,13 @@ import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import HttpApi from 'i18next-http-backend'; +const normalizeDetectedLanguage = (language: string): string => { + const normalizedLanguage = language.trim().toLowerCase(); + const zhTwAliases = ['zh-tw', 'zh_tw', 'zh-hant', 'zh_hant', 'zh-hant-tw', 'zh_hant_tw']; + + return zhTwAliases.includes(normalizedLanguage) ? 'zh_tw' : language; +}; + i18n .use(HttpApi) .use(LanguageDetector) @@ -19,6 +26,7 @@ i18n detection: { order: ['localStorage', 'navigator'], caches: ['localStorage'], + convertDetectedLanguage: normalizeDetectedLanguage, }, debug: process.env.NODE_ENV === 'development', diff --git a/worklenz-frontend/src/pages/account-setup/account-setup.tsx b/worklenz-frontend/src/pages/account-setup/account-setup.tsx index 3c189b574..1641ca420 100644 --- a/worklenz-frontend/src/pages/account-setup/account-setup.tsx +++ b/worklenz-frontend/src/pages/account-setup/account-setup.tsx @@ -470,7 +470,8 @@ const AccountSetup: React.FC = () => { { key: Language.PT, label: 'Português', flag: '🇵🇹' }, { key: Language.DE, label: 'Deutsch', flag: '🇩🇪' }, { key: Language.ALB, label: 'Shqip', flag: '🇦🇱' }, - { key: Language.ZH_CN, label: '简体中文', flag: '🇨🇳' } + { key: Language.ZH_CN, label: '简体中文', flag: '🇨🇳' }, + { key: Language.ZH_TW, label: '繁體中文(台灣)', flag: '🇹🇼' } ]; const handleLanguageChange = (languageKey: ILanguageType) => { diff --git a/worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx b/worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx index 5a77097ed..2949d871a 100644 --- a/worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx +++ b/worklenz-frontend/src/pages/settings/language-and-region/language-and-region-settings.tsx @@ -59,6 +59,10 @@ const LanguageAndRegionSettings = () => { value: Language.ZH_CN, label: '简体中文', }, + { + value: Language.ZH_TW, + label: '繁體中文(台灣)', + }, { value: Language.KO, label: '한국어', diff --git a/worklenz-frontend/src/utils/calculate-time-difference.ts b/worklenz-frontend/src/utils/calculate-time-difference.ts index c3e13f19a..c8ba8d45a 100644 --- a/worklenz-frontend/src/utils/calculate-time-difference.ts +++ b/worklenz-frontend/src/utils/calculate-time-difference.ts @@ -8,13 +8,13 @@ import { differenceInYears, formatDistanceToNow, } from 'date-fns'; -import { enUS, es, pt } from 'date-fns/locale'; +import { enUS, es, pt, zhTW } from 'date-fns/locale'; import { getLanguageFromLocalStorage } from './language-utils'; export function calculateTimeDifference(timestamp: string | Date): string { const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp; const localeString = getLanguageFromLocalStorage(); - const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt; + const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt; const now = new Date(); const diffInSeconds = differenceInSeconds(now, date); diff --git a/worklenz-frontend/src/utils/calculate-time-gap.ts b/worklenz-frontend/src/utils/calculate-time-gap.ts index 7de673b85..767c04654 100644 --- a/worklenz-frontend/src/utils/calculate-time-gap.ts +++ b/worklenz-frontend/src/utils/calculate-time-gap.ts @@ -1,10 +1,10 @@ import { formatDistanceToNow } from 'date-fns'; -import { enUS, es, pt } from 'date-fns/locale'; +import { enUS, es, pt, zhTW } from 'date-fns/locale'; import { getLanguageFromLocalStorage } from './language-utils'; export function calculateTimeGap(timestamp: string | Date): string { const localeString = getLanguageFromLocalStorage(); - const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt; + const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt; const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp; return formatDistanceToNow(date, { addSuffix: true, locale }); } diff --git a/worklenz-frontend/src/utils/current-date-string.ts b/worklenz-frontend/src/utils/current-date-string.ts index 10b879294..7b396c89a 100644 --- a/worklenz-frontend/src/utils/current-date-string.ts +++ b/worklenz-frontend/src/utils/current-date-string.ts @@ -1,4 +1,5 @@ import dayjs from 'dayjs'; +import 'dayjs/locale/zh-tw'; import { getLanguageFromLocalStorage } from './language-utils'; export const currentDateString = (): string => { @@ -23,6 +24,9 @@ export const currentDateString = (): string => { case 'zh_cn': locale = 'zh-cn'; break; + case 'zh_tw': + locale = 'zh-tw'; + break; case 'alb': locale = 'sq'; // Albanian locale code for dayjs break; @@ -48,6 +52,9 @@ export const currentDateString = (): string => { case 'zh_cn': todayText = '今天是'; break; + case 'zh_tw': + todayText = '今天是'; + break; case 'alb': todayText = 'Sot është'; break; diff --git a/worklenz-frontend/src/utils/dateUtils.ts b/worklenz-frontend/src/utils/dateUtils.ts index ad22c9114..4a705d7a8 100644 --- a/worklenz-frontend/src/utils/dateUtils.ts +++ b/worklenz-frontend/src/utils/dateUtils.ts @@ -5,6 +5,7 @@ import 'dayjs/locale/de'; import 'dayjs/locale/es'; import 'dayjs/locale/pt'; import 'dayjs/locale/zh-cn'; +import 'dayjs/locale/zh-tw'; import { getLanguageFromLocalStorage } from './language-utils'; // Initialize plugins @@ -19,7 +20,8 @@ const getLocaleFromLanguage = (language: string): string => { 'es': 'es', 'pt': 'pt', 'alb': 'en', // Albanian not supported by dayjs, fallback to English - 'zh': 'zh-cn' + 'zh': 'zh-cn', + 'zh_tw': 'zh-tw' }; return localeMap[language] || 'en'; }; diff --git a/worklenz-frontend/src/utils/format-date-time-with-locale.ts b/worklenz-frontend/src/utils/format-date-time-with-locale.ts index 23548ac3a..56c3805df 100644 --- a/worklenz-frontend/src/utils/format-date-time-with-locale.ts +++ b/worklenz-frontend/src/utils/format-date-time-with-locale.ts @@ -1,5 +1,5 @@ import { format } from 'date-fns'; -import { enUS, es, pt } from 'date-fns/locale'; +import { enUS, es, pt, zhTW } from 'date-fns/locale'; import { getLanguageFromLocalStorage } from './language-utils'; export const formatDateTimeWithLocale = (dateString: string): string => { @@ -7,6 +7,6 @@ export const formatDateTimeWithLocale = (dateString: string): string => { const date = new Date(dateString); const localeString = getLanguageFromLocalStorage(); - const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt; + const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt; return format(date, 'MMM d, yyyy, h:mm:ss a', { locale }); }; diff --git a/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts b/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts index 876dbf94a..527e1bd5d 100644 --- a/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts +++ b/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts @@ -1,5 +1,5 @@ import { format } from 'date-fns'; -import { enUS, es, pt } from 'date-fns/locale'; +import { enUS, es, pt, zhTW } from 'date-fns/locale'; import { getLanguageFromLocalStorage } from './language-utils'; /** @@ -38,7 +38,8 @@ export const formatDateTimeWithUserTimezone = ( const localeMap = { 'en': 'en-US', 'es': 'es-ES', - 'pt': 'pt-PT' + 'pt': 'pt-PT', + 'zh_tw': 'zh-TW' }; const locale = localeMap[localeString as keyof typeof localeMap] || 'en-US'; @@ -47,7 +48,7 @@ export const formatDateTimeWithUserTimezone = ( // Fallback to date-fns formatting for UTC or when no timezone const localeString = getLanguageFromLocalStorage(); - const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt; + const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : localeString === 'zh_tw' ? zhTW : pt; return format(date, 'MMM d, yyyy, h:mm:ss a', { locale }); } catch (error) { console.error('Error formatting date with user timezone:', error); diff --git a/worklenz-frontend/src/utils/greetingString.ts b/worklenz-frontend/src/utils/greetingString.ts index 86043f0be..c6028b515 100644 --- a/worklenz-frontend/src/utils/greetingString.ts +++ b/worklenz-frontend/src/utils/greetingString.ts @@ -47,6 +47,12 @@ export const greetingString = (name: string): string => { morning = '早上好'; afternoon = '下午好'; evening = '晚上好'; + } else if (language === 'zh_tw') { + greetingPrefix = '您好'; + greetingSuffix = ''; + morning = '早安'; + afternoon = '午安'; + evening = '晚安'; } // Get the localized time period based on the current time @@ -56,7 +62,7 @@ export const greetingString = (name: string): string => { else localizedTimePeriod = evening; // Handle Chinese language which has different structure - if (language === 'zh_cn') { + if (language === 'zh_cn' || language === 'zh_tw') { return `${greetingPrefix} ${name}, ${localizedTimePeriod}!`; } diff --git a/worklenz-frontend/src/utils/language-utils.ts b/worklenz-frontend/src/utils/language-utils.ts index 6e0643d62..46ff5e94c 100644 --- a/worklenz-frontend/src/utils/language-utils.ts +++ b/worklenz-frontend/src/utils/language-utils.ts @@ -1,17 +1,31 @@ import { ILanguageType, Language } from '@/features/i18n/localesSlice'; const STORAGE_KEY = 'i18nextLng'; +const ZH_TW_ALIASES = ['zh-tw', 'zh_tw', 'zh-hant', 'zh_hant', 'zh-hant-tw', 'zh_hant_tw']; + +const normalizeLanguage = (language?: string | null): ILanguageType | null => { + const normalizedLanguage = language?.trim().toLowerCase(); + + if (!normalizedLanguage) return null; + if (ZH_TW_ALIASES.includes(normalizedLanguage)) return Language.ZH_TW; + if (Object.values(Language).includes(normalizedLanguage as Language)) { + return normalizedLanguage as ILanguageType; + } + + const browserLang = normalizedLanguage.split('-')[0]; + if (Object.values(Language).includes(browserLang as Language)) { + return browserLang as ILanguageType; + } + + return null; +}; /** * Gets the user's browser language and returns it if supported, otherwise returns English * @returns The detected supported language or English as fallback */ export const getDefaultLanguage = (): ILanguageType => { - const browserLang = navigator.language.split('-')[0]; - if (Object.values(Language).includes(browserLang as Language)) { - return browserLang as ILanguageType; - } - return Language.EN; + return normalizeLanguage(navigator.language) || Language.EN; }; export const DEFAULT_LANGUAGE: ILanguageType = getDefaultLanguage();