From 118ef6b2fe500825dc7c0be7de0a64bfac63095b Mon Sep 17 00:00:00 2001 From: ZhaiSoul <842607283@qq.com> Date: Tue, 11 Mar 2025 13:58:00 +0800 Subject: [PATCH] feat: add search page i18n --- web/public/locales/en/views/search.json | 35 ++++++ web/public/locales/zh-CN/views/search.json | 35 ++++++ web/src/components/input/InputWithTags.tsx | 109 ++++++++---------- .../overlay/dialog/SearchFilterDialog.tsx | 4 +- 4 files changed, 118 insertions(+), 65 deletions(-) create mode 100644 web/public/locales/en/views/search.json create mode 100644 web/public/locales/zh-CN/views/search.json diff --git a/web/public/locales/en/views/search.json b/web/public/locales/en/views/search.json new file mode 100644 index 000000000..fbb43253e --- /dev/null +++ b/web/public/locales/en/views/search.json @@ -0,0 +1,35 @@ +{ + "search": "Search", + "savedSearches": "Saved Searches", + "searchFor": "Search for {{inputValue}}", + "button": { + "clear": "Clear search", + "save": "Save search", + "delete": "Delete saved search", + "filterInformation": "Filter information", + "filterActive": "Filters active" + }, + "filter": { + "toast": { + "error": { + "beforeDateBeLaterAfter": "The 'before' date must be later than the 'after' date.", + "afterDatebeEarlierBefore": "The 'after' date must be earlier than the 'before' date.", + "minScoreMustBeLessOrEqualMaxScore": "The 'min_score' must be less than or equal to the 'max_score'.", + "maxScoreMustBeGreaterOrEqualMinScore": "The 'max_score' must be greater than or equal to the 'min_score'.", + "minSpeedMustBeLessOrEqualMaxSpeed": "The 'min_speed' must be less than or equal to the 'max_speed'.", + "maxSpeedMustBeGreaterOrEqualMinSpeed": "The 'max_speed' must be greater than or equal to the 'min_speed'." + } + }, + "tips": { + "title": "How to use text filters", + "desc": "Filters help you narrow down your search results. Here's how to use them in the input field:", + "desc.step": "", + "desc.example": "Example: cameras:front_door label:person before:01012024 time_range:3:00PM-4:00PM " + } + }, + "similaritySearch": { + "title": "Similarity Search", + "active": "Similarity search active", + "clear": "Clear similarity search" + } +} \ No newline at end of file diff --git a/web/public/locales/zh-CN/views/search.json b/web/public/locales/zh-CN/views/search.json new file mode 100644 index 000000000..11e806844 --- /dev/null +++ b/web/public/locales/zh-CN/views/search.json @@ -0,0 +1,35 @@ +{ + "search": "搜索", + "savedSearches": "已保存的搜索", + "searchFor": "搜索 {{inputValue}}", + "button": { + "clear": "清除搜索", + "save": "保存搜索", + "delete": "删除已保存的搜索", + "filterInformation": "筛选信息", + "filterActive": "筛选器已激活" + }, + "filter": { + "toast": { + "error": { + "beforeDateBeLaterAfter": "“之前”日期必须晚于“之后”日期。", + "afterDatebeEarlierBefore": "“之后”日期必须早于“之前”日期。", + "minScoreMustBeLessOrEqualMaxScore": "最小分值 必须小于或等于 最大分值。", + "maxScoreMustBeGreaterOrEqualMinScore": "最大分值 必须大于或等于 最小分值", + "minSpeedMustBeLessOrEqualMaxSpeed": "最低速度 必须小于或等于 最高速度", + "maxSpeedMustBeGreaterOrEqualMinSpeed": "最高速度 必须大于或等于 最低速度" + } + }, + "tips": { + "title": "如何使用文本筛选器(英文)", + "desc": "筛选器可帮助您缩小搜索范围。注意,目前还暂不支持中文搜索。以下是在输入字段中使用筛选器的方法:", + "desc.step": "", + "desc.example": "示例:cameras:front_door label:person before:01012024 time_range:3:00PM-4:00PM" + } + }, + "similaritySearch": { + "title": "相似搜索", + "active": "相似搜索已激活", + "clear": "清除相似搜索" + } +} diff --git a/web/src/components/input/InputWithTags.tsx b/web/src/components/input/InputWithTags.tsx index 3ae78e70a..56fc6feb3 100644 --- a/web/src/components/input/InputWithTags.tsx +++ b/web/src/components/input/InputWithTags.tsx @@ -51,6 +51,7 @@ import { toast } from "sonner"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { MdImageSearch } from "react-icons/md"; +import { Trans, useTranslation } from "react-i18next"; type InputWithTagsProps = { inputFocused: boolean; @@ -73,6 +74,7 @@ export default function InputWithTags({ setSearch, allSuggestions, }: InputWithTagsProps) { + const { t } = useTranslation(["views/search"]); const { data: config } = useSWR("config", { revalidateOnFocus: false, }); @@ -236,12 +238,9 @@ export default function InputWithTags({ filters.after && timestamp <= filters.after * 1000 ) { - toast.error( - "The 'before' date must be later than the 'after' date.", - { - position: "top-center", - }, - ); + toast.error(t("filter.toast.error.beforeDateBeLaterAfter"), { + position: "top-center", + }); return; } if ( @@ -249,12 +248,9 @@ export default function InputWithTags({ filters.before && timestamp >= filters.before * 1000 ) { - toast.error( - "The 'after' date must be earlier than the 'before' date.", - { - position: "top-center", - }, - ); + toast.error(t("afterDatebeEarlierBefore"), { + position: "top-center", + }); return; } if (type === "before") { @@ -274,7 +270,7 @@ export default function InputWithTags({ score > filters.max_score * 100 ) { toast.error( - "The 'min_score' must be less than or equal to the 'max_score'.", + t("filter.toast.error.minScoreMustBeLessOrEqualMaxScore"), { position: "top-center", }, @@ -287,7 +283,7 @@ export default function InputWithTags({ score < filters.min_score * 100 ) { toast.error( - "The 'max_score' must be greater than or equal to the 'min_score'.", + t("filter.toast.error.maxScoreMustBeGreaterOrEqualMinScore"), { position: "top-center", }, @@ -308,7 +304,7 @@ export default function InputWithTags({ speed > filters.max_speed ) { toast.error( - "The 'min_speed' must be less than or equal to the 'max_speed'.", + t("filter.toast.error.minSpeedMustBeLessOrEqualMaxSpeed"), { position: "top-center", }, @@ -321,7 +317,7 @@ export default function InputWithTags({ speed < filters.min_speed ) { toast.error( - "The 'max_speed' must be greater than or equal to the 'min_speed'.", + t("filter.toast.error.maxSpeedMustBeGreaterOrEqualMinSpeed"), { position: "top-center", }, @@ -380,7 +376,7 @@ export default function InputWithTags({ setCurrentFilterType(null); } }, - [filters, setFilters, allSuggestions], + [filters, setFilters, allSuggestions, t], ); function formatFilterValues( @@ -408,7 +404,11 @@ export default function InputWithTags({ return Math.round(Number(filterValues) * 100).toString() + "%"; } else if (filterType === "min_speed" || filterType === "max_speed") { return ( - filterValues + (config?.ui.unit_system == "metric" ? " kph" : " mph") + filterValues + + " " + + (config?.ui.unit_system == "metric" + ? t("unit.speed.kph", { ns: "common" }) + : t("unit.speed.mph", { ns: "common" })) ); } else if ( filterType === "has_clip" || @@ -665,7 +665,7 @@ export default function InputWithTags({ /> - Clear search + {t("button.clear")} )} @@ -679,7 +679,7 @@ export default function InputWithTags({ /> - Save search + {t("button.save")} )} @@ -688,12 +688,14 @@ export default function InputWithTags({ - Similarity search active + + {t("similaritySearch.active")} + )} @@ -702,10 +704,10 @@ export default function InputWithTags({ @@ -863,7 +846,7 @@ export default function InputWithTags({ !inputValue && searchHistoryLoaded && (searchHistory?.length ?? 0) > 0 && ( - + {searchHistory?.map((suggestion, index) => ( - Delete saved search + {t("button.delete")} diff --git a/web/src/components/overlay/dialog/SearchFilterDialog.tsx b/web/src/components/overlay/dialog/SearchFilterDialog.tsx index 2e2ca4475..f84501bca 100644 --- a/web/src/components/overlay/dialog/SearchFilterDialog.tsx +++ b/web/src/components/overlay/dialog/SearchFilterDialog.tsx @@ -558,8 +558,8 @@ export function SpeedFilterContent({ {t("estimatedSpeed", { unit: config?.ui.unit_system == "metric" - ? t("unit.speed.kph") - : t("unit.speed.mph"), + ? t("unit.speed.kph", { ns: "common" }) + : t("unit.speed.mph", { ns: "common" }), })}