Add translations
This commit is contained in:
parent
5792decc8f
commit
a8903e7279
10 changed files with 102 additions and 12 deletions
66
src/i18n.ts
66
src/i18n.ts
|
@ -1,3 +1,65 @@
|
||||||
export function t(key: string) {
|
import { DEFAULT_LANG } from "./config";
|
||||||
return key;
|
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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@ import { LANGUAGES } from "./config.ts";
|
||||||
|
|
||||||
export type Lang = (typeof LANGUAGES)[number];
|
export type Lang = (typeof LANGUAGES)[number];
|
||||||
|
|
||||||
export function isLang(str: string): str is Lang {
|
export function isLang(str: string | undefined): str is Lang {
|
||||||
return (LANGUAGES as ReadonlyArray<string>).includes(str);
|
return !!str && (LANGUAGES as ReadonlyArray<string>).includes(str);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
<html>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
import { t } from "../i18n";
|
import { makeT } from "../i18n";
|
||||||
import Button from "../ui/Button.astro";
|
import Button from "../ui/Button.astro";
|
||||||
import Dialog from "../ui/Dialog.astro";
|
import Dialog from "../ui/Dialog.astro";
|
||||||
import Checkbox from "../ui/Checkbox.astro";
|
import Checkbox from "../ui/Checkbox.astro";
|
||||||
|
@ -14,6 +14,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { lang, options = {} } = Astro.props;
|
const { lang, options = {} } = Astro.props;
|
||||||
|
const t = makeT(lang);
|
||||||
const result = letterOptionsSchema.safeParse(options);
|
const result = letterOptionsSchema.safeParse(options);
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import Field from "../ui/Field.astro";
|
import Field from "../ui/Field.astro";
|
||||||
import Radio from "../ui/Radio.astro";
|
import Radio from "../ui/Radio.astro";
|
||||||
import RadioGroup from "../ui/RadioGroup.astro";
|
import RadioGroup from "../ui/RadioGroup.astro";
|
||||||
import { t } from "../i18n";
|
import { makeT } from "../i18n";
|
||||||
import Label from "../ui/Label.astro";
|
import Label from "../ui/Label.astro";
|
||||||
import Select from "../ui/Select.astro";
|
import Select from "../ui/Select.astro";
|
||||||
import type { Lang } from "../lang";
|
import type { Lang } from "../lang";
|
||||||
|
@ -12,6 +12,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { lang } = Astro.props;
|
const { lang } = Astro.props;
|
||||||
|
const t = makeT(lang);
|
||||||
|
|
||||||
interface Branch {
|
interface Branch {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
|
@ -27,7 +27,7 @@ const responseSchema = z.object({
|
||||||
data: z.record(z.any()),
|
data: z.record(z.any()),
|
||||||
});
|
});
|
||||||
|
|
||||||
function convertObjectKeys<T>(
|
function convertObjectKeys(
|
||||||
obj: Record<string, any>,
|
obj: Record<string, any>,
|
||||||
fn: (key: string) => string
|
fn: (key: string) => string
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import html from "html-template-tag";
|
import html from "html-template-tag";
|
||||||
import { t } from "../i18n";
|
import { makeT } from "../i18n";
|
||||||
|
import type { Lang } from "../lang";
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
lang: string;
|
lang: Lang;
|
||||||
href: string;
|
href: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function renderMail({ lang, href }: Params) {
|
export default function renderMail({ lang, href }: Params) {
|
||||||
|
const t = makeT(lang);
|
||||||
const htmlBody = html`<!DOCTYPE html>
|
const htmlBody = html`<!DOCTYPE html>
|
||||||
<html lang="${lang}">
|
<html lang="${lang}">
|
||||||
<head>
|
<head>
|
||||||
|
|
14
src/middleware.ts
Normal file
14
src/middleware.ts
Normal 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();
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
import { t } from "../../i18n";
|
import { makeT } from "../../i18n";
|
||||||
import Layout from "../../layouts/Layout.astro";
|
import Layout from "../../layouts/Layout.astro";
|
||||||
import LetterBodyForm from "../../letter/LetterBodyForm.astro";
|
import LetterBodyForm from "../../letter/LetterBodyForm.astro";
|
||||||
import LetterOptionsForm from "../../letter/LetterOptionsForm.astro";
|
import LetterOptionsForm from "../../letter/LetterOptionsForm.astro";
|
||||||
|
@ -7,6 +7,7 @@ import LetterOptionsForm from "../../letter/LetterOptionsForm.astro";
|
||||||
export { getStaticPaths } from "../../static-paths";
|
export { getStaticPaths } from "../../static-paths";
|
||||||
|
|
||||||
const { lang } = Astro.params;
|
const { lang } = Astro.params;
|
||||||
|
const t = makeT(lang);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|
|
@ -12,7 +12,7 @@ export async function GET({ params: { lang } }: APIContext<never, Params>) {
|
||||||
const { plainBody } = renderMail({ lang, href: "" });
|
const { plainBody } = renderMail({ lang, href: "" });
|
||||||
return new Response(plainBody, {
|
return new Response(plainBody, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/plain",
|
"Content-Type": "text/plain;charset=utf-8",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue