Implement smart capitalization based on locale (#17860)

This commit is contained in:
Nicolas Mowen
2025-04-22 16:21:09 -06:00
committed by GitHub
parent b6e0e5698a
commit f9b2db4405
44 changed files with 95 additions and 71 deletions
+1 -1
View File
@@ -222,7 +222,7 @@ export default function ExportCard({
<Skeleton className="absolute inset-0 aspect-video rounded-lg md:rounded-2xl" />
)}
<div className="rounded-b-l pointer-events-none absolute inset-x-0 bottom-0 h-[20%] rounded-lg bg-gradient-to-t from-black/60 to-transparent md:rounded-2xl">
<div className="mx-3 flex h-full items-end justify-between pb-1 text-sm capitalize text-white">
<div className="mx-3 flex h-full items-end justify-between pb-1 text-sm text-white smart-capitalize">
{exportedRecording.name.replaceAll("_", " ")}
</div>
</div>
+1 -1
View File
@@ -175,7 +175,7 @@ export default function ReviewCard({
<div className="font-extra-light text-xs">{formattedDate}</div>
</div>
</TooltipTrigger>
<TooltipContent className="capitalize">
<TooltipContent className="smart-capitalize">
{[
...new Set([
...(event.data.objects || []),
+1 -1
View File
@@ -146,7 +146,7 @@ export default function SearchThumbnail({
</TooltipTrigger>
</div>
<TooltipPortal>
<TooltipContent className="capitalize">
<TooltipContent className="smart-capitalize">
{[searchResult.sub_label ?? objectLabel]
.filter(
(item) => item !== undefined && !item.includes("-verified"),
@@ -196,7 +196,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
</Button>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent className="capitalize" side="right">
<TooltipContent className="smart-capitalize" side="right">
{name}
</TooltipContent>
</TooltipPortal>
@@ -847,7 +847,7 @@ export function CameraGroupEdit({
<FormControl key={camera}>
<div className="flex items-center justify-between gap-1">
<Label
className="mx-2 w-full cursor-pointer capitalize text-primary"
className="mx-2 w-full cursor-pointer text-primary smart-capitalize"
htmlFor={camera.replaceAll("_", " ")}
>
{camera.replaceAll("_", " ")}
@@ -62,7 +62,7 @@ export function CamerasFilterButton({
const trigger = (
<Button
className="flex items-center gap-2 capitalize"
className="flex items-center gap-2 smart-capitalize"
aria-label={t("cameras.label")}
variant={selectedCameras?.length == undefined ? "default" : "select"}
size="sm"
@@ -172,7 +172,7 @@ export function CamerasFilterContent({
return (
<div
key={name}
className="w-full cursor-pointer rounded-lg px-2 py-0.5 text-sm capitalize text-primary hover:bg-muted"
className="w-full cursor-pointer rounded-lg px-2 py-0.5 text-sm text-primary smart-capitalize hover:bg-muted"
onClick={() => {
setCurrentCameras([...conf.cameras]);
}}
+1 -1
View File
@@ -16,7 +16,7 @@ export default function FilterSwitch({
return (
<div className="flex items-center justify-between gap-1">
<Label
className={`mx-2 w-full cursor-pointer capitalize text-primary ${disabled ? "text-secondary-foreground" : ""}`}
className={`mx-2 w-full cursor-pointer text-primary smart-capitalize ${disabled ? "text-secondary-foreground" : ""}`}
htmlFor={label}
>
{label}
@@ -126,7 +126,7 @@ export function GeneralFilterContent({
{["debug", "info", "warning", "error"].map((item) => (
<div className="flex items-center justify-between" key={item}>
<Label
className="mx-2 w-full cursor-pointer capitalize text-primary"
className="mx-2 w-full cursor-pointer text-primary smart-capitalize"
htmlFor={item}
>
{item.replaceAll("_", " ")}
@@ -354,7 +354,7 @@ function GeneralFilterButton({
variant={
selectedLabels?.length || selectedZones?.length ? "select" : "default"
}
className="flex items-center gap-2 capitalize"
className="flex items-center gap-2 smart-capitalize"
aria-label={t("filter")}
>
<FaFilter
@@ -285,7 +285,7 @@ function GeneralFilterButton({
<Button
size="sm"
variant={selectedLabels?.length ? "select" : "default"}
className="flex items-center gap-2 capitalize"
className="flex items-center gap-2 smart-capitalize"
aria-label={t("labels.label")}
>
<MdLabel
@@ -457,7 +457,7 @@ function SortTypeButton({
? "select"
: "default"
}
className="flex items-center gap-2 capitalize"
className="flex items-center gap-2 smart-capitalize"
aria-label={t("labels.label")}
>
<MdSort
+2 -2
View File
@@ -22,7 +22,7 @@ export function ZoneMaskFilterButton({
<Button
size="sm"
variant={selectedZoneMask?.length ? "select" : "default"}
className="flex items-center gap-2 capitalize"
className="flex items-center gap-2 smart-capitalize"
aria-label={t("zoneMask.filterBy")}
>
<FaFilter
@@ -96,7 +96,7 @@ export function GeneralFilterContent({
{["zone", "motion_mask", "object_mask"].map((item) => (
<div key={item} className="flex items-center justify-between">
<Label
className="mx-2 w-full cursor-pointer capitalize text-primary"
className="mx-2 w-full cursor-pointer text-primary smart-capitalize"
htmlFor={item}
>
{t(
@@ -191,7 +191,7 @@ export function CombinedStorageGraph({
<TableBody>
{series.map((item) => (
<TableRow key={item.name}>
<TableCell className="flex flex-row items-center gap-2 font-medium capitalize">
<TableCell className="flex flex-row items-center gap-2 font-medium smart-capitalize">
{" "}
<div
className="size-3 rounded-md"
+1 -1
View File
@@ -72,7 +72,7 @@ export function LogChip({ severity, onClickSeverity }: LogChipProps) {
return (
<div className="min-w-16 lg:min-w-20">
<span
className={`rounded-md px-1 py-[1px] text-xs capitalize ${onClickSeverity ? "cursor-pointer" : ""} ${severityClassName}`}
className={`rounded-md px-1 py-[1px] text-xs smart-capitalize ${onClickSeverity ? "cursor-pointer" : ""} ${severityClassName}`}
onClick={(e) => {
e.stopPropagation();
+2 -2
View File
@@ -816,7 +816,7 @@ export default function InputWithTags({
.map((value, index) => (
<span
key={`${filterType}-${index}`}
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800"
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm text-green-800 smart-capitalize"
>
{t("filter.label." + filterType)}:{" "}
{filterType === "labels"
@@ -838,7 +838,7 @@ export default function InputWithTags({
: !(filterType == "event_id" && isSimilaritySearch) && (
<span
key={filterType}
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800"
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm text-green-800 smart-capitalize"
>
{filterType === "event_id"
? t("trackedObjectId")
+1 -1
View File
@@ -265,7 +265,7 @@ export default function LiveContextMenu({
<ContextMenuTrigger>{children}</ContextMenuTrigger>
<ContextMenuContent>
<div className="flex flex-col items-start gap-1 py-1 pl-2">
<div className="text-md capitalize text-primary-variant">
<div className="text-md text-primary-variant smart-capitalize">
{camera.replaceAll("_", " ")}
</div>
{preferredLiveMode == "jsmpeg" && isRestreamed && (
@@ -83,7 +83,7 @@ export default function CameraInfoDialog({
>
<DialogContent>
<DialogHeader>
<DialogTitle className="capitalize">
<DialogTitle className="smart-capitalize">
{t("cameras.info.cameraProbeInfo", {
camera: camera.name.replaceAll("_", " "),
})}
+1 -1
View File
@@ -288,7 +288,7 @@ export function ExportContent({
id={opt}
value={opt}
/>
<Label className="cursor-pointer capitalize" htmlFor={opt}>
<Label className="cursor-pointer smart-capitalize" htmlFor={opt}>
{isNaN(parseInt(opt))
? opt == "timeline"
? t("export.time.fromTimeline")
@@ -91,7 +91,7 @@ export default function FaceSelectionDialog({
)}
>
<SelectorItem
className="flex cursor-pointer gap-2 capitalize"
className="flex cursor-pointer gap-2 smart-capitalize"
onClick={() => setNewFace(true)}
>
<LuPlus />
@@ -100,7 +100,7 @@ export default function FaceSelectionDialog({
{faceNames.map((faceName) => (
<SelectorItem
key={faceName}
className="flex cursor-pointer gap-2 capitalize"
className="flex cursor-pointer gap-2 smart-capitalize"
onClick={() => onTrainAttempt(faceName)}
>
<LuScanFace />
@@ -26,7 +26,7 @@ export default function MobileCameraDrawer({
<Drawer open={cameraDrawer} onOpenChange={setCameraDrawer}>
<DrawerTrigger asChild>
<Button
className="rounded-lg capitalize"
className="rounded-lg smart-capitalize"
aria-label={t("menu.live.cameras.title")}
size="sm"
>
@@ -38,7 +38,7 @@ export default function MobileCameraDrawer({
{allCameras.map((cam) => (
<div
key={cam}
className={`mx-4 w-full py-2 text-center capitalize ${cam == selected ? "rounded-lg bg-secondary" : ""}`}
className={`mx-4 w-full py-2 text-center smart-capitalize ${cam == selected ? "rounded-lg bg-secondary" : ""}`}
onClick={() => {
onSelectCamera(cam);
setCameraDrawer(false);
@@ -324,7 +324,7 @@ export default function MobileReviewSettingsDrawer({
>
<DrawerTrigger asChild>
<Button
className="rounded-lg capitalize"
className="rounded-lg smart-capitalize"
aria-label={t("filters")}
variant={
filter?.labels || filter?.after || filter?.zones
@@ -23,7 +23,7 @@ export default function MobileTimelineDrawer({
<Drawer open={drawer} onOpenChange={setDrawer}>
<DrawerTrigger asChild>
<Button
className="rounded-lg capitalize"
className="rounded-lg smart-capitalize"
aria-label="Select timeline or events list"
size="sm"
>
@@ -32,7 +32,7 @@ export default function MobileTimelineDrawer({
</DrawerTrigger>
<DrawerContent className="mx-1 flex max-h-[75dvh] flex-col items-center gap-2 overflow-hidden rounded-t-2xl px-4 pb-4">
<div
className={`mx-4 w-full py-2 text-center capitalize ${selected == "timeline" ? "rounded-lg bg-secondary" : ""}`}
className={`mx-4 w-full py-2 text-center smart-capitalize ${selected == "timeline" ? "rounded-lg bg-secondary" : ""}`}
onClick={() => {
onSelect("timeline");
setDrawer(false);
@@ -41,7 +41,7 @@ export default function MobileTimelineDrawer({
Timeline
</div>
<div
className={`mx-4 w-full py-2 text-center capitalize ${selected == "events" ? "rounded-lg bg-secondary" : ""}`}
className={`mx-4 w-full py-2 text-center smart-capitalize ${selected == "events" ? "rounded-lg bg-secondary" : ""}`}
onClick={() => {
onSelect("events");
setDrawer(false);
@@ -572,7 +572,7 @@ export default function ObjectLifecycle({
</div>
</div>
<div className="mx-3 text-lg">
<div className="flex flex-row items-center capitalize text-primary">
<div className="flex flex-row items-center text-primary smart-capitalize">
{getLifecycleItemDescription(item)}
</div>
<div className="text-sm text-primary-variant">
@@ -616,7 +616,7 @@ export default function ObjectLifecycle({
)}
<div
key={index}
className="cursor-pointer capitalize"
className="cursor-pointer smart-capitalize"
onClick={() => setSelectedZone(zone)}
>
{zone.replaceAll("_", " ")}
@@ -722,7 +722,7 @@ export default function ObjectLifecycle({
/>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent className="capitalize">
<TooltipContent className="smart-capitalize">
{getLifecycleItemDescription(item)}
</TooltipContent>
</TooltipPortal>
@@ -100,7 +100,7 @@ export function ObjectPath({
/>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent side="top" className="capitalize">
<TooltipContent side="top" className="smart-capitalize">
{pos.lifecycle_item
? getLifecycleItemDescription(pos.lifecycle_item)
: "Tracked point"}
@@ -223,7 +223,9 @@ export default function ObjectPathPlotter() {
}}
/>
<span className="text-sm">
<strong className="mr-1 capitalize">{event.label}</strong>
<strong className="mr-1 smart-capitalize">
{event.label}
</strong>
{formatUnixTimestampToDateTime(event.start_time, {
timezone: config?.ui.timezone,
})}
@@ -233,7 +233,7 @@ export default function ReviewDetailDialog({
<div className="text-sm text-primary/40">
{t("details.camera")}
</div>
<div className="text-sm capitalize">
<div className="text-sm smart-capitalize">
{review.camera.replaceAll("_", " ")}
</div>
</div>
@@ -249,12 +249,12 @@ export default function ReviewDetailDialog({
<div className="text-sm text-primary/40">
{t("details.objects")}
</div>
<div className="scrollbar-container flex max-h-32 flex-col items-start gap-2 overflow-y-auto text-sm capitalize">
<div className="scrollbar-container flex max-h-32 flex-col items-start gap-2 overflow-y-auto text-sm smart-capitalize">
{events?.map((event) => {
return (
<div
key={event.id}
className="flex flex-row items-center gap-2 capitalize"
className="flex flex-row items-center gap-2 smart-capitalize"
>
{getIconForLabel(
event.label,
@@ -290,12 +290,12 @@ export default function ReviewDetailDialog({
<div className="text-sm text-primary/40">
{t("details.zones")}
</div>
<div className="flex flex-col items-start gap-2 text-sm capitalize">
<div className="flex flex-col items-start gap-2 text-sm smart-capitalize">
{review.data.zones.map((zone) => {
return (
<div
key={zone}
className="flex flex-row items-center gap-2 capitalize"
className="flex flex-row items-center gap-2 smart-capitalize"
>
{zone.replaceAll("_", " ")}
</div>
@@ -231,7 +231,7 @@ export default function SearchDetailDialog({
{item == "object_lifecycle" && (
<FaRotate className="size-4" />
)}
<div className="capitalize">{t(`type.${item}`)}</div>
<div className="smart-capitalize">{t(`type.${item}`)}</div>
</ToggleGroupItem>
))}
</ToggleGroup>
@@ -711,7 +711,7 @@ function ObjectDetailsTab({
<div className="flex w-full flex-col gap-3">
<div className="flex flex-col gap-1.5">
<div className="text-sm text-primary/40">{t("details.label")}</div>
<div className="flex flex-row items-center gap-2 text-sm capitalize">
<div className="flex flex-row items-center gap-2 text-sm smart-capitalize">
{getIconForLabel(search.label, "size-4 text-primary")}
{t(search.label, {
ns: "objects",
@@ -831,7 +831,7 @@ function ObjectDetailsTab({
)}
<div className="flex flex-col gap-1.5">
<div className="text-sm text-primary/40">{t("details.camera")}</div>
<div className="text-sm capitalize">
<div className="text-sm smart-capitalize">
{search.camera.replaceAll("_", " ")}
</div>
</div>
+1 -1
View File
@@ -361,7 +361,7 @@ export default function LivePlayer({
</TooltipTrigger>
</div>
<TooltipPortal>
<TooltipContent className="capitalize">
<TooltipContent className="smart-capitalize">
{[
...new Set([
...(objects || []).map(({ label, sub_label }) =>
@@ -262,7 +262,7 @@ export default function PreviewThumbnailPlayer({
</div>
</TooltipTrigger>
</div>
<TooltipContent className="capitalize">
<TooltipContent className="smart-capitalize">
{[
...new Set([
...(review.data.objects || []),
@@ -185,7 +185,7 @@ export function CameraStreamingDialog({
return (
<DialogContent className="sm:max-w-[425px]">
<DialogHeader className="mb-4">
<DialogTitle className="capitalize">
<DialogTitle className="smart-capitalize">
{t("group.camera.setting.title", {
cameraName: camera.replaceAll("_", " "),
})}
+1 -1
View File
@@ -933,7 +933,7 @@ export function ZoneObjectSelector({
{allLabels.map((item) => (
<div key={item} className="flex items-center justify-between">
<Label
className="w-full cursor-pointer capitalize text-primary"
className="w-full cursor-pointer text-primary smart-capitalize"
htmlFor={item}
>
{t(item, { ns: "objects" })}