Merge pull request #472 from ichi1007/feature/add-i18n-japanese-key

feat(i18n): add Japanese locale and update translations for existing locales
This commit is contained in:
Sid
2026-04-25 16:21:24 -07:00
committed by GitHub
29 changed files with 552 additions and 22 deletions
+29 -17
View File
@@ -154,10 +154,10 @@ export default function VideoEditor() {
const nextSpeedIdRef = useRef(1);
const { shortcuts, isMac } = useShortcuts();
const { locale, setLocale, t: rawT } = useI18n();
const t = useScopedT("editor");
const ts = useScopedT("settings");
const availableLocales = getAvailableLocales();
const { locale, setLocale } = useI18n();
const nextAnnotationIdRef = useRef(1);
const nextAnnotationZIndexRef = useRef(1);
@@ -362,7 +362,10 @@ export default function VideoEditor() {
setLastSavedSnapshot(
createProjectSnapshot(
webcamSourcePath
? { screenVideoPath: sourcePath, webcamVideoPath: webcamSourcePath }
? {
screenVideoPath: sourcePath,
webcamVideoPath: webcamSourcePath,
}
: { screenVideoPath: sourcePath },
INITIAL_EDITOR_STATE,
),
@@ -548,18 +551,18 @@ export default function VideoEditor() {
}
if (!result.success) {
toast.error(result.message || "Failed to load project");
toast.error(result.message || t("project.failedToLoad"));
return;
}
const restored = await applyLoadedProject(result.project, result.path ?? null);
if (!restored) {
toast.error("Invalid project file format");
toast.error(t("project.invalidFormat"));
return;
}
toast.success(`Project loaded from ${result.path}`);
}, [applyLoadedProject]);
toast.success(t("project.loadedFrom", { path: result.path ?? "" }));
}, [applyLoadedProject, t]);
useEffect(() => {
const removeLoadListener = window.electronAPI.onMenuLoadProject(handleLoadProject);
@@ -962,7 +965,11 @@ export default function VideoEditor() {
pushState((prev) => ({
zoomRegions: prev.zoomRegions.map((region) =>
region.id === id
? { ...region, zoomInDurationMs: zoomIn, zoomOutDurationMs: zoomOut }
? {
...region,
zoomInDurationMs: zoomIn,
zoomOutDurationMs: zoomOut,
}
: region,
),
}));
@@ -1296,17 +1303,22 @@ export default function VideoEditor() {
const handleExportSaved = useCallback(
(formatLabel: "GIF" | "Video", filePath: string) => {
setExportedFilePath(filePath);
toast.success(`${formatLabel} exported successfully`, {
description: filePath,
action: {
label: "Show in Folder",
onClick: () => {
void handleShowExportedFile(filePath);
toast.success(
t("export.exportedSuccessfully", {
format: formatLabel,
}),
{
description: filePath,
action: {
label: rawT("common.actions.showInFolder"),
onClick: () => {
void handleShowExportedFile(filePath);
},
},
},
});
);
},
[handleShowExportedFile],
[handleShowExportedFile, t, rawT],
);
const handleSaveUnsavedExport = useCallback(async () => {
@@ -1687,7 +1699,7 @@ export default function VideoEditor() {
if (loading) {
return (
<div className="flex items-center justify-center h-screen bg-background">
<div className="text-foreground">Loading video...</div>
<div className="text-foreground">{t("loadingVideo")}</div>
</div>
);
}
@@ -1701,7 +1713,7 @@ export default function VideoEditor() {
onClick={handleLoadProject}
className="px-3 py-1.5 rounded-md bg-[#34B27B] text-white text-sm hover:bg-[#34B27B]/90"
>
Load Project File
{ts("project.load")}
</button>
</div>
</div>
@@ -2,6 +2,7 @@ import type { Span } from "dnd-timeline";
import { useItem, useTimelineContext } from "dnd-timeline";
import { Gauge, MessageSquare, Scissors, ZoomIn } from "lucide-react";
import { useMemo } from "react";
import { useScopedT } from "@/contexts/I18nContext";
import { cn } from "@/lib/utils";
import {
DEFAULT_ZOOM_IN_MS,
@@ -59,6 +60,7 @@ export default function Item({
children,
onZoomDurationChange,
}: ItemProps) {
const t = useScopedT("timeline");
const { pixelsToValue } = useTimelineContext();
const { setNodeRef, attributes, listeners, itemStyle, itemContentStyle } = useItem({
id,
@@ -251,14 +253,14 @@ export default function Item({
<>
<Scissors className="w-3.5 h-3.5 shrink-0" />
<span className="text-[11px] font-semibold tracking-tight whitespace-nowrap">
Trim
{t("labels.trim")}
</span>
</>
) : isSpeed ? (
<>
<Gauge className="w-3.5 h-3.5 shrink-0" />
<span className="text-[11px] font-semibold tracking-tight whitespace-nowrap">
{speedValue !== undefined ? `${speedValue}×` : "Speed"}
{speedValue !== undefined ? `${speedValue}×` : t("labels.speed")}
</span>
</>
) : (