Translations for Email Notifications (#1278)

Co-authored-by: Niko Heller <hellerniko@gmail.com>
This commit is contained in:
Luke Vella 2024-09-02 19:30:58 +01:00 committed by GitHub
parent aa52a0f26f
commit f4218c3115
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 1071 additions and 970 deletions

View file

@ -0,0 +1,16 @@
import { i18nDefaultConfig, i18nInstance } from "../i18n";
import { EmailContext } from "../types";
i18nInstance.init({
...i18nDefaultConfig,
initImmediate: true,
});
export const previewEmailContext: EmailContext = {
logoUrl: "https://rallly-public.s3.amazonaws.com/images/rallly-logo-mark.png",
baseUrl: "https://rallly.co",
domain: "rallly.co",
supportEmail: "support@rallly.co",
i18n: i18nInstance,
t: i18nInstance.getFixedT("en"),
};

View file

@ -0,0 +1,72 @@
import {
Body,
Container,
Head,
Html,
Img,
Preview,
Section,
} from "@react-email/components";
import { Trans } from "react-i18next/TransWithoutContext";
import { EmailContext } from "../types";
import { darkTextColor, fontFamily, Link, Text } from "./styled-components";
export interface EmailLayoutProps {
preview: string;
ctx: EmailContext;
}
const containerStyles = {
maxWidth: "480px",
margin: "0 auto",
background: "white",
fontFamily,
padding: "32px 8px",
color: darkTextColor,
};
export const EmailLayout = ({
preview,
children,
ctx,
}: React.PropsWithChildren<EmailLayoutProps>) => {
const { logoUrl } = ctx;
return (
<Html>
<Head />
<Preview>{preview}</Preview>
<Body style={{ backgroundColor: "#FFFFFF" }}>
<Container style={containerStyles}>
<Img
src={logoUrl}
width="32"
height="32"
style={{
marginBottom: 32,
}}
alt="Rallly Logo"
/>
{children}
<Section style={{ marginTop: 32 }}>
<Text light={true}>
<Trans
i18n={ctx.i18n}
t={ctx.t}
i18nKey="common_poweredBy"
ns="emails"
defaults="Powered by <a>{{domain}}</a>"
values={{ domain: "rallly.co" }}
components={{
a: (
<Link href="https://rallly.co?utm_source=email&utm_medium=transactional" />
),
}}
/>
</Text>
</Section>
</Container>
</Body>
</Html>
);
};

View file

@ -0,0 +1,60 @@
import { Section } from "@react-email/section";
import { Trans } from "react-i18next/TransWithoutContext";
import type { EmailContext } from "../types";
import { EmailLayout } from "./email-layout";
import { Button, Link, Text } from "./styled-components";
export interface NotificationBaseProps {
title: string;
pollUrl: string;
disableNotificationsUrl: string;
ctx: EmailContext;
}
export interface NotificationEmailProps extends NotificationBaseProps {
preview: string;
}
export const NotificationEmail = ({
pollUrl,
disableNotificationsUrl,
preview,
children,
ctx,
}: React.PropsWithChildren<NotificationEmailProps>) => {
const { domain } = ctx;
return (
<EmailLayout ctx={ctx} preview={preview}>
{children}
<Section style={{ marginTop: 32, marginBottom: 32 }}>
<Button href={pollUrl}>
{ctx.t("common_viewOn", {
ns: "emails",
defaultValue: "View on {{domain}}",
domain,
})}
</Button>
</Section>
<Text light={true}>
<Trans
i18n={ctx.i18n}
t={ctx.t}
i18nKey="common_disableNotifications"
ns="emails"
defaults="If you would like to stop receiving updates you can <a>turn notifications off</a>."
components={{
a: (
<Link
className="whitespace-nowrap"
href={disableNotificationsUrl}
/>
),
}}
/>
</Text>
</EmailLayout>
);
};
export default NotificationEmail;

View file

@ -0,0 +1,158 @@
import {
Button as UnstyledButton,
Heading as UnstyledHeading,
Link as UnstyledLink,
LinkProps,
Section as UnstyledSection,
SectionProps,
Text as UnstyledText,
TextProps,
} from "@react-email/components";
import type { EmailContext } from "../types";
export const lightTextColor = "#4B5563";
export const darkTextColor = "#1F2937";
export const borderColor = "#E2E8F0";
export const Text = (
props: TextProps & { light?: boolean; small?: boolean },
) => {
const { light, small, ...forwardProps } = props;
return (
<UnstyledText
{...forwardProps}
style={{
margin: "16px 0",
fontFamily,
fontSize: small ? "14px" : "16px",
color: light ? lightTextColor : darkTextColor,
lineHeight: "1.5",
...props.style,
}}
/>
);
};
export const Domain = ({ ctx }: { ctx: EmailContext }) => {
const { baseUrl, domain } = ctx;
return <Link href={baseUrl}>{domain}</Link>;
};
export const Button = (props: React.ComponentProps<typeof UnstyledButton>) => {
return (
<UnstyledButton
{...props}
className={props.className}
style={{
backgroundColor: "#4F46E5",
borderRadius: "4px",
padding: "12px 14px",
fontFamily,
boxSizing: "border-box",
display: "block",
width: "100%",
maxWidth: "100%",
textAlign: "center",
fontSize: "16px",
color: "white",
}}
/>
);
};
export const Link = (props: LinkProps) => {
return (
<UnstyledLink
{...props}
style={{ color: "#4F46E5", fontFamily, ...props.style }}
/>
);
};
const fontSize = {
h1: "20px",
h2: "18px",
h3: "16px",
h4: "16px",
h5: "14px",
h6: "12px",
};
export const Heading = (
props: React.ComponentProps<typeof UnstyledHeading>,
) => {
const { as = "h1" } = props;
return (
<UnstyledHeading
{...props}
as={as}
style={{
fontSize: fontSize[as],
...props.style,
}}
/>
);
};
export const SubHeadingText = (props: TextProps) => {
return (
<UnstyledText
{...props}
style={{
marginBottom: "16px",
marginTop: "8px",
fontSize: 16,
color: "#374151",
fontFamily,
...props.style,
}}
/>
);
};
export const Section = (props: SectionProps) => {
return (
<UnstyledSection
{...props}
style={{ marginTop: "16px", marginBottom: "16px", ...props.style }}
/>
);
};
export const SmallText = (props: TextProps) => {
return (
<UnstyledText
{...props}
style={{
fontSize: "14px",
color: "#6B7280",
...props.style,
}}
/>
);
};
export const Card = (props: SectionProps) => {
return (
<Section
{...props}
style={{
borderRadius: "4px",
backgroundColor: "#F9FAFB",
paddingRight: "16px",
paddingLeft: "16px",
border: "1px solid #E2E8F0",
}}
/>
);
};
export const trackingWide = {
letterSpacing: 2,
};
export const fontFamily =
"'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif";
export const primaryColor = "#4F46E5";