mirror of
https://github.com/documenso/documenso.git
synced 2025-11-23 21:21:37 +10:00
feat: web i18n (#1286)
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
@ -52,9 +54,11 @@ export const ConfigureDirectTemplateFormPartial = ({
|
||||
initialEmail,
|
||||
onSubmit,
|
||||
}: ConfigureDirectTemplateFormProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { data: session } = useSession();
|
||||
|
||||
const { Recipient } = template;
|
||||
const { derivedRecipientAccessAuth } = useRequiredDocumentAuthContext();
|
||||
const { data: session } = useSession();
|
||||
|
||||
const recipientsWithBlankDirectRecipientEmail = Recipient.map((recipient) => {
|
||||
if (recipient.id === directTemplateRecipient.id) {
|
||||
@ -110,7 +114,9 @@ export const ConfigureDirectTemplateFormPartial = ({
|
||||
name="email"
|
||||
render={({ field, fieldState }) => (
|
||||
<FormItem>
|
||||
<FormLabel required>Email</FormLabel>
|
||||
<FormLabel required>
|
||||
<Trans>Email</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
@ -126,7 +132,7 @@ export const ConfigureDirectTemplateFormPartial = ({
|
||||
|
||||
{!fieldState.error && (
|
||||
<p className="text-muted-foreground text-xs">
|
||||
Enter your email address to receive the completed document.
|
||||
<Trans>Enter your email address to receive the completed document.</Trans>
|
||||
</p>
|
||||
)}
|
||||
|
||||
@ -139,11 +145,7 @@ export const ConfigureDirectTemplateFormPartial = ({
|
||||
</DocumentFlowFormContainerContent>
|
||||
|
||||
<DocumentFlowFormContainerFooter>
|
||||
<DocumentFlowFormContainerStep
|
||||
title={flowStep.title}
|
||||
step={currentStep}
|
||||
maxStep={totalSteps}
|
||||
/>
|
||||
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
|
||||
|
||||
<DocumentFlowFormContainerActions
|
||||
loading={form.formState.isSubmitting}
|
||||
|
||||
@ -4,7 +4,10 @@ import { useState } from 'react';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import { msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '@documenso/lib/constants/recipient-roles';
|
||||
import type { Field } from '@documenso/prisma/client';
|
||||
import { type Recipient } from '@documenso/prisma/client';
|
||||
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
|
||||
@ -41,6 +44,7 @@ export const DirectTemplatePageView = ({
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { email, fullName, setEmail } = useRequiredSigningContext();
|
||||
@ -49,17 +53,18 @@ export const DirectTemplatePageView = ({
|
||||
const [step, setStep] = useState<DirectTemplateStep>('configure');
|
||||
const [isDocumentPdfLoaded, setIsDocumentPdfLoaded] = useState(false);
|
||||
|
||||
const recipientRoleDescription = RECIPIENT_ROLES_DESCRIPTION[directTemplateRecipient.role];
|
||||
const recipientRoleDescription = RECIPIENT_ROLES_DESCRIPTION_ENG[directTemplateRecipient.role];
|
||||
|
||||
const directTemplateFlow: Record<DirectTemplateStep, DocumentFlowStep> = {
|
||||
configure: {
|
||||
title: 'General',
|
||||
description: 'Preview and configure template.',
|
||||
title: msg`General`,
|
||||
description: msg`Preview and configure template.`,
|
||||
stepIndex: 1,
|
||||
},
|
||||
sign: {
|
||||
title: `${recipientRoleDescription.actionVerb} document`,
|
||||
description: `${recipientRoleDescription.actionVerb} the document to complete the process.`,
|
||||
// Todo: Translations
|
||||
title: msg`${recipientRoleDescription.actionVerb} document`,
|
||||
description: msg`${recipientRoleDescription.actionVerb} the document to complete the process.`,
|
||||
stepIndex: 2,
|
||||
},
|
||||
};
|
||||
@ -109,8 +114,10 @@ export const DirectTemplatePageView = ({
|
||||
redirectUrl ? router.push(redirectUrl) : router.push(`/sign/${token}/complete`);
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'We were unable to submit this document at this time. Please try again later.',
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(
|
||||
msg`We were unable to submit this document at this time. Please try again later.`,
|
||||
),
|
||||
variant: 'destructive',
|
||||
});
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@ -10,20 +11,26 @@ export default function NotFound() {
|
||||
return (
|
||||
<div className="mx-auto flex min-h-[80vh] w-full items-center justify-center py-32">
|
||||
<div>
|
||||
<p className="text-muted-foreground font-semibold">404 Template not found</p>
|
||||
<p className="text-muted-foreground font-semibold">
|
||||
<Trans>404 Template not found</Trans>
|
||||
</p>
|
||||
|
||||
<h1 className="mt-3 text-2xl font-bold md:text-3xl">Oops! Something went wrong.</h1>
|
||||
<h1 className="mt-3 text-2xl font-bold md:text-3xl">
|
||||
<Trans>Oops! Something went wrong.</Trans>
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground mt-4 text-sm">
|
||||
The template you are looking for may have been disabled, deleted or may have never
|
||||
existed.
|
||||
<Trans>
|
||||
The template you are looking for may have been disabled, deleted or may have never
|
||||
existed.
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex gap-x-2.5 gap-y-4 md:items-center">
|
||||
<Button asChild className="w-32">
|
||||
<Link href="/">
|
||||
<ChevronLeft className="mr-2 h-4 w-4" />
|
||||
Go Back
|
||||
<Trans>Go Back</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { notFound, redirect } from 'next/navigation';
|
||||
|
||||
import { Plural } from '@lingui/macro';
|
||||
import { UsersIcon } from 'lucide-react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getTemplateByDirectLinkToken } from '@documenso/lib/server-only/template/get-template-by-direct-link-token';
|
||||
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
||||
@ -22,6 +24,8 @@ export type TemplatesDirectPageProps = {
|
||||
};
|
||||
|
||||
export default async function TemplatesDirectPage({ params }: TemplatesDirectPageProps) {
|
||||
setupI18nSSR();
|
||||
|
||||
const { token } = params;
|
||||
|
||||
if (!token) {
|
||||
@ -75,8 +79,7 @@ export default async function TemplatesDirectPage({ params }: TemplatesDirectPag
|
||||
<div className="text-muted-foreground mb-8 mt-2.5 flex items-center gap-x-2">
|
||||
<UsersIcon className="h-4 w-4" />
|
||||
<p className="text-muted-foreground/80">
|
||||
{template.Recipient.length}{' '}
|
||||
{template.Recipient.length > 1 ? 'recipients' : 'recipient'}
|
||||
<Plural value={template.Recipient.length} one="# recipient" other="# recipients" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
@ -70,6 +72,8 @@ export const SignDirectTemplateForm = ({
|
||||
template,
|
||||
onSubmit,
|
||||
}: SignDirectTemplateFormProps) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext();
|
||||
|
||||
const [localFields, setLocalFields] = useState<DirectTemplateLocalField[]>(directRecipientFields);
|
||||
@ -168,7 +172,7 @@ export const SignDirectTemplateForm = ({
|
||||
<ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
|
||||
{validateUninsertedFields && uninsertedFields[0] && (
|
||||
<FieldToolTip key={uninsertedFields[0].id} field={uninsertedFields[0]} color="warning">
|
||||
Click to insert field
|
||||
<Trans>Click to insert field</Trans>
|
||||
</FieldToolTip>
|
||||
)}
|
||||
|
||||
@ -318,7 +322,9 @@ export const SignDirectTemplateForm = ({
|
||||
<div className="-mx-2 flex flex-1 flex-col gap-4 overflow-y-auto px-2">
|
||||
<div className="flex flex-1 flex-col gap-y-4">
|
||||
<div>
|
||||
<Label htmlFor="full-name">Full Name</Label>
|
||||
<Label htmlFor="full-name">
|
||||
<Trans>Full Name</Trans>
|
||||
</Label>
|
||||
|
||||
<Input
|
||||
id="full-name"
|
||||
@ -328,7 +334,9 @@ export const SignDirectTemplateForm = ({
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="Signature">Signature</Label>
|
||||
<Label htmlFor="Signature">
|
||||
<Trans>Signature</Trans>
|
||||
</Label>
|
||||
|
||||
<Card className="mt-2" gradient degrees={-120}>
|
||||
<CardContent className="p-0">
|
||||
@ -348,11 +356,7 @@ export const SignDirectTemplateForm = ({
|
||||
</DocumentFlowFormContainerContent>
|
||||
|
||||
<DocumentFlowFormContainerFooter>
|
||||
<DocumentFlowFormContainerStep
|
||||
title={flowStep.title}
|
||||
step={currentStep}
|
||||
maxStep={totalSteps}
|
||||
/>
|
||||
<DocumentFlowFormContainerStep step={currentStep} maxStep={totalSteps} />
|
||||
|
||||
<div className="mt-4 flex gap-x-4">
|
||||
<Button
|
||||
@ -362,7 +366,7 @@ export const SignDirectTemplateForm = ({
|
||||
disabled={isSubmitting}
|
||||
onClick={previousStep}
|
||||
>
|
||||
Back
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
|
||||
<SignDialog
|
||||
|
||||
@ -2,12 +2,15 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { signOut } from 'next-auth/react';
|
||||
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
export const DirectTemplateAuthPageView = () => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const [isSigningOut, setIsSigningOut] = useState(false);
|
||||
@ -21,8 +24,8 @@ export const DirectTemplateAuthPageView = () => {
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'We were unable to log you out at this time.',
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(msg`We were unable to log you out at this time.`),
|
||||
duration: 10000,
|
||||
variant: 'destructive',
|
||||
});
|
||||
@ -34,10 +37,12 @@ export const DirectTemplateAuthPageView = () => {
|
||||
return (
|
||||
<div className="mx-auto flex h-[70vh] w-full max-w-md flex-col items-center justify-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-semibold">Authentication required</h1>
|
||||
<h1 className="text-3xl font-semibold">
|
||||
<Trans>Authentication required</Trans>
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground mt-2 text-sm">
|
||||
You need to be logged in to view this page.
|
||||
<Trans>You need to be logged in to view this page.</Trans>
|
||||
</p>
|
||||
|
||||
<Button
|
||||
@ -46,7 +51,7 @@ export const DirectTemplateAuthPageView = () => {
|
||||
onClick={async () => handleChangeAccount()}
|
||||
loading={isSigningOut}
|
||||
>
|
||||
Login
|
||||
<Trans>Login</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user