⬆️ Upgrade i18next (#1592)

This commit is contained in:
Luke Vella 2025-03-01 15:56:56 +00:00 committed by GitHub
parent 8c84a92a58
commit bff2dd3a20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 56 additions and 65 deletions

View file

@ -1,21 +1,19 @@
import "react-i18next";
import "i18next";
import type blog from "../public/locales/en/blog.json";
import type common from "../public/locales/en/common.json";
import type home from "../public/locales/en/home.json";
import type pricing from "../public/locales/en/pricing.json";
interface I18nNamespaces {
common: typeof common;
home: typeof home;
pricing: typeof pricing;
blog: typeof blog;
}
declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "common";
resources: I18nNamespaces;
resources: {
common: typeof common;
home: typeof home;
pricing: typeof pricing;
blog: typeof blog;
};
returnNull: false;
}
}

View file

@ -1,6 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

View file

@ -23,7 +23,7 @@
"accept-language-parser": "^1.5.0",
"dayjs": "^1.11.10",
"gray-matter": "^4.0.3",
"i18next": "^22.4.9",
"i18next": "^24.2.2",
"i18next-icu": "^2.3.0",
"intl-messageformat": "^10.3.4",
"lodash": "^4.17.21",
@ -31,7 +31,7 @@
"next-i18next": "^13.0.3",
"next-mdx-remote": "^5.0.0",
"next-seo": "^6.1.0",
"react-i18next": "^12.1.4",
"react-i18next": "^15.4.1",
"react-use": "^17.4.0"
},
"devDependencies": {

View file

@ -6,7 +6,7 @@ import { BigTestimonial, Marketing, MentionedBy } from "@/components/marketing";
import { getTranslation } from "@/i18n/server";
export default async function Page({ params }: { params: { locale: string } }) {
const { t } = await getTranslation(params.locale, ["common", "home"]);
const { t } = await getTranslation(params.locale, "home");
return (
<Marketing>
<MarketingHero

View file

@ -4,17 +4,21 @@ import { BigTestimonial, Marketing, MentionedBy } from "@/components/marketing";
import { getTranslation } from "@/i18n/server";
export default async function Page({ params }: { params: { locale: string } }) {
const { t } = await getTranslation(params.locale, ["common", "home"]);
const { t } = await getTranslation(params.locale, ["home", "common"]);
return (
<Marketing>
<MarketingHero
title={t("home:headline", {
title={t("headline", {
defaultValue: "Ditch the back-and-forth emails",
ns: "home",
})}
description={t("home:subheading", {
description={t("subheading", {
defaultValue: "Streamline your scheduling process and save time",
ns: "home",
})}
callToAction={t("getStarted", {
ns: "common",
})}
callToAction={t("getStarted")}
/>
<Bonus t={t} />
<BigTestimonial />
@ -30,10 +34,11 @@ export async function generateMetadata({
}) {
const { t } = await getTranslation(params.locale, "home");
return {
title: t("home:metaTitle", {
title: t("metaTitle", {
defaultValue: "Rallly: Group Scheduling Tool",
ns: "home",
}),
description: t("home:metaDescription", {
description: t("metaDescription", {
defaultValue:
"Create polls and vote to find the best day or time. A free alternative to Doodle.",
}),

View file

@ -8,7 +8,7 @@ import { linkToApp } from "@/lib/linkToApp";
import { PriceTables } from "./pricing-table";
const FAQ = async ({ t }: { t: TFunction }) => {
const FAQ = async ({ t }: { t: TFunction<"pricing"> }) => {
return (
<section>
<h2 className="text-2xl font-bold">
@ -123,7 +123,7 @@ const FAQ = async ({ t }: { t: TFunction }) => {
};
export default async function Page({ params }: { params: { locale: string } }) {
const { t } = await getTranslation(params.locale, ["common", "pricing"]);
const { t } = await getTranslation(params.locale, "pricing");
return (
<article className="mx-auto max-w-3xl space-y-6">
<header className="space-y-2 sm:p-6 sm:text-center">

View file

@ -10,7 +10,7 @@ import { Trans } from "react-i18next/TransWithoutContext";
import { BonusItem } from "@/components/home/bonus-item";
export async function Bonus({ t }: { t: TFunction }) {
export async function Bonus({ t }: { t: TFunction<"home" | "common"> }) {
const userCount = await prisma.user.count();
return (
<div className="mx-auto flex flex-wrap justify-center gap-2 whitespace-nowrap text-center sm:grid-cols-4 sm:gap-4 sm:gap-x-8">

View file

@ -21,13 +21,14 @@ const initI18next = async (lng: string, ns: Namespace) => {
return i18nInstance;
};
export async function getTranslation(
export async function getTranslation<Ns extends Namespace>(
locale: string,
ns: Namespace = defaultNS,
ns?: Ns,
) {
const i18nextInstance = await initI18next(locale, ns);
const fixedNs = ns ?? defaultNS;
const i18nextInstance = await initI18next(locale, fixedNs);
return {
t: i18nextInstance.getFixedT(locale, Array.isArray(ns) ? ns[0] : ns),
t: i18nextInstance.getFixedT<Ns>(locale),
i18n: i18nextInstance,
};
}

View file

@ -1,5 +1,5 @@
import allLanguages from "@rallly/languages";
import type { InitOptions } from "i18next";
import type { InitOptions, Namespace } from "i18next";
export const fallbackLng = "en";
export const languages = Object.keys(allLanguages);
@ -7,7 +7,7 @@ export const defaultNS = "common";
export function getOptions(
lng = fallbackLng,
ns: string | string[] = defaultNS,
ns: Namespace = defaultNS,
): InitOptions {
return {
// debug: true,

View file

@ -54,7 +54,7 @@
"cookie": "^0.7.0",
"crypto": "^1.0.1",
"dayjs": "^1.11.10",
"i18next": "^22.4.9",
"i18next": "^24.2.2",
"i18next-icu": "^2.3.0",
"i18next-resources-to-backend": "^1.1.4",
"ics": "^3.1.0",
@ -75,7 +75,7 @@
"react-big-calendar": "^1.8.1",
"react-hook-form": "^7.42.1",
"react-hook-form-persist": "^3.0.0",
"react-i18next": "^12.1.4",
"react-i18next": "^15.4.1",
"react-remove-scroll": "^2.5.6",
"react-use": "^17.4.0",
"smoothscroll-polyfill": "^0.4.4",

View file

@ -6,41 +6,25 @@ import { checkApiAuthorization } from "@/utils/api-auth";
/**
* Remove polls and corresponding data that have been marked deleted for more than 7 days.
*/
export async function POST(req: Request) {
export async function POST() {
const unauthorized = checkApiAuthorization();
if (unauthorized) return unauthorized;
const options = (await req.json()) as { take?: number } | undefined;
// First get the ids of all the polls that have been marked as deleted for at least 7 days
const deletedPolls = await prisma.poll.findMany({
select: {
id: true,
},
const deletedPolls = await prisma.poll.deleteMany({
where: {
deleted: true,
deletedAt: {
lt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
},
},
take: options?.take ?? 1000,
});
const deletedPollIds = deletedPolls.map((poll) => poll.id);
const { count: deletedPollCount } = await prisma.poll.deleteMany({
where: {
id: {
in: deletedPollIds,
},
},
});
return NextResponse.json({
success: true,
summary: {
deleted: {
polls: deletedPollCount,
polls: deletedPolls.count,
},
},
});

View file

@ -1,5 +1,5 @@
import allLanguages from "@rallly/languages";
import type { InitOptions } from "i18next";
import type { InitOptions, Namespace } from "i18next";
export const fallbackLng = "en";
export const languages = Object.keys(allLanguages);
@ -7,7 +7,7 @@ export const defaultNS = "app";
export function getOptions(
lng = fallbackLng,
ns: string | string[] = defaultNS,
ns: Namespace = defaultNS,
): InitOptions {
return {
supportedLngs: languages,