From 19ec6fa2457898ec6998998152ab23e827f3ad0e Mon Sep 17 00:00:00 2001 From: GuoQing Liu <842607283@qq.com> Date: Wed, 13 May 2026 20:38:33 +0800 Subject: [PATCH] fix: fix i18n (#23174) * fix: fix embedding time locale * fix: fix setting i18n * fix: fix lpr setting item i18n * fix: fix code --- web/public/locales/en/config/cameras.json | 2 +- web/public/locales/en/config/global.json | 2 +- web/public/locales/en/views/settings.json | 26 ++++++++++++++----- .../config-form/section-configs/audio.ts | 1 + .../section-configs/audio_transcription.ts | 5 ++++ .../section-configs/face_recognition.ts | 2 +- .../config-form/section-configs/onvif.ts | 5 ++++ .../config-form/section-configs/record.ts | 11 ++++++++ .../config-form/section-configs/snapshots.ts | 2 +- .../theme/fields/KnownPlatesField.tsx | 16 +++++++++--- .../theme/fields/ReplaceRulesField.tsx | 25 ++++++++++++++---- .../theme/templates/ObjectFieldTemplate.tsx | 5 +++- web/src/pages/Explore.tsx | 8 +++++- 13 files changed, 89 insertions(+), 21 deletions(-) diff --git a/web/public/locales/en/config/cameras.json b/web/public/locales/en/config/cameras.json index 4f2c0ea01..f645dd33a 100644 --- a/web/public/locales/en/config/cameras.json +++ b/web/public/locales/en/config/cameras.json @@ -950,4 +950,4 @@ "label": "Original camera state", "description": "Keep track of original state of camera." } -} +} \ No newline at end of file diff --git a/web/public/locales/en/config/global.json b/web/public/locales/en/config/global.json index 61f0e41cc..b10f0a7af 100644 --- a/web/public/locales/en/config/global.json +++ b/web/public/locales/en/config/global.json @@ -1597,4 +1597,4 @@ "description": "Ignore time synchronization differences between camera and Frigate server for ONVIF communication." } } -} +} \ No newline at end of file diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index b73534d5d..756e2ddb3 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -1659,12 +1659,17 @@ "continuous": "Continuous" } }, - "snapshot": { - "retainMode": { - "all": "All", - "motion": "Motion", - "active_objects": "Active Objects" - } + "retainMode": { + "all": "All", + "motion": "Motion", + "active_objects": "Active Objects" + }, + "previewQuality": { + "very_high": "Very High", + "high": "High", + "medium": "Medium", + "low": "Low", + "very_low": "Very Low" }, "ui": { "timeFormat": { @@ -1700,7 +1705,14 @@ }, "onvif": { "profileAuto": "Auto", - "profileLoading": "Loading profiles..." + "profileLoading": "Loading profiles...", + "autotracking": { + "zooming": { + "disabled": "Disabled", + "absolute": "Absolute", + "relative": "Relative" + } + } }, "modelSize": { "small": "Small", diff --git a/web/src/components/config-form/section-configs/audio.ts b/web/src/components/config-form/section-configs/audio.ts index 31f19e93d..dda6198c5 100644 --- a/web/src/components/config-form/section-configs/audio.ts +++ b/web/src/components/config-form/section-configs/audio.ts @@ -42,6 +42,7 @@ const audio: SectionConfigOverrides = { "filters.*": { "ui:options": { additionalPropertyKeyReadonly: true, + isAudioLabels: true, }, }, listen: { diff --git a/web/src/components/config-form/section-configs/audio_transcription.ts b/web/src/components/config-form/section-configs/audio_transcription.ts index fbc0c1dc0..0983708a2 100644 --- a/web/src/components/config-form/section-configs/audio_transcription.ts +++ b/web/src/components/config-form/section-configs/audio_transcription.ts @@ -25,6 +25,11 @@ const audioTranscription: SectionConfigOverrides = { hiddenFields: ["enabled_in_config", "live_enabled"], advancedFields: ["language", "device", "model_size"], overrideFields: ["enabled", "live_enabled"], + uiSchema: { + model_size: { + "ui:options": { size: "xs", enumI18nPrefix: "modelSize" }, + }, + }, }, global: { fieldOrder: ["enabled", "language", "device", "model_size"], diff --git a/web/src/components/config-form/section-configs/face_recognition.ts b/web/src/components/config-form/section-configs/face_recognition.ts index 9d346b26c..031152fff 100644 --- a/web/src/components/config-form/section-configs/face_recognition.ts +++ b/web/src/components/config-form/section-configs/face_recognition.ts @@ -65,7 +65,7 @@ const faceRecognition: SectionConfigOverrides = { ], uiSchema: { model_size: { - "ui:options": { size: "xs" }, + "ui:options": { size: "xs", enumI18nPrefix: "modelSize" }, }, }, }, diff --git a/web/src/components/config-form/section-configs/onvif.ts b/web/src/components/config-form/section-configs/onvif.ts index acfb68f16..71163a034 100644 --- a/web/src/components/config-form/section-configs/onvif.ts +++ b/web/src/components/config-form/section-configs/onvif.ts @@ -39,6 +39,11 @@ const onvif: SectionConfigOverrides = { track: { "ui:widget": "objectLabels", }, + zooming: { + "ui:options": { + enumI18nPrefix: "onvif.autotracking.zooming", + }, + }, }, }, }, diff --git a/web/src/components/config-form/section-configs/record.ts b/web/src/components/config-form/section-configs/record.ts index 1d47454d7..0e0da3069 100644 --- a/web/src/components/config-form/section-configs/record.ts +++ b/web/src/components/config-form/section-configs/record.ts @@ -49,6 +49,17 @@ const record: SectionConfigOverrides = { "ui:options": { suppressMultiSchema: true, size: "lg" }, }, }, + "alerts.retain.mode": { + "ui:options": { enumI18nPrefix: "retainMode" }, + }, + "detections.retain.mode": { + "ui:options": { enumI18nPrefix: "retainMode" }, + }, + "preview.quality": { + "ui:options": { + enumI18nPrefix: "previewQuality", + }, + }, }, }, global: { diff --git a/web/src/components/config-form/section-configs/snapshots.ts b/web/src/components/config-form/section-configs/snapshots.ts index 48d6fe92a..94bfd9dc8 100644 --- a/web/src/components/config-form/section-configs/snapshots.ts +++ b/web/src/components/config-form/section-configs/snapshots.ts @@ -37,7 +37,7 @@ const snapshots: SectionConfigOverrides = { }, "retain.mode": { "ui:options": { - enumI18nPrefix: "snapshot.retainMode", + enumI18nPrefix: "retainMode", }, }, }, diff --git a/web/src/components/config-form/theme/fields/KnownPlatesField.tsx b/web/src/components/config-form/theme/fields/KnownPlatesField.tsx index e47c29867..44577ff9f 100644 --- a/web/src/components/config-form/theme/fields/KnownPlatesField.tsx +++ b/web/src/components/config-form/theme/fields/KnownPlatesField.tsx @@ -28,7 +28,11 @@ export function KnownPlatesField(props: FieldProps) { | ConfigFormContext | undefined; - const { t } = useTranslation(["views/settings", "common"]); + const configNamespace = + formContext?.i18nNamespace ?? + (formContext?.level === "camera" ? "config/cameras" : "config/global"); + const { t: fallbackT } = useTranslation(["common", configNamespace]); + const t = formContext?.t ?? fallbackT; const data: KnownPlatesData = useMemo(() => { if (!formData || typeof formData !== "object" || Array.isArray(formData)) { @@ -39,8 +43,14 @@ export function KnownPlatesField(props: FieldProps) { const entries = useMemo(() => Object.entries(data), [data]); - const title = (schema as RJSFSchema).title; - const description = (schema as RJSFSchema).description; + const id = idSchema?.$id ?? props.name; + const sectionPrefix = formContext?.sectionI18nPrefix; + + const title = + t(`${sectionPrefix}.${id}.label`) ?? (schema as RJSFSchema).title; + const description = + t(`${sectionPrefix}.${id}.description`) ?? + (schema as RJSFSchema).description; const hasItems = entries.length > 0; const emptyPath = useMemo(() => [] as FieldPathList, []); diff --git a/web/src/components/config-form/theme/fields/ReplaceRulesField.tsx b/web/src/components/config-form/theme/fields/ReplaceRulesField.tsx index 08c5c781f..da93f6ce6 100644 --- a/web/src/components/config-form/theme/fields/ReplaceRulesField.tsx +++ b/web/src/components/config-form/theme/fields/ReplaceRulesField.tsx @@ -47,7 +47,11 @@ export function ReplaceRulesField(props: FieldProps) { | ConfigFormContext | undefined; - const { t } = useTranslation(["common"]); + const configNamespace = + formContext?.i18nNamespace ?? + (formContext?.level === "camera" ? "config/cameras" : "config/global"); + const { t: fallbackT } = useTranslation(["common", configNamespace]); + const t = formContext?.t ?? fallbackT; const rules: ReplaceRule[] = useMemo(() => { if (!Array.isArray(formData)) { @@ -60,10 +64,21 @@ export function ReplaceRulesField(props: FieldProps) { () => getItemSchema(schema as RJSFSchema), [schema], ); - const title = (schema as RJSFSchema).title; - const description = (schema as RJSFSchema).description; - const patternTitle = getPropertyTitle(itemSchema, "pattern"); - const replacementTitle = getPropertyTitle(itemSchema, "replacement"); + + const id = idSchema?.$id ?? props.name; + const sectionPrefix = formContext?.sectionI18nPrefix; + + const title = + t(`${sectionPrefix}.${id}.label`) ?? (schema as RJSFSchema).title; + const description = + t(`${sectionPrefix}.${id}.description`) ?? + (schema as RJSFSchema).description; + const patternTitle = + t(`${sectionPrefix}.${id}.pattern.label`) ?? + getPropertyTitle(itemSchema, "pattern"); + const replacementTitle = + t(`${sectionPrefix}.${id}.replacement.label`) ?? + getPropertyTitle(itemSchema, "replacement"); const hasItems = rules.length > 0; const emptyPath = useMemo(() => [] as FieldPathList, []); diff --git a/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx b/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx index 78f08098c..bdd61eb1f 100644 --- a/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx +++ b/web/src/components/config-form/theme/templates/ObjectFieldTemplate.tsx @@ -210,6 +210,9 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { (p.content.props as RjsfElementProps).uiSchema?.["ui:options"] ?.advanced !== true, ); + + const isAudioLabels = uiSchema?.["ui:options"]?.isAudioLabels === true; + const hasModifiedAdvanced = advancedProps.some((prop) => checkSubtreeModified([...fieldPath, prop.name]), ); @@ -243,7 +246,7 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { const path = fieldPathId?.path; const filterObjectLabel = path ? getFilterObjectLabel(path) : undefined; const translatedFilterLabel = filterObjectLabel - ? getTranslatedLabel(filterObjectLabel, "object") + ? getTranslatedLabel(filterObjectLabel, isAudioLabels ? "audio" : "object") : undefined; if (path) { translationPath = buildTranslationPath( diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 35860ed35..b5b19c8af 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -24,6 +24,7 @@ import useSWR from "swr"; import useSWRInfinite from "swr/infinite"; import { useDocDomain } from "@/hooks/use-doc-domain"; import { JINA_EMBEDDING_MODELS } from "@/lib/const"; +import { useDateLocale } from "@/hooks/use-date-locale"; const API_LIMIT = 25; @@ -43,6 +44,8 @@ export default function Explore() { const { t } = useTranslation(["views/explore"]); const { getLocaleDocUrl } = useDocDomain(); + const dateLocale = useDateLocale(); + const { data: config } = useSWR("config", { revalidateOnFocus: false, }); @@ -417,7 +420,10 @@ export default function Explore() { )} {reindexState.time_remaining >= 0 && - (formatSecondsToDuration(reindexState.time_remaining) || + (formatSecondsToDuration( + reindexState.time_remaining, + dateLocale, + ) || t( "exploreIsUnavailable.embeddingsReindexing.finishingShortly", ))}