Add translations

This commit is contained in:
Ondřej 2024-02-06 23:03:26 +01:00
parent 5792decc8f
commit a8903e7279
10 changed files with 102 additions and 12 deletions

View file

@ -1,3 +1,65 @@
export function t(key: string) {
return key;
import { DEFAULT_LANG } from "./config";
import type { Lang } from "./lang";
type AnyString = string & {};
// Reference
const en = {
website_name: "Let's stop dirty money",
website_description: undefined,
switch_country: "Switch country",
"form.first_name": "First name",
"form.last_name": "Last name",
"form.variant": "I'm most concerned that",
"form.variant.1": "EPH destroys global climate",
"form.variant.2": "EPH fuels energy crisis",
"form.variant.3": "EPH undermines democracy",
"form.gender": undefined,
"form.gender.f": undefined,
"form.gender.m": undefined,
"form.branch": "I'm client of",
"form.letter": "Letter body",
"form.email": "E-mail",
"form.phone": "Phone number",
"form.send": "Send",
"confirm_email.title": "Confirm your e-mail",
"confirm_email.body": "Click the link below to verify your e-mail address.",
"confirm_email.link": "Confirm",
} as const;
type TranslationKey = keyof typeof en;
type Translation = Partial<Record<TranslationKey | AnyString, string | undefined>>;
const cs: Translation = {
website_name: "Zastavme špinavé prachy",
switch_country: "Změnit zemi",
"form.first_name": "Jméno",
"form.last_name": "Příjmení",
"form.variant": "Nejvíc mi vadí, že",
"form.variant.1": "EPH ničí klima",
"form.variant.2": "EPH způsobuje energetickou krizi",
"form.variant.3": "EPH ohrožuje demokracii",
"form.gender": "Preferuju být oslován/a v",
"form.gender.f": "ženském rodě",
"form.gender.m": "mužském rodě",
"form.branch": "Jsem klient/ka pobočky",
"form.letter": "Text dopisu",
"form.email": "E-mail",
"form.phone": "Telefonní číslo",
"form.send": "Odeslat",
"confirm_email.title": "Potvrď svůj e-mail",
"confirm_email.body": "Kliknutím na následující odkaz potvrdíš svou e-mailovou adresu.",
"confirm_email.link": "Potvrdit e-mail",
};
const de: Translation = {};
const translations: Record<Lang, Translation> = { en, cs, de };
export type TranslateFn = (key: TranslationKey | AnyString) => string;
export function makeT(lang: Lang = DEFAULT_LANG): TranslateFn {
return function t(key) {
return translations[lang]?.[key] ?? key;
};
}

View file

@ -2,6 +2,6 @@ import { LANGUAGES } from "./config.ts";
export type Lang = (typeof LANGUAGES)[number];
export function isLang(str: string): str is Lang {
return (LANGUAGES as ReadonlyArray<string>).includes(str);
export function isLang(str: string | undefined): str is Lang {
return !!str && (LANGUAGES as ReadonlyArray<string>).includes(str);
}

View file

@ -1,5 +1,14 @@
---
import { t } from "../i18n";
import { DEFAULT_LANG } from "../config";
import { makeT } from "../i18n";
import type { Lang } from "../lang";
interface Props {
lang?: Lang;
}
const { lang = DEFAULT_LANG } = Astro.props;
const t = makeT(lang);
---
<html>

View file

@ -1,5 +1,5 @@
---
import { t } from "../i18n";
import { makeT } from "../i18n";
import Button from "../ui/Button.astro";
import Dialog from "../ui/Dialog.astro";
import Checkbox from "../ui/Checkbox.astro";
@ -14,6 +14,7 @@ interface Props {
}
const { lang, options = {} } = Astro.props;
const t = makeT(lang);
const result = letterOptionsSchema.safeParse(options);
---

View file

@ -2,7 +2,7 @@
import Field from "../ui/Field.astro";
import Radio from "../ui/Radio.astro";
import RadioGroup from "../ui/RadioGroup.astro";
import { t } from "../i18n";
import { makeT } from "../i18n";
import Label from "../ui/Label.astro";
import Select from "../ui/Select.astro";
import type { Lang } from "../lang";
@ -12,6 +12,7 @@ interface Props {
}
const { lang } = Astro.props;
const t = makeT(lang);
interface Branch {
id: number;

View file

@ -27,7 +27,7 @@ const responseSchema = z.object({
data: z.record(z.any()),
});
function convertObjectKeys<T>(
function convertObjectKeys(
obj: Record<string, any>,
fn: (key: string) => string
): Record<string, any> {

View file

@ -1,12 +1,14 @@
import html from "html-template-tag";
import { t } from "../i18n";
import { makeT } from "../i18n";
import type { Lang } from "../lang";
interface Params {
lang: string;
lang: Lang;
href: string;
}
export default function renderMail({ lang, href }: Params) {
const t = makeT(lang);
const htmlBody = html`<!DOCTYPE html>
<html lang="${lang}">
<head>

14
src/middleware.ts Normal file
View file

@ -0,0 +1,14 @@
import type { MiddlewareHandler } from "astro";
import { isLang } from "./lang";
export const onRequest: MiddlewareHandler = ({ params }, next) => {
// Return 404 on unknown languages
// On statically generated routes, this behavior is automatically handled by getStaticPaths.
if (params.lang && !isLang(params.lang)) {
return new Response(null, {
status: 404,
statusText: "Not found",
});
}
return next();
};

View file

@ -1,5 +1,5 @@
---
import { t } from "../../i18n";
import { makeT } from "../../i18n";
import Layout from "../../layouts/Layout.astro";
import LetterBodyForm from "../../letter/LetterBodyForm.astro";
import LetterOptionsForm from "../../letter/LetterOptionsForm.astro";
@ -7,6 +7,7 @@ import LetterOptionsForm from "../../letter/LetterOptionsForm.astro";
export { getStaticPaths } from "../../static-paths";
const { lang } = Astro.params;
const t = makeT(lang);
---
<Layout>

View file

@ -12,7 +12,7 @@ export async function GET({ params: { lang } }: APIContext<never, Params>) {
const { plainBody } = renderMail({ lang, href: "" });
return new Response(plainBody, {
headers: {
"Content-Type": "text/plain",
"Content-Type": "text/plain;charset=utf-8",
},
});
}