bump react-i18next to 18 and fix i18next 27 types

react-i18next 18 peers i18next >= 27.0.1, so the two move together.
i18next 27's TFunction no longer fits the hand-written t signatures in
the config form, and it rejects undefined as the options argument.
This commit is contained in:
Josh Hawkins
2026-11-15 11:31:51 -06:00
parent e857074fd0
commit 055b4622cc
7 changed files with 25 additions and 38 deletions
+13 -31
View File
@@ -65,7 +65,7 @@
"react-dropzone": "^20.2.0",
"react-grid-layout": "^2.3.0",
"react-hook-form": "^7.92.0",
"react-i18next": "^17.1.13",
"react-i18next": "^18.0.1",
"react-icons": "^5.7.0",
"react-konva": "^19.3.2",
"react-markdown": "^10.1.0",
@@ -350,6 +350,7 @@
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -3183,9 +3184,6 @@
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -3203,9 +3201,6 @@
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -3223,9 +3218,6 @@
"ppc64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -3243,9 +3235,6 @@
"s390x"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -3263,9 +3252,6 @@
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -3283,9 +3269,6 @@
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -9166,25 +9149,24 @@
}
},
"node_modules/react-i18next": {
"version": "17.1.13",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.1.13.tgz",
"integrity": "sha512-ZxhOSlyKSKPtbUNPLwYMmE3w3bhoc0jaZJZ6bc7wGOT6cLbrbs//6m01HxCLtFdSjHvFpwFwoPhxxzIlHSs9cw==",
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-18.0.1.tgz",
"integrity": "sha512-kPWXI0E/lrPj3+/umcKNzYZVnWmEedLiamMzDIHfbs+5ANf6yzvlZsjKou8f7kaGkfMMDLIZBejV+4aSN1CyBw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.29.7",
"html-parse-stringify": "^4.0.1",
"use-sync-external-store": "^1.6.0"
"html-parse-stringify": "^4.0.1"
},
"engines": {
"node": ">=22"
},
"peerDependencies": {
"i18next": ">= 26.2.0",
"react": ">= 16.8.0",
"@types/react": "*",
"i18next": ">= 27.0.1",
"react": ">= 18.0.0",
"typescript": "^5 || ^6 || ^7"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"@types/react": {
"optional": true
},
"typescript": {
+1 -1
View File
@@ -77,7 +77,7 @@
"react-dropzone": "^20.2.0",
"react-grid-layout": "^2.3.0",
"react-hook-form": "^7.92.0",
"react-i18next": "^17.1.13",
"react-i18next": "^18.0.1",
"react-icons": "^5.7.0",
"react-konva": "^19.3.2",
"react-markdown": "^10.1.0",
@@ -56,7 +56,7 @@ export function ConfigFieldMessage({
<SeverityIcon severity={severity} />
<AlertDescription>
<div className="flex flex-col gap-1">
<span>{t(messageKey, values)}</span>
<span>{t(messageKey, values ?? {})}</span>
{docLink && (
<Link
to={getLocaleDocUrl(docLink)}
@@ -44,7 +44,9 @@ export function ConfigMessageBanner({ messages }: ConfigMessageBannerProps) {
className="flex items-center [&>svg+div]:translate-y-0 [&>svg]:static [&>svg~*]:pl-2"
>
<SeverityIcon severity={msg.severity} />
<AlertDescription>{t(msg.messageKey, msg.values)}</AlertDescription>
<AlertDescription>
{t(msg.messageKey, msg.values ?? {})}
</AlertDescription>
</Alert>
))}
</div>
@@ -6,6 +6,7 @@ import type {
} from "@rjsf/utils";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { LuCircleAlert } from "react-icons/lu";
import type { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
import {
buildTranslationPath,
@@ -93,7 +94,7 @@ const resolveErrorFieldLabel = ({
error: RJSFValidationError;
schema: RJSFSchema | undefined;
formContext?: ConfigFormContext;
t: (key: string, options?: Record<string, unknown>) => string;
t: TFunction;
i18n: ReturnType<typeof useTranslation>["i18n"];
}): string | undefined => {
const segments = parsePropertyPath(error.property || "");
@@ -5,6 +5,7 @@
* for RJSF form fields.
*/
import type { TFunction } from "i18next";
import type { ConfigFormContext } from "@/types/configForm";
import { getEffectiveAttributeLabels } from "@/utils/configUtil";
@@ -70,13 +71,13 @@ export function buildTranslationPath(
*/
export function resolveConfigTranslation(
i18n: { exists: (key: string, opts?: Record<string, unknown>) => boolean },
t: (key: string, opts?: Record<string, unknown>) => string,
t: TFunction,
translationPath: string,
suffix: "label" | "description",
sectionI18nPrefix?: string,
ns?: string,
): string | undefined {
const opts = ns ? { ns } : undefined;
const opts = ns ? { ns } : {};
if (
sectionI18nPrefix &&
+2 -1
View File
@@ -1,3 +1,4 @@
import type { TFunction } from "i18next";
import type { RendererComponent } from "@/components/config-form/sectionExtras/registry";
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
@@ -58,7 +59,7 @@ export type ConfigFormContext = {
fieldDocs?: Record<string, string>;
restartRequired?: string[];
requiresRestart?: boolean;
t?: (key: string, options?: Record<string, unknown>) => string;
t?: TFunction;
renderers?: Record<string, RendererComponent>;
isProfile?: boolean;
};