From 2fbaf7b312961ca9c1930f285a95b4c2314bb278 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 30 Jun 2025 11:37:18 -0700 Subject: [PATCH] SPA (#1538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # READ CAREFULLY THEN REMOVE Remove bullet points that are not relevant. PLEASE REFRAIN FROM USING AI TO WRITE YOUR CODE AND PR DESCRIPTION. IF YOU DO USE AI TO WRITE YOUR CODE PLEASE PROVIDE A DESCRIPTION AND REVIEW IT CAREFULLY. MAKE SURE YOU UNDERSTAND THE CODE YOU ARE SUBMITTING USING AI. - Pull requests that do not follow these guidelines will be closed without review or comment. - If you use AI to write your PR description your pr will be close without review or comment. - If you are unsure about anything, feel free to ask for clarification. ## Description Please provide a clear description of your changes. --- ## Type of Change Please delete options that are not relevant. - [ ] 🐛 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (non-breaking change which adds functionality) - [ ] 💥 Breaking change (fix or feature with breaking changes) - [ ] 📝 Documentation update - [ ] 🎨 UI/UX improvement - [ ] 🔒 Security enhancement - [ ] ⚡ Performance improvement ## Areas Affected Please check all that apply: - [ ] Email Integration (Gmail, IMAP, etc.) - [ ] User Interface/Experience - [ ] Authentication/Authorization - [ ] Data Storage/Management - [ ] API Endpoints - [ ] Documentation - [ ] Testing Infrastructure - [ ] Development Workflow - [ ] Deployment/Infrastructure ## Testing Done Describe the tests you've done: - [ ] Unit tests added/updated - [ ] Integration tests added/updated - [ ] Manual testing performed - [ ] Cross-browser testing (if UI changes) - [ ] Mobile responsiveness verified (if UI changes) ## Security Considerations For changes involving data or authentication: - [ ] No sensitive data is exposed - [ ] Authentication checks are in place - [ ] Input validation is implemented - [ ] Rate limiting is considered (if applicable) ## Checklist - [ ] I have read the [CONTRIBUTING](https://github.com/Mail-0/Zero/blob/staging/.github/CONTRIBUTING.md) document - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in complex areas - [ ] I have updated the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix/feature works - [ ] All tests pass locally - [ ] Any dependent changes are merged and published ## Additional Notes Add any other context about the pull request here. ## Screenshots/Recordings Add screenshots or recordings here if applicable. --- _By submitting this pull request, I confirm that my contribution is made under the terms of the project's license._ ## Summary by CodeRabbit * **New Features** * Introduced a new localization system replacing translation hooks with direct message function calls. * Added localization project configuration and Inlang plugins for improved message formatting. * **Bug Fixes** * Enhanced pluralization handling across all supported languages for accurate message display. * **Refactor** * Unified translation and locale management across components by removing hook-based translations. * Removed obsolete navigation and email signature settings pages. * Simplified query and server provider logic, removing connection-specific and internationalization context code. * Disabled server-side rendering in the mail app. * Removed Cloudflare Worker request handler and related environment augmentations. * **Chores** * Updated dependencies and scripts, adding Inlang CLI and removing unused packages. * Cleaned up configuration files including Vite, Wrangler, and i18n settings. * Added `.gitignore` for localization cache. * Updated static asset handling and environment configurations for Cloudflare deployments. --- apps/mail/app/(auth)/login/error-message.tsx | 7 +- apps/mail/app/(routes)/mail/[folder]/page.tsx | 24 +- apps/mail/app/(routes)/mail/page.tsx | 6 +- .../(routes)/settings/[...settings]/page.tsx | 6 +- .../app/(routes)/settings/appearance/page.tsx | 25 +- .../(routes)/settings/connections/page.tsx | 28 +- .../(routes)/settings/danger-zone/page.tsx | 33 +- .../app/(routes)/settings/general/page.tsx | 84 +- .../app/(routes)/settings/labels/page.tsx | 27 +- .../app/(routes)/settings/privacy/page.tsx | 23 +- .../app/(routes)/settings/security/page.tsx | 19 +- .../settings/shortcuts/hotkey-recorder.tsx | 12 +- .../app/(routes)/settings/shortcuts/page.tsx | 27 +- .../app/(routes)/settings/signatures/page.tsx | 386 -- apps/mail/app/entry.server.tsx | 3 +- apps/mail/app/root.tsx | 33 +- apps/mail/components/connection/add.tsx | 11 +- .../context/command-palette-context.tsx | 16 +- .../context/label-sidebar-context.tsx | 18 +- .../components/context/thread-context.tsx | 46 +- apps/mail/components/create/create-email.tsx | 6 +- apps/mail/components/create/editor.tsx | 26 +- .../create/image-compression-settings.tsx | 30 +- apps/mail/components/labels/label-dialog.tsx | 14 +- apps/mail/components/mail/mail-display.tsx | 90 +- apps/mail/components/mail/mail-iframe.tsx | 12 +- apps/mail/components/mail/mail-list.tsx | 41 +- apps/mail/components/mail/mail.tsx | 25 +- apps/mail/components/mail/nav-main.tsx | 16 - apps/mail/components/mail/note-panel.tsx | 115 +- apps/mail/components/mail/reply-composer.tsx | 8 +- apps/mail/components/mail/thread-display.tsx | 30 +- apps/mail/components/theme/theme-switcher.tsx | 16 +- apps/mail/components/ui/app-sidebar.tsx | 5 +- apps/mail/components/ui/nav-main.tsx | 21 +- apps/mail/components/ui/nav-user.tsx | 45 +- apps/mail/config/navigation.ts | 41 +- apps/mail/hooks/driver/use-delete.ts | 9 +- apps/mail/hooks/use-notes.tsx | 5 +- apps/mail/hooks/use-optimistic-actions.ts | 17 +- apps/mail/i18n/config.ts | 45 - apps/mail/i18n/request.ts | 41 - apps/mail/lib/hotkeys/mail-list-hotkeys.tsx | 33 +- apps/mail/lib/notes-utils.ts | 39 +- apps/mail/lib/utils.ts | 28 +- apps/mail/{locales => messages}/ar.json | 62 +- apps/mail/{locales => messages}/ca.json | 50 +- apps/mail/{locales => messages}/cs.json | 58 +- apps/mail/{locales => messages}/de.json | 51 +- apps/mail/{locales => messages}/en.json | 53 +- apps/mail/{locales => messages}/es.json | 50 +- apps/mail/{locales => messages}/fa.json | 50 +- apps/mail/{locales => messages}/fr.json | 50 +- apps/mail/{locales => messages}/hi.json | 50 +- apps/mail/{locales => messages}/hu.json | 50 +- apps/mail/{locales => messages}/ja.json | 50 +- apps/mail/{locales => messages}/ko.json | 50 +- apps/mail/{locales => messages}/lv.json | 50 +- apps/mail/{locales => messages}/nl.json | 50 +- apps/mail/{locales => messages}/pl.json | 58 +- apps/mail/{locales => messages}/pt.json | 50 +- apps/mail/{locales => messages}/ru.json | 71 +- apps/mail/{locales => messages}/tr.json | 50 +- apps/mail/{locales => messages}/vi.json | 50 +- apps/mail/{locales => messages}/zh_CN.json | 52 +- apps/mail/{locales => messages}/zh_TW.json | 50 +- apps/mail/package.json | 34 +- apps/mail/project.inlang/.gitignore | 1 + apps/mail/project.inlang/project_id | 1 + apps/mail/project.inlang/settings.json | 33 + apps/mail/providers/query-provider.tsx | 25 +- apps/mail/providers/server-providers.tsx | 13 +- apps/mail/react-router.config.ts | 2 +- apps/mail/vite.config.ts | 19 +- apps/mail/worker.ts | 39 - apps/mail/wrangler.jsonc | 12 +- apps/server/src/trpc/routes/cookies.ts | 11 +- i18n.json | 2 +- pnpm-lock.yaml | 3412 +++++++++-------- 79 files changed, 3339 insertions(+), 2962 deletions(-) delete mode 100644 apps/mail/app/(routes)/settings/signatures/page.tsx delete mode 100644 apps/mail/components/mail/nav-main.tsx delete mode 100644 apps/mail/i18n/config.ts delete mode 100644 apps/mail/i18n/request.ts rename apps/mail/{locales => messages}/ar.json (94%) rename apps/mail/{locales => messages}/ca.json (95%) rename apps/mail/{locales => messages}/cs.json (93%) rename apps/mail/{locales => messages}/de.json (94%) rename apps/mail/{locales => messages}/en.json (94%) rename apps/mail/{locales => messages}/es.json (95%) rename apps/mail/{locales => messages}/fa.json (96%) rename apps/mail/{locales => messages}/fr.json (95%) rename apps/mail/{locales => messages}/hi.json (96%) rename apps/mail/{locales => messages}/hu.json (95%) rename apps/mail/{locales => messages}/ja.json (95%) rename apps/mail/{locales => messages}/ko.json (95%) rename apps/mail/{locales => messages}/lv.json (95%) rename apps/mail/{locales => messages}/nl.json (94%) rename apps/mail/{locales => messages}/pl.json (93%) rename apps/mail/{locales => messages}/pt.json (95%) rename apps/mail/{locales => messages}/ru.json (93%) rename apps/mail/{locales => messages}/tr.json (95%) rename apps/mail/{locales => messages}/vi.json (95%) rename apps/mail/{locales => messages}/zh_CN.json (94%) rename apps/mail/{locales => messages}/zh_TW.json (94%) create mode 100644 apps/mail/project.inlang/.gitignore create mode 100644 apps/mail/project.inlang/project_id create mode 100644 apps/mail/project.inlang/settings.json delete mode 100644 apps/mail/worker.ts diff --git a/apps/mail/app/(auth)/login/error-message.tsx b/apps/mail/app/(auth)/login/error-message.tsx index 7729c358e..027569cef 100644 --- a/apps/mail/app/(auth)/login/error-message.tsx +++ b/apps/mail/app/(auth)/login/error-message.tsx @@ -1,8 +1,8 @@ import { TriangleAlert } from 'lucide-react'; -import { useTranslations } from 'use-intl'; import { useQueryState } from 'nuqs'; import { useEffect } from 'react'; import { toast } from 'sonner'; +import { m } from '@/paraglide/messages'; const errorMessages = ['required_scopes_missing'] as const; @@ -19,11 +19,10 @@ const isErrorMessage = (error: string): error is (typeof errorMessages)[number] const ErrorMessage = () => { const [error] = useQueryState('error'); - const t = useTranslations(); useEffect(() => { if (error && isErrorToast(error)) { - toast.error(t(`errorMessages.${error}`)); + toast.error(m[`errorMessages.${error}`]()); } }); @@ -36,7 +35,7 @@ const ErrorMessage = () => {

- {t(`errorMessages.${error}`)} + {m[`errorMessages.${error}`]()}

diff --git a/apps/mail/app/(routes)/mail/[folder]/page.tsx b/apps/mail/app/(routes)/mail/[folder]/page.tsx index 36c7f4fe3..fcbcbf637 100644 --- a/apps/mail/app/(routes)/mail/[folder]/page.tsx +++ b/apps/mail/app/(routes)/mail/[folder]/page.tsx @@ -1,11 +1,10 @@ -import { Navigate, useLoaderData, useNavigate } from 'react-router'; +import { useLoaderData, useNavigate } from 'react-router'; import { useTRPC } from '@/providers/query-provider'; import { MailLayout } from '@/components/mail/mail'; -import { useQuery } from '@tanstack/react-query'; +import { useLabels } from '@/hooks/use-labels'; import { authProxy } from '@/lib/auth-proxy'; import { useEffect, useState } from 'react'; import type { Route } from './+types/page'; -import { Loader2 } from 'lucide-react'; const ALLOWED_FOLDERS = ['inbox', 'draft', 'sent', 'spam', 'bin', 'archive']; @@ -23,20 +22,15 @@ export async function clientLoader({ params, request }: Route.ClientLoaderArgs) export default function MailPage() { const { folder } = useLoaderData(); const navigate = useNavigate(); - const trpc = useTRPC(); - const [isLabelValid, setIsLabelValid] = useState(null); - const [isLoading, setIsLoading] = useState(true); + const [isLabelValid, setIsLabelValid] = useState(true); const isStandardFolder = ALLOWED_FOLDERS.includes(folder); - const { data: userLabels, isLoading: isLoadingLabels } = useQuery( - trpc.labels.list.queryOptions(void 0), - ); + const { data: userLabels, isLoading: isLoadingLabels } = useLabels(); useEffect(() => { if (isStandardFolder) { setIsLabelValid(true); - setIsLoading(false); return; } @@ -55,7 +49,6 @@ export default function MailPage() { const labelExists = checkLabelExists(userLabels); setIsLabelValid(labelExists); - setIsLoading(false); if (!labelExists) { const timer = setTimeout(() => { @@ -65,18 +58,9 @@ export default function MailPage() { } } else { setIsLabelValid(false); - setIsLoading(false); } }, [folder, userLabels, isLoadingLabels, isStandardFolder, navigate]); - if (isLoading) { - return ( -
- -
- ); - } - if (!isLabelValid) { return (
diff --git a/apps/mail/app/(routes)/mail/page.tsx b/apps/mail/app/(routes)/mail/page.tsx index c9475b756..aeb5f05ea 100644 --- a/apps/mail/app/(routes)/mail/page.tsx +++ b/apps/mail/app/(routes)/mail/page.tsx @@ -1,5 +1,3 @@ -import { redirect } from 'react-router'; - -export function loader() { - throw redirect(`/mail/inbox`); +export function clientLoader() { + return Response.redirect(`${import.meta.env.VITE_PUBLIC_APP_URL}/mail/inbox`); } diff --git a/apps/mail/app/(routes)/settings/[...settings]/page.tsx b/apps/mail/app/(routes)/settings/[...settings]/page.tsx index 08a7769f3..7553a0878 100644 --- a/apps/mail/app/(routes)/settings/[...settings]/page.tsx +++ b/apps/mail/app/(routes)/settings/[...settings]/page.tsx @@ -3,7 +3,7 @@ import ConnectionsPage from '../connections/page'; import AppearancePage from '../appearance/page'; import ShortcutsPage from '../shortcuts/page'; import SecurityPage from '../security/page'; -import { useTranslations } from 'use-intl'; +import { m } from '@/paraglide/messages'; import GeneralPage from '../general/page'; import { useParams } from 'react-router'; import LabelsPage from '../labels/page'; @@ -21,12 +21,12 @@ const settingsPages: Record = { export default function SettingsPage() { const params = useParams(); const section = params.settings?.[0] || 'general'; - const t = useTranslations(); + const SettingsComponent = settingsPages[section]; if (!SettingsComponent) { - return
{t('pages.error.settingsNotFound')}
; + return
{m['pages.error.settingsNotFound']()}
; } return ; diff --git a/apps/mail/app/(routes)/settings/appearance/page.tsx b/apps/mail/app/(routes)/settings/appearance/page.tsx index da084aed9..c20bc2fed 100644 --- a/apps/mail/app/(routes)/settings/appearance/page.tsx +++ b/apps/mail/app/(routes)/settings/appearance/page.tsx @@ -15,14 +15,13 @@ import { } from '@/components/ui/select'; import { SettingsCard } from '@/components/settings/settings-card'; import { zodResolver } from '@hookform/resolvers/zod'; -import type { MessageKey } from '@/config/navigation'; import { useTRPC } from '@/providers/query-provider'; import { useMutation } from '@tanstack/react-query'; import { useSettings } from '@/hooks/use-settings'; import { Laptop, Moon, Sun } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { useTranslations } from 'use-intl'; import { useForm } from 'react-hook-form'; +import { m } from '@/paraglide/messages'; import { useTheme } from 'next-themes'; import { useState } from 'react'; import { toast } from 'sonner'; @@ -36,7 +35,7 @@ type Theme = 'dark' | 'light' | 'system'; export default function AppearancePage() { const [isSaving, setIsSaving] = useState(false); - const t = useTranslations(); + const { data, refetch } = useSettings(); const { theme, systemTheme, resolvedTheme, setTheme } = useTheme(); const trpc = useTRPC(); @@ -79,8 +78,8 @@ export default function AppearancePage() { colorTheme: values.colorTheme as Theme, }), { - success: t('common.settings.saved'), - error: t('common.settings.failedToSave'), + success: m['common.settings.saved'](), + error: m['common.settings.failedToSave'](), finally: async () => { await refetch(); setIsSaving(false); @@ -95,11 +94,11 @@ export default function AppearancePage() { return (
- {isSaving ? t('common.actions.saving') : t('common.actions.saveChanges')} + {isSaving ? m['common.actions.saving']() : m['common.actions.saveChanges']()} } > @@ -113,7 +112,7 @@ export default function AppearancePage() { name="colorTheme" render={({ field }) => ( - {t('pages.settings.appearance.theme')} + {m['pages.settings.appearance.theme']()} @@ -106,8 +105,8 @@ function DeleteAccountDialog() {
@@ -118,13 +117,11 @@ function DeleteAccountDialog() { } export default function DangerPage() { - const t = useTranslations(); - return (
diff --git a/apps/mail/app/(routes)/settings/general/page.tsx b/apps/mail/app/(routes)/settings/general/page.tsx index 6b543fc80..c4bd80387 100644 --- a/apps/mail/app/(routes)/settings/general/page.tsx +++ b/apps/mail/app/(routes)/settings/general/page.tsx @@ -20,31 +20,25 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { SettingsCard } from '@/components/settings/settings-card'; import { Globe, Clock, XIcon, Mail, InfoIcon } from 'lucide-react'; import { useEmailAliases } from '@/hooks/use-email-aliases'; +import { getLocale, setLocale } from '@/paraglide/runtime'; import { useState, useEffect, useMemo, memo } from 'react'; import { userSettingsSchema } from '@zero/server/schemas'; import { ScrollArea } from '@/components/ui/scroll-area'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useTranslations, useLocale } from 'use-intl'; import { useTRPC } from '@/providers/query-provider'; import { getBrowserTimezone } from '@/lib/timezones'; import { Textarea } from '@/components/ui/textarea'; import { useSettings } from '@/hooks/use-settings'; -import { availableLocales } from '@/i18n/config'; import { Switch } from '@/components/ui/switch'; import { Button } from '@/components/ui/button'; -import { useRevalidator } from 'react-router'; +// import { useRevalidator } from 'react-router'; +import { m } from '@/paraglide/messages'; import { cn } from '@/lib/utils'; import { toast } from 'sonner'; import * as z from 'zod'; const TimezoneSelect = memo( - ({ - field, - t, - }: { - field: ControllerRenderProps, 'timezone'>; - t: any; - }) => { + ({ field }: { field: ControllerRenderProps, 'timezone'> }) => { const [open, setOpen] = useState(false); const [timezoneSearch, setTimezoneSearch] = useState(''); @@ -76,7 +70,7 @@ const TimezoneSelect = memo(
setTimezoneSearch(e.target.value)} /> @@ -85,7 +79,7 @@ const TimezoneSelect = memo(
{filteredTimezones.length === 0 && (
- {t('pages.settings.general.noResultsFound')} + {m['pages.settings.general.noResultsFound']()}
)} {filteredTimezones.map((timezone) => ( @@ -117,17 +111,17 @@ TimezoneSelect.displayName = 'TimezoneSelect'; export default function GeneralPage() { const [isSaving, setIsSaving] = useState(false); - const locale = useLocale(); - const t = useTranslations(); - const { data } = useSettings(); + const locale = getLocale(); + + const { data, refetch: refetchSettings } = useSettings(); const { data: aliases } = useEmailAliases(); const trpc = useTRPC(); const queryClient = useQueryClient(); const { mutateAsync: saveUserSettings } = useMutation(trpc.settings.save.mutationOptions()); - const { mutateAsync: setLocaleCookie } = useMutation( - trpc.cookiePreferences.setLocaleCookie.mutationOptions(), - ); - const { revalidate } = useRevalidator(); + // const { mutateAsync: setLocaleCookie } = useMutation( + // trpc.cookiePreferences.setLocaleCookie.mutationOptions(), + // ); + // const { revalidate } = useRevalidator(); const form = useForm>({ resolver: zodResolver(userSettingsSchema), @@ -144,6 +138,7 @@ export default function GeneralPage() { useEffect(() => { if (data?.settings) { form.reset(data.settings); + setLocale(data.settings.language as any); } }, [form, data?.settings]); @@ -159,26 +154,18 @@ export default function GeneralPage() { async function onSubmit(values: z.infer) { setIsSaving(true); const saved = data?.settings ? { ...data.settings } : undefined; + try { - await saveUserSettings(values); queryClient.setQueryData(trpc.settings.get.queryKey(), (updater) => { if (!updater) return; return { settings: { ...updater.settings, ...values } }; }); + await saveUserSettings(values); + await refetchSettings(); - if (saved?.language !== values.language) { - await setLocaleCookie({ locale: values.language }); - const localeName = new Intl.DisplayNames([values.language], { type: 'language' }).of( - values.language, - ); - toast.success(t('common.settings.languageChanged', { locale: localeName! })); - await revalidate(); - } - - toast.success(t('common.settings.saved')); + toast.success(m['common.settings.saved']()); } catch (error) { - console.error('Failed to save settings:', error); - toast.error(t('common.settings.failedToSave')); + toast.error(m['common.settings.failedToSave']()); queryClient.setQueryData(trpc.settings.get.queryKey(), (updater) => { if (!updater) return; return saved ? { settings: { ...updater.settings, ...saved } } : updater; @@ -191,11 +178,11 @@ export default function GeneralPage() { return (
- {isSaving ? t('common.actions.saving') : t('common.actions.saveChanges')} + {isSaving ? m['common.actions.saving']() : m['common.actions.saveChanges']()} } > @@ -207,20 +194,21 @@ export default function GeneralPage() { name="language" render={({ field }) => ( - {t('pages.settings.general.language')} + {m['pages.settings.general.language']()} @@ -231,8 +219,8 @@ export default function GeneralPage() { name="timezone" render={({ field }) => ( - {t('pages.settings.general.timezone')} - + {m['pages.settings.general.timezone']()} + )} /> @@ -243,13 +231,13 @@ export default function GeneralPage() { render={({ field }) => ( - {t('pages.settings.general.defaultEmailAlias')}{' '} + {m['pages.settings.general.defaultEmailAlias']()}{' '} - {t('pages.settings.general.defaultEmailDescription')} + {m['pages.settings.general.defaultEmailDescription']()} @@ -258,7 +246,7 @@ export default function GeneralPage() { @@ -289,9 +277,9 @@ export default function GeneralPage() { render={({ field }) => (
- {t('pages.settings.general.zeroSignature')} + {m['pages.settings.general.zeroSignature']()} - {t('pages.settings.general.zeroSignatureDescription')} + {m['pages.settings.general.zeroSignatureDescription']()}
@@ -306,9 +294,9 @@ export default function GeneralPage() { render={({ field }) => (
- {t('pages.settings.general.autoRead')} + {m['pages.settings.general.autoRead']()} - {t('pages.settings.general.autoReadDescription')} + {m['pages.settings.general.autoReadDescription']()}
diff --git a/apps/mail/app/(routes)/settings/labels/page.tsx b/apps/mail/app/(routes)/settings/labels/page.tsx index a91f854f3..787fc0502 100644 --- a/apps/mail/app/(routes)/settings/labels/page.tsx +++ b/apps/mail/app/(routes)/settings/labels/page.tsx @@ -31,7 +31,7 @@ import { GMAIL_COLORS } from '@/lib/constants'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { useTranslations } from 'use-intl'; +import { m } from '@/paraglide/messages'; import { useForm } from 'react-hook-form'; import { Command } from 'lucide-react'; import { COLORS } from './colors'; @@ -39,7 +39,6 @@ import { useState } from 'react'; import { toast } from 'sonner'; export default function LabelsPage() { - const t = useTranslations(); const { data: labels, isLoading, error, refetch } = useLabels(); const [isDialogOpen, setIsDialogOpen] = useState(false); const [editingLabel, setEditingLabel] = useState(null); @@ -55,18 +54,18 @@ export default function LabelsPage() { ? updateLabel({ id: editingLabel.id!, name: data.name, color: data.color }) : createLabel({ color: data.color, name: data.name }), { - loading: t('common.labels.savingLabel'), - success: t('common.labels.saveLabelSuccess'), - error: t('common.labels.failedToSavingLabel'), + loading: m['common.labels.savingLabel'](), + success: m['common.labels.saveLabelSuccess'](), + error: m['common.labels.failedToSavingLabel'](), }, ); }; const handleDelete = async (id: string) => { toast.promise(deleteLabel({ id }), { - loading: t('common.labels.deletingLabel'), - success: t('common.labels.deleteLabelSuccess'), - error: t('common.labels.failedToDeleteLabel'), + loading: m['common.labels.deletingLabel'](), + success: m['common.labels.deleteLabelSuccess'](), + error: m['common.labels.failedToDeleteLabel'](), finally: async () => { await refetch(); }, @@ -81,14 +80,14 @@ export default function LabelsPage() { return (
- {t('common.mail.createNewLabel')} + {m['common.mail.createNewLabel']()} } editingLabel={editingLabel} @@ -114,7 +113,7 @@ export default function LabelsPage() {

{error.message}

) : labels?.length === 0 ? (

- {t('common.mail.noLabelsAvailable')} + {m['common.mail.noLabelsAvailable']()}

) : (
@@ -148,7 +147,7 @@ export default function LabelsPage() { - {t('common.labels.editLabel')} + {m['common.labels.editLabel']()} @@ -163,7 +162,7 @@ export default function LabelsPage() { - {t('common.labels.deleteLabel')} + {m['common.labels.deleteLabel']()}
diff --git a/apps/mail/app/(routes)/settings/privacy/page.tsx b/apps/mail/app/(routes)/settings/privacy/page.tsx index 4da6633db..3ae49d017 100644 --- a/apps/mail/app/(routes)/settings/privacy/page.tsx +++ b/apps/mail/app/(routes)/settings/privacy/page.tsx @@ -18,7 +18,7 @@ import { useSettings } from '@/hooks/use-settings'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { useState, useEffect } from 'react'; -import { useTranslations } from 'use-intl'; +import { m } from '@/paraglide/messages'; import { useForm } from 'react-hook-form'; import { XIcon } from 'lucide-react'; import { toast } from 'sonner'; @@ -26,7 +26,6 @@ import * as z from 'zod'; export default function PrivacyPage() { const [isSaving, setIsSaving] = useState(false); - const t = useTranslations(); const { data, refetch } = useSettings(); const trpc = useTRPC(); const { mutateAsync: saveUserSettings } = useMutation(trpc.settings.save.mutationOptions()); @@ -56,8 +55,8 @@ export default function PrivacyPage() { ...values, }), { - success: t('common.settings.saved'), - error: t('common.settings.failedToSave'), + success: m['common.settings.saved'](), + error: m['common.settings.failedToSave'](), finally: async () => { await refetch(); setIsSaving(false); @@ -70,11 +69,11 @@ export default function PrivacyPage() { return (
- {isSaving ? t('common.actions.saving') : t('common.actions.saveChanges')} + {isSaving ? m['common.actions.saving']() : m['common.actions.saveChanges']()} } > @@ -88,10 +87,10 @@ export default function PrivacyPage() {
- {t('pages.settings.privacy.externalImages')} + {m['pages.settings.privacy.externalImages']()} - {t('pages.settings.privacy.externalImagesDescription')} + {m['pages.settings.privacy.externalImagesDescription']()}
@@ -108,10 +107,10 @@ export default function PrivacyPage() {
- {t('pages.settings.privacy.trustedSenders')} + {m['pages.settings.privacy.trustedSenders']()} - {t('pages.settings.privacy.trustedSendersDescription')} + {m['pages.settings.privacy.trustedSendersDescription']()}
@@ -131,7 +130,7 @@ export default function PrivacyPage() { - {t('common.actions.remove')} + {m['common.actions.remove']()}
))} diff --git a/apps/mail/app/(routes)/settings/security/page.tsx b/apps/mail/app/(routes)/settings/security/page.tsx index 97593deb5..e3eee76d4 100644 --- a/apps/mail/app/(routes)/settings/security/page.tsx +++ b/apps/mail/app/(routes)/settings/security/page.tsx @@ -10,7 +10,7 @@ import { SettingsCard } from '@/components/settings/settings-card'; import { zodResolver } from '@hookform/resolvers/zod'; import { Switch } from '@/components/ui/switch'; import { Button } from '@/components/ui/button'; -import { useTranslations } from 'use-intl'; +import { m } from '@/paraglide/messages'; import { useForm } from 'react-hook-form'; import { KeyRound } from 'lucide-react'; import { useState } from 'react'; @@ -23,7 +23,6 @@ const formSchema = z.object({ export default function SecurityPage() { const [isSaving, setIsSaving] = useState(false); - const t = useTranslations(); const form = useForm>({ resolver: zodResolver(formSchema), @@ -46,13 +45,13 @@ export default function SecurityPage() { return (
- +
} @@ -67,10 +66,10 @@ export default function SecurityPage() {
- {t('pages.settings.security.twoFactorAuth')} + {m['pages.settings.security.twoFactorAuth']()} - {t('pages.settings.security.twoFactorAuthDescription')} + {m['pages.settings.security.twoFactorAuthDescription']()}
@@ -86,10 +85,10 @@ export default function SecurityPage() {
- {t('pages.settings.security.loginNotifications')} + {m['pages.settings.security.loginNotifications']()} - {t('pages.settings.security.loginNotificationsDescription')} + {m['pages.settings.security.loginNotificationsDescription']()}
diff --git a/apps/mail/app/(routes)/settings/shortcuts/hotkey-recorder.tsx b/apps/mail/app/(routes)/settings/shortcuts/hotkey-recorder.tsx index 73f4bace8..f70b2eff4 100644 --- a/apps/mail/app/(routes)/settings/shortcuts/hotkey-recorder.tsx +++ b/apps/mail/app/(routes)/settings/shortcuts/hotkey-recorder.tsx @@ -1,7 +1,6 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; -import type { MessageKey } from '@/config/navigation'; import { useEffect, useState } from 'react'; -import { useTranslations } from 'use-intl'; +import { m } from '@/paraglide/messages'; interface HotkeyRecorderProps { isOpen: boolean; @@ -16,7 +15,6 @@ export function HotkeyRecorder({ onHotkeyRecorded, currentKeys, }: HotkeyRecorderProps) { - const t = useTranslations(); const [recordedKeys, setRecordedKeys] = useState([]); const [isRecording, setIsRecording] = useState(false); @@ -67,15 +65,13 @@ export function HotkeyRecorder({ - - {t('pages.settings.shortcuts.actions.recordHotkey' as MessageKey)} - + {m['pages.settings.shortcuts.actions.recordHotkey']()}
{isRecording - ? t('pages.settings.shortcuts.actions.pressKeys' as MessageKey) - : t('pages.settings.shortcuts.actions.releaseKeys' as MessageKey)} + ? m['pages.settings.shortcuts.actions.pressKeys']() + : m['pages.settings.shortcuts.actions.releaseKeys']()}
{(recordedKeys.length > 0 ? recordedKeys : currentKeys).map((key) => ( diff --git a/apps/mail/app/(routes)/settings/shortcuts/page.tsx b/apps/mail/app/(routes)/settings/shortcuts/page.tsx index 30969df26..377967fc8 100644 --- a/apps/mail/app/(routes)/settings/shortcuts/page.tsx +++ b/apps/mail/app/(routes)/settings/shortcuts/page.tsx @@ -2,17 +2,12 @@ import { keyboardShortcuts, type Shortcut } from '@/config/shortcuts'; import { SettingsCard } from '@/components/settings/settings-card'; import { formatDisplayKeys } from '@/lib/hotkeys/use-hotkey-utils'; import { useShortcutCache } from '@/lib/hotkeys/use-hotkey-utils'; -import { useState, type ReactNode, useEffect } from 'react'; -import type { MessageKey } from '@/config/navigation'; -import { HotkeyRecorder } from './hotkey-recorder'; -import { Button } from '@/components/ui/button'; -import { useSession } from '@/lib/auth-client'; -import { useTranslations } from 'use-intl'; -import { toast } from 'sonner'; import { useCategorySettings } from '@/hooks/use-categories'; +import { useState, type ReactNode, useEffect } from 'react'; +import { useSession } from '@/lib/auth-client'; +import { m } from '@/paraglide/messages'; export default function ShortcutsPage() { - const t = useTranslations(); const { data: session } = useSession(); const { shortcuts, @@ -24,8 +19,8 @@ export default function ShortcutsPage() { return (
// -// } -// > -//
-// -// {/* Enable Signature Switch */} -// ( -// -//
-// -// {t('pages.settings.signatures.enableSignature')} -// -// -// {t('pages.settings.signatures.enableSignatureDescription')} -// -//
-// -// -// -//
-// )} -// /> - -// {watchSignatureEnabled && ( -// <> -// {/* Include by Default Switch */} -// ( -// -//
-// -// {t('pages.settings.signatures.includeByDefault')} -// -// -// {t('pages.settings.signatures.includeByDefaultDescription')} -// -//
-// -// -// -//
-// )} -// /> - -// {/* Editor Type Selector - Improved UI */} -//
-// {t('pages.settings.signatures.editorType')} -//
-// -// -//
-// -// {t('pages.settings.signatures.editorTypeDescription')} -// -//
- -// {/* Signature Editor - either plain text or rich editor */} -//
-// - -// {watchEditorType === 'plain' ? ( -//
-//