feat: add search page i18n

This commit is contained in:
ZhaiSoul 2025-03-11 13:58:00 +08:00
parent a52592bcac
commit 118ef6b2fe
4 changed files with 118 additions and 65 deletions

View File

@ -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": "<ul className=\"list-disc pl-5 text-sm text-primary-variant\"><li>Type a filter name followed by a colon (e.g., \"cameras:\").</li><li>Select a value from the suggestions or type your own.</li><li>Use multiple filters by adding them one after another with a space in between.</li><li>Date filters (before: and after:) use <em>{{DateFormat}}</em> format.</li><li>Time range filter uses <em>{{exampleTime}}</em> format.</li><li>Remove filters by clicking the 'x' next to them.</li></ul>",
"desc.example": "Example: <code className=\"text-primary\">cameras:front_door label:person before:01012024 time_range:3:00PM-4:00PM </code>"
}
},
"similaritySearch": {
"title": "Similarity Search",
"active": "Similarity search active",
"clear": "Clear similarity search"
}
}

View File

@ -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": "<ul className=\"list-disc pl-5 text-sm text-primary-variant\"><li>输入筛选器名称后跟一个冒号例如“cameras:”)。</li><li>从建议中选择一个值或输入您自己的值。</li><li>使用多个筛选器时,可以在它们之间用空格分隔。</li><li>日期筛选器before: 和 after:)使用 <em>{{DateFormat}}</em> 格式。</li><li>时间范围筛选器使用 <em>{{exampleTime}}</em> 格式。</li><li>点击筛选器旁边的“x”即可移除筛选条件。</li></ul>",
"desc.example": "示例:<code className=\"text-primary\">cameras:front_door label:person before:01012024 time_range:3:00PM-4:00PM</code>"
}
},
"similaritySearch": {
"title": "相似搜索",
"active": "相似搜索已激活",
"clear": "清除相似搜索"
}
}

View File

@ -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<FrigateConfig>("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({
/>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>Clear search</TooltipContent>
<TooltipContent>{t("button.clear")}</TooltipContent>
</TooltipPortal>
</Tooltip>
)}
@ -679,7 +679,7 @@ export default function InputWithTags({
/>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>Save search</TooltipContent>
<TooltipContent>{t("button.save")}</TooltipContent>
</TooltipPortal>
</Tooltip>
)}
@ -688,12 +688,14 @@ export default function InputWithTags({
<Tooltip>
<TooltipTrigger className="cursor-default">
<MdImageSearch
aria-label="Similarity search active"
aria-label={t("similaritySearch.active")}
className="size-4 text-selected"
/>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>Similarity search active</TooltipContent>
<TooltipContent>
{t("similaritySearch.active")}
</TooltipContent>
</TooltipPortal>
</Tooltip>
)}
@ -702,10 +704,10 @@ export default function InputWithTags({
<PopoverTrigger asChild>
<button
className="focus:outline-none"
aria-label="Filter information"
aria-label={t("button.filterInformation")}
>
<LuFilter
aria-label="Filters active"
aria-label={t("button.filterActive")}
className={cn(
"size-4",
Object.keys(filters).length > 0
@ -717,43 +719,24 @@ export default function InputWithTags({
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="space-y-2">
<h3 className="font-medium">How to use text filters</h3>
<h3 className="font-medium">{t("filter.tips.title")}</h3>
<p className="text-sm text-muted-foreground">
Filters help you narrow down your search results. Here's how
to use them in the input field:
{t("filter.tips.desc")}
</p>
<ul className="list-disc pl-5 text-sm text-primary-variant">
<li>
Type a filter name followed by a colon (e.g., "cameras:").
</li>
<li>
Select a value from the suggestions or type your own.
</li>
<li>
Use multiple filters by adding them one after another with
a space in between.
</li>
<li>
Date filters (before: and after:) use{" "}
<em>{getIntlDateFormat()}</em> format.
</li>
<li>
Time range filter uses{" "}
<em>
{config?.ui.time_format == "24hour"
<Trans
ns="views/search"
values={{
DateFormat: getIntlDateFormat(),
exampleTime:
config?.ui.time_format == "24hour"
? "15:00-16:00"
: "3:00PM-4:00PM"}{" "}
</em>
format.
</li>
<li>Remove filters by clicking the 'x' next to them.</li>
</ul>
: "3:00PM-4:00PM",
}}
>
filter.tips.desc.step
</Trans>
<p className="text-sm text-muted-foreground">
Example:{" "}
<code className="text-primary">
cameras:front_door label:person before:01012024
time_range:3:00PM-4:00PM
</code>
<Trans ns="views/search">filter.tips.desc.example</Trans>
</p>
</div>
</PopoverContent>
@ -780,13 +763,13 @@ export default function InputWithTags({
)}
>
{!currentFilterType && inputValue && (
<CommandGroup heading="Search">
<CommandGroup heading={t("search")}>
<CommandItem
className="cursor-pointer"
onSelect={() => handleSearch(inputValue)}
>
<LuSearch className="mr-2 h-4 w-4" />
Search for "{inputValue}"
{t("searchFor", { inputValue })}
</CommandItem>
</CommandGroup>
)}
@ -796,11 +779,11 @@ export default function InputWithTags({
<div className="my-2 flex flex-wrap gap-2 px-2">
{isSimilaritySearch && (
<span className="inline-flex items-center whitespace-nowrap rounded-full bg-blue-100 px-2 py-0.5 text-sm text-blue-800">
Similarity Search
{t("similaritySearch.title")}
<button
onClick={handleClearSimilarity}
className="ml-1 focus:outline-none"
aria-label="Clear similarity search"
aria-label={t("similaritySearch.clear")}
>
<LuX className="h-3 w-3" />
</button>
@ -863,7 +846,7 @@ export default function InputWithTags({
!inputValue &&
searchHistoryLoaded &&
(searchHistory?.length ?? 0) > 0 && (
<CommandGroup heading="Saved Searches">
<CommandGroup heading={t("savedSearches")}>
{searchHistory?.map((suggestion, index) => (
<CommandItem
key={index}
@ -884,7 +867,7 @@ export default function InputWithTags({
</button>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent>Delete saved search</TooltipContent>
<TooltipContent>{t("button.delete")}</TooltipContent>
</TooltipPortal>
</Tooltip>
</CommandItem>

View File

@ -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" }),
})}
</div>
<div className="flex items-center gap-1">