diff --git a/web/package-lock.json b/web/package-lock.json
index dd6611a560..6eabb5b0da 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -47,7 +47,7 @@
"date-fns-tz": "^3.2.0",
"framer-motion": "^14.12.0",
"hls.js": "^1.7.4",
- "i18next": "^26.7.2",
+ "i18next": "^27.1.1",
"i18next-http-backend": "^4.0.10",
"idb-keyval": "^6.3.0",
"js-yaml": "^5.5.0",
@@ -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": [
@@ -6095,9 +6078,9 @@
}
},
"node_modules/i18next": {
- "version": "26.7.2",
- "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.7.2.tgz",
- "integrity": "sha512-RsOY3BeaCDdkk52NeJSFAXELQ2rQXBO/tOub6L8ue2ICPfR/FRDWDOfEDg4xMPd1SqO5goVYQ6i72qWEUB/PPw==",
+ "version": "27.1.1",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-27.1.1.tgz",
+ "integrity": "sha512-Qq3aJizS3RXQ30crhx9BKQLrHoa6GKpgrOJjVZ9vgj3EBxAddrmVfDNUrAMxSUimw5Jc+ld4SSZUAzf1q4nGyQ==",
"funding": [
{
"type": "individual",
@@ -6113,6 +6096,9 @@
}
],
"license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
"peerDependencies": {
"typescript": "^5 || ^6 || ^7"
},
@@ -9163,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": {
diff --git a/web/package.json b/web/package.json
index 10639278ec..f193fa1050 100644
--- a/web/package.json
+++ b/web/package.json
@@ -59,7 +59,7 @@
"date-fns-tz": "^3.2.0",
"framer-motion": "^14.12.0",
"hls.js": "^1.7.4",
- "i18next": "^26.7.2",
+ "i18next": "^27.1.1",
"i18next-http-backend": "^4.0.10",
"idb-keyval": "^6.3.0",
"js-yaml": "^5.5.0",
@@ -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",
diff --git a/web/src/components/config-form/ConfigFieldMessage.tsx b/web/src/components/config-form/ConfigFieldMessage.tsx
index 78921c1605..0a77399695 100644
--- a/web/src/components/config-form/ConfigFieldMessage.tsx
+++ b/web/src/components/config-form/ConfigFieldMessage.tsx
@@ -56,7 +56,7 @@ export function ConfigFieldMessage({
-
{t(messageKey, values)}
+
{t(messageKey, values ?? {})}
{docLink && (
-
{t(msg.messageKey, msg.values)}
+
+ {t(msg.messageKey, msg.values ?? {})}
+
))}
diff --git a/web/src/components/config-form/theme/templates/ErrorListTemplate.tsx b/web/src/components/config-form/theme/templates/ErrorListTemplate.tsx
index a0edc9e7ab..3ee0ca9f6f 100644
--- a/web/src/components/config-form/theme/templates/ErrorListTemplate.tsx
+++ b/web/src/components/config-form/theme/templates/ErrorListTemplate.tsx
@@ -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;
+ t: TFunction;
i18n: ReturnType["i18n"];
}): string | undefined => {
const segments = parsePropertyPath(error.property || "");
diff --git a/web/src/components/config-form/theme/utils/i18n.ts b/web/src/components/config-form/theme/utils/i18n.ts
index 2d18558f18..9fdd13186b 100644
--- a/web/src/components/config-form/theme/utils/i18n.ts
+++ b/web/src/components/config-form/theme/utils/i18n.ts
@@ -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) => boolean },
- t: (key: string, opts?: Record) => 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 &&
diff --git a/web/src/types/configForm.ts b/web/src/types/configForm.ts
index 0313db733c..3cc94ae95e 100644
--- a/web/src/types/configForm.ts
+++ b/web/src/types/configForm.ts
@@ -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;
restartRequired?: string[];
requiresRestart?: boolean;
- t?: (key: string, options?: Record) => string;
+ t?: TFunction;
renderers?: Record;
isProfile?: boolean;
};