feat: web i18n (#1286)

This commit is contained in:
David Nguyen
2024-08-27 20:34:39 +09:00
committed by GitHub
parent 0829311214
commit 75c8772a02
294 changed files with 14846 additions and 2229 deletions

View File

@ -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}

View File

@ -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',
});

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>