mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-27 14:19:01 +03:00
Settings i18n improvements (#22571)
* i18n improvements for settings UI - deduplicate shared detector translation keys and centralize config translation resolution - add missing i18n keys * formatting
This commit is contained in:
@@ -7,7 +7,11 @@ import type {
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { LuCircleAlert } from "react-icons/lu";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { buildTranslationPath, humanizeKey } from "../utils";
|
||||
import {
|
||||
buildTranslationPath,
|
||||
resolveConfigTranslation,
|
||||
humanizeKey,
|
||||
} from "../utils";
|
||||
import type { ConfigFormContext } from "@/types/configForm";
|
||||
|
||||
type ErrorSchemaNode = RJSFSchema & {
|
||||
@@ -114,22 +118,15 @@ const resolveErrorFieldLabel = ({
|
||||
);
|
||||
|
||||
if (effectiveNamespace && translationPath) {
|
||||
const prefixedTranslationKey =
|
||||
sectionI18nPrefix && !translationPath.startsWith(`${sectionI18nPrefix}.`)
|
||||
? `${sectionI18nPrefix}.${translationPath}.label`
|
||||
: undefined;
|
||||
const translationKey = `${translationPath}.label`;
|
||||
|
||||
if (
|
||||
prefixedTranslationKey &&
|
||||
i18n.exists(prefixedTranslationKey, { ns: effectiveNamespace })
|
||||
) {
|
||||
return t(prefixedTranslationKey, { ns: effectiveNamespace });
|
||||
}
|
||||
|
||||
if (i18n.exists(translationKey, { ns: effectiveNamespace })) {
|
||||
return t(translationKey, { ns: effectiveNamespace });
|
||||
}
|
||||
const translated = resolveConfigTranslation(
|
||||
i18n,
|
||||
t,
|
||||
translationPath,
|
||||
"label",
|
||||
sectionI18nPrefix,
|
||||
effectiveNamespace,
|
||||
);
|
||||
if (translated) return translated;
|
||||
}
|
||||
|
||||
const schemaNode = resolveSchemaNodeForPath(schema, segments);
|
||||
|
||||
@@ -20,6 +20,7 @@ import { requiresRestartForFieldPath } from "@/utils/configUtil";
|
||||
import RestartRequiredIndicator from "@/components/indicators/RestartRequiredIndicator";
|
||||
import {
|
||||
buildTranslationPath,
|
||||
resolveConfigTranslation,
|
||||
getFilterObjectLabel,
|
||||
hasOverrideAtPath,
|
||||
humanizeKey,
|
||||
@@ -219,20 +220,16 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
// Try to get translated label, falling back to schema title, then RJSF label
|
||||
let finalLabel = label;
|
||||
if (effectiveNamespace && translationPath) {
|
||||
// Prefer camera-scoped translations when a section prefix is provided
|
||||
const prefixedTranslationKey =
|
||||
sectionI18nPrefix && !translationPath.startsWith(`${sectionI18nPrefix}.`)
|
||||
? `${sectionI18nPrefix}.${translationPath}.label`
|
||||
: undefined;
|
||||
const translationKey = `${translationPath}.label`;
|
||||
|
||||
if (
|
||||
prefixedTranslationKey &&
|
||||
i18n.exists(prefixedTranslationKey, { ns: effectiveNamespace })
|
||||
) {
|
||||
finalLabel = t(prefixedTranslationKey, { ns: effectiveNamespace });
|
||||
} else if (i18n.exists(translationKey, { ns: effectiveNamespace })) {
|
||||
finalLabel = t(translationKey, { ns: effectiveNamespace });
|
||||
const translatedLabel = resolveConfigTranslation(
|
||||
i18n,
|
||||
t,
|
||||
translationPath,
|
||||
"label",
|
||||
sectionI18nPrefix,
|
||||
effectiveNamespace,
|
||||
);
|
||||
if (translatedLabel) {
|
||||
finalLabel = translatedLabel;
|
||||
} else if (schemaTitle) {
|
||||
finalLabel = schemaTitle;
|
||||
} else if (translatedFilterObjectLabel) {
|
||||
@@ -330,18 +327,16 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
// Try to get translated description, falling back to schema description
|
||||
let finalDescription = description || "";
|
||||
if (effectiveNamespace && translationPath) {
|
||||
const prefixedDescriptionKey =
|
||||
sectionI18nPrefix && !translationPath.startsWith(`${sectionI18nPrefix}.`)
|
||||
? `${sectionI18nPrefix}.${translationPath}.description`
|
||||
: undefined;
|
||||
const descriptionKey = `${translationPath}.description`;
|
||||
if (
|
||||
prefixedDescriptionKey &&
|
||||
i18n.exists(prefixedDescriptionKey, { ns: effectiveNamespace })
|
||||
) {
|
||||
finalDescription = t(prefixedDescriptionKey, { ns: effectiveNamespace });
|
||||
} else if (i18n.exists(descriptionKey, { ns: effectiveNamespace })) {
|
||||
finalDescription = t(descriptionKey, { ns: effectiveNamespace });
|
||||
const translatedDescription = resolveConfigTranslation(
|
||||
i18n,
|
||||
t,
|
||||
translationPath,
|
||||
"description",
|
||||
sectionI18nPrefix,
|
||||
effectiveNamespace,
|
||||
);
|
||||
if (translatedDescription) {
|
||||
finalDescription = translatedDescription;
|
||||
} else if (schemaDescription) {
|
||||
finalDescription = schemaDescription;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { requiresRestartForFieldPath } from "@/utils/configUtil";
|
||||
import { ConfigFormContext } from "@/types/configForm";
|
||||
import {
|
||||
buildTranslationPath,
|
||||
resolveConfigTranslation,
|
||||
getDomainFromNamespace,
|
||||
getFilterObjectLabel,
|
||||
humanizeKey,
|
||||
@@ -263,16 +264,14 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
||||
|
||||
let inferredLabel: string | undefined;
|
||||
if (i18nNs && translationPath) {
|
||||
const prefixedLabelKey =
|
||||
sectionI18nPrefix && !translationPath.startsWith(`${sectionI18nPrefix}.`)
|
||||
? `${sectionI18nPrefix}.${translationPath}.label`
|
||||
: undefined;
|
||||
const labelKey = `${translationPath}.label`;
|
||||
if (prefixedLabelKey && i18n.exists(prefixedLabelKey, { ns: i18nNs })) {
|
||||
inferredLabel = t(prefixedLabelKey, { ns: i18nNs });
|
||||
} else if (i18n.exists(labelKey, { ns: i18nNs })) {
|
||||
inferredLabel = t(labelKey, { ns: i18nNs });
|
||||
}
|
||||
inferredLabel = resolveConfigTranslation(
|
||||
i18n,
|
||||
t,
|
||||
translationPath,
|
||||
"label",
|
||||
sectionI18nPrefix,
|
||||
i18nNs,
|
||||
);
|
||||
}
|
||||
if (!inferredLabel && translatedFilterLabel) {
|
||||
inferredLabel = translatedFilterLabel;
|
||||
@@ -286,19 +285,14 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
||||
|
||||
let inferredDescription: string | undefined;
|
||||
if (i18nNs && translationPath) {
|
||||
const prefixedDescriptionKey =
|
||||
sectionI18nPrefix && !translationPath.startsWith(`${sectionI18nPrefix}.`)
|
||||
? `${sectionI18nPrefix}.${translationPath}.description`
|
||||
: undefined;
|
||||
const descriptionKey = `${translationPath}.description`;
|
||||
if (
|
||||
prefixedDescriptionKey &&
|
||||
i18n.exists(prefixedDescriptionKey, { ns: i18nNs })
|
||||
) {
|
||||
inferredDescription = t(prefixedDescriptionKey, { ns: i18nNs });
|
||||
} else if (i18n.exists(descriptionKey, { ns: i18nNs })) {
|
||||
inferredDescription = t(descriptionKey, { ns: i18nNs });
|
||||
}
|
||||
inferredDescription = resolveConfigTranslation(
|
||||
i18n,
|
||||
t,
|
||||
translationPath,
|
||||
"description",
|
||||
sectionI18nPrefix,
|
||||
i18nNs,
|
||||
);
|
||||
}
|
||||
const schemaDescription = schema?.description;
|
||||
const fallbackDescription =
|
||||
|
||||
Reference in New Issue
Block a user