Internationalization (i18n) (#627)

* `feat: added internationalization lib, added english labels`
* `fixes from code review`
* `feat: add crowdin github actions`
* `fix: added support for i18n in testing. Fixed broken tests`
* `fix: missing translations`
* `removed unneded import`
* `more components updated with missing translations`
* `fixed lint issue`
* `Refactor: updated how translations are scoped, updated all references`
* `fixing broken tests`
* `feat: added language switcher, updated some translations`
* `fixed linting issues`
* `reverting vite config`
* `add english group id's to command palette`
* `updated PR template for i18n`
This commit is contained in:
Dan Ditomaso
2025-05-27 08:33:43 -04:00
committed by GitHub
parent 9d74fe2d6e
commit eb2a2717b1
95 changed files with 4651 additions and 1497 deletions
+48 -46
View File
@@ -5,16 +5,8 @@ import {
BatteryMediumIcon,
PlugZapIcon,
} from "lucide-react";
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
interface DeviceMetrics {
batteryLevel?: number | null;
voltage?: number | null;
}
interface BatteryStatusProps {
deviceMetrics?: DeviceMetrics | null;
}
import { useTranslation } from "react-i18next";
import { DeviceMetrics } from "./types.ts";
interface BatteryStateConfig {
condition: (level: number) => boolean;
@@ -23,34 +15,45 @@ interface BatteryStateConfig {
text: (level: number) => string;
}
const batteryStates: BatteryStateConfig[] = [
{
condition: (level) => level > 100,
Icon: PlugZapIcon,
className: "text-gray-500",
text: () => "Plugged in",
},
{
condition: (level) => level > 80,
Icon: BatteryFullIcon,
className: "text-green-500",
text: (level) => `${level}% charging`,
},
{
condition: (level) => level > 20,
Icon: BatteryMediumIcon,
className: "text-yellow-500",
text: (level) => `${level}% charging`,
},
{
condition: () => true,
Icon: BatteryLowIcon,
className: "text-red-500",
text: (level) => `${level}% charging`,
},
];
interface BatteryStatusProps {
deviceMetrics?: DeviceMetrics | null;
}
const getBatteryState = (level: number) => {
const getBatteryStates = (
t: (key: string, options?: object) => string,
): BatteryStateConfig[] => {
return [
{
condition: (level) => level > 100,
Icon: PlugZapIcon,
className: "text-gray-500",
text: () => t("batteryStatus.pluggedIn"),
},
{
condition: (level) => level > 80,
Icon: BatteryFullIcon,
className: "text-green-500",
text: (level) => t("batteryStatus.charging", { level }),
},
{
condition: (level) => level > 20,
Icon: BatteryMediumIcon,
className: "text-yellow-500",
text: (level) => t("batteryStatus.charging", { level }),
},
{
condition: () => true,
Icon: BatteryLowIcon,
className: "text-red-500",
text: (level) => t("batteryStatus.charging", { level }),
},
];
};
const getBatteryState = (
level: number,
batteryStates: BatteryStateConfig[],
) => {
return batteryStates.find((state) => state.condition(level));
};
@@ -62,25 +65,24 @@ const BatteryStatus: React.FC<BatteryStatusProps> = ({ deviceMetrics }) => {
return null;
}
const { batteryLevel, voltage } = deviceMetrics;
const currentState = getBatteryState(batteryLevel) ??
const { t } = useTranslation();
const batteryStates = getBatteryStates(t);
const { batteryLevel } = deviceMetrics;
const currentState = getBatteryState(batteryLevel, batteryStates) ??
batteryStates[batteryStates.length - 1];
const BatteryIcon = currentState.Icon;
const iconClassName = currentState.className;
const statusText = currentState.text(batteryLevel);
const voltageTitle = `${voltage?.toPrecision(3) ?? "Unknown"} volts`;
return (
<div
className="flex items-center gap-1 mt-0.5 text-gray-500"
title={voltageTitle}
className="flex items-center gap-1 mt-0.5 "
aria-label={t("batteryStatus.title")}
>
<BatteryIcon size={22} className={iconClassName} />
<Subtle aria-label="Battery">
{statusText}
</Subtle>
{statusText}
</div>
);
};