Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Geist, Geist_Mono } from "next/font/google";
import Script from "next/script";
import { GoogleAnalytics } from "@next/third-parties/google";
import { Analytics } from "@vercel/analytics/next";
import { buildLocaleBootScript } from "@/lib/i18n";
import "./globals.css";

const geistSans = Geist({
Expand Down Expand Up @@ -64,7 +65,7 @@ export const metadata: Metadata = {

/** Apply stored appearance before paint to avoid a light→dark flash. */
const appearanceBootScript = `(function(){try{if(localStorage.getItem("rescript.appearance")==="dark")document.documentElement.classList.add("dark")}catch(e){}})();`;
const localeBootScript = `(function(){try{var p=localStorage.getItem("rescript.ui-locale")||"system";var l=p;if(p==="system"){var a=navigator.languages&&navigator.languages.length?navigator.languages:[navigator.language];l="en";for(var i=0;i<a.length;i++){var v=(a[i]||"").toLowerCase();if(v==="zh"||v.indexOf("zh-")===0){l="zh-CN";break}if(v==="en"||v.indexOf("en-")===0){l="en";break}}}document.documentElement.lang=l==="zh-CN"?"zh-CN":"en"}catch(e){}})();`;
const localeBootScript = buildLocaleBootScript();

export default function RootLayout({
children,
Expand Down
14 changes: 7 additions & 7 deletions components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import ModelSelector, {
import ImportTranscriptOption from "./ImportTranscriptOption";
import { MODEL_ORDER } from "@/lib/models";
import { isTypingTarget } from "@/lib/keyboard";
import { en } from "@/lib/i18n/messages/en";
import { useI18n } from "./I18nProvider";

/** How long the desktop mode-change overlay stays up. Matches the macOS
Expand Down Expand Up @@ -216,9 +217,9 @@ export default function Editor() {
(async () => {
const s = useEditorStore.getState();
try {
s.setProgress({ message: "Loading media engine…", value: null });
s.setProgress({ message: en["progress.loadingMediaEngine"], value: null });
await getFFmpeg();
s.setProgress({ message: "Extracting audio…", value: null });
s.setProgress({ message: en["progress.extractingAudio"], value: null });
const audio = await extractAudio(videoFile);
s.setAudio(audio);
// ffmpeg's gigabyte is pure overhead from here until the user exports,
Expand All @@ -237,14 +238,13 @@ export default function Editor() {
// pulling the media engine is the user's network, not a bug, and
// "Failed to fetch" tells them nothing about what to do next.
if (isNetworkError(err)) {
s.setError(
"Couldn't load the media engine — the connection dropped. " +
"Check your internet and try again."
);
s.setError(en["error.mediaEngineNetwork"]);
return;
}
reportError(err, "media-pipeline");
s.setError(err instanceof Error ? err.message : "Failed to process this file.");
s.setError(
err instanceof Error ? err.message : en["error.processFile"]
);
}
})();
}, [videoFile, skipTranscription, transcribe]);
Expand Down
5 changes: 3 additions & 2 deletions components/ExportDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { useCutRanges } from "@/hooks/useCutRanges";
import { useI18n } from "./I18nProvider";
import { localizeRuntimeMessage } from "@/lib/i18n";
import { en } from "@/lib/i18n/messages/en";

type ExportTab = "video" | "audio" | "transcript" | "subtitles";

Expand Down Expand Up @@ -200,7 +201,7 @@ export default function ExportDialog() {
...(activeTab === "audio" ? {} : { resolution }),
});
} catch (err) {
setError(err instanceof Error ? err.message : "Export failed.");
setError(err instanceof Error ? err.message : en["error.export"]);
} finally {
setStatus("ready");
}
Expand Down Expand Up @@ -232,7 +233,7 @@ export default function ExportDialog() {
setError(null);
trackEvent("export_completed", { kind, format });
} catch (err) {
setError(err instanceof Error ? err.message : "Export failed.");
setError(err instanceof Error ? err.message : en["error.export"]);
}
},
[
Expand Down
3 changes: 2 additions & 1 deletion components/I18nProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
saveUiLocalePreference,
systemLanguages,
translate,
UI_LOCALE_META,
UI_LOCALE_STORAGE_KEY,
type MessageKey,
type Translate,
Expand Down Expand Up @@ -58,7 +59,7 @@ export default function I18nProvider({ children }: { children: ReactNode }) {
}, []);

useEffect(() => {
document.documentElement.lang = locale;
document.documentElement.lang = UI_LOCALE_META[locale].htmlLang;
document.title = translate(locale, "app.title");
window.rescriptDesktop?.setUiLocale(locale);
}, [locale]);
Expand Down
20 changes: 14 additions & 6 deletions components/SettingsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import { useTelemetryPref } from "@/hooks/useTelemetryPref";
import Popover, { PopoverContent, PopoverTrigger } from "./Popover";
import type { Appearance } from "@/lib/theme";
import { useI18n } from "./I18nProvider";
import type { UiLocalePreference } from "@/lib/i18n";
import {
UI_LOCALES,
UI_LOCALE_META,
isUiLocalePreference,
} from "@/lib/i18n";

const MENU_LINKS = [
{ labelKey: "settings.support", href: DISCORD_INVITE_URL, Icon: DiscordIcon },
Expand Down Expand Up @@ -101,14 +105,18 @@ export default function SettingsMenu() {
{t("settings.interfaceLanguage")}
<select
value={preference}
onChange={(event) =>
setPreference(event.target.value as UiLocalePreference)
}
onChange={(event) => {
const next = event.target.value;
if (isUiLocalePreference(next)) setPreference(next);
}}
className="mt-2 block h-8 w-full rounded-lg border border-zinc-200 bg-white px-2 text-[12px] text-zinc-700 outline-none dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200"
>
<option value="system">{t("common.system")}</option>
<option value="en">{t("language.english")}</option>
<option value="zh-CN">{t("language.simplifiedChinese")}</option>
{UI_LOCALES.map((locale) => (
<option key={locale} value={locale}>
{UI_LOCALE_META[locale].nativeLabel}
</option>
))}
</select>
</label>
</section>
Expand Down
7 changes: 6 additions & 1 deletion components/TranscriptPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,12 @@ export default function TranscriptPanel() {
<div className="ml-auto flex items-center gap-2">
{deletedCount > 0 && (
<span className="rounded-md bg-red-50 px-2 py-0.5 text-[9px] font-medium text-red-600 line-clamp-1 line-through dark:bg-red-950/40 dark:text-red-400">
{t("transcript.wordsDeleted", { count: deletedCount })}
{t(
deletedCount === 1
? "transcript.wordDeleted"
: "transcript.wordsDeleted",
{ count: deletedCount }
)}
</span>
)}
{status === "ready" && <TranscriptToolsMenu />}
Expand Down
7 changes: 4 additions & 3 deletions components/UploadScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,10 @@ export default function UploadScreen({
) : ready ? (
<>
<p className="text-[15px] font-medium text-zinc-800 dark:text-zinc-100">
{t("upload.dropOrBrowse", {
browse: t("upload.browse"),
})}
{t("upload.dropPrefix")}{" "}
<span className="text-neutral-600 dark:text-neutral-300">
{t("upload.browse")}
</span>
</p>
<p className="mt-1 text-[13px] text-zinc-400 dark:text-zinc-500">
{source === "import"
Expand Down
108 changes: 0 additions & 108 deletions electron/locale.ts

This file was deleted.

22 changes: 22 additions & 0 deletions electron/locale/catalogs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { UiLocale } from "../../lib/i18n/locales";
import { de } from "./de";
import { en, type DesktopMessageKey } from "./en";
import { es } from "./es";
import { fr } from "./fr";
import { ja } from "./ja";
import { ko } from "./ko";
import { zhCN } from "./zh-CN";
import { zhTW } from "./zh-TW";

export type DesktopMessageCatalog = Record<DesktopMessageKey, string>;

export const desktopCatalogs: Record<UiLocale, DesktopMessageCatalog> = {
en,
"zh-CN": zhCN,
"zh-TW": zhTW,
ja,
ko,
es,
fr,
de,
};
43 changes: 43 additions & 0 deletions electron/locale/de.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { DesktopMessageKey } from "./en";

export const de: Record<DesktopMessageKey, string> = {
file: "Datei",
openProject: "Projekt öffnen…",
reopenLast: "Letztes Projekt erneut öffnen",
recentProjects: "Zuletzt verwendete Projekte",
noRecent: "Keine zuletzt verwendeten Projekte",
clearRecent: "Zuletzt verwendete Projekte löschen",
edit: "Bearbeiten",
undo: "Rückgängig",
redo: "Wiederholen",
cut: "Ausschneiden",
copy: "Kopieren",
paste: "Einfügen",
pasteMatch: "Einfügen und Stil anpassen",
delete: "Löschen",
selectAll: "Alles auswählen",
view: "Ansicht",
reload: "Neu laden",
forceReload: "Neu laden erzwingen",
devTools: "Entwicklertools ein-/ausblenden",
resetZoom: "Originalgröße",
zoomIn: "Vergrößern",
zoomOut: "Verkleinern",
fullscreen: "Vollbild ein-/ausschalten",
window: "Fenster",
minimize: "Minimieren",
zoom: "Zoomen",
front: "Alle nach vorne bringen",
close: "Fenster schließen",
quit: "Rescript beenden",
about: "Über Rescript",
services: "Dienste",
hide: "Rescript ausblenden",
hideOthers: "Andere ausblenden",
unhide: "Alle einblenden",
restart: "Neu starten",
later: "Später",
updateTitle: "Update verfügbar",
updateMessage: "Rescript {version} ist bereit zur Installation.",
updateDetail: "Starten Sie jetzt neu, um das Update anzuwenden.",
};
43 changes: 43 additions & 0 deletions electron/locale/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export const en = {
file: "File",
openProject: "Open Project…",
reopenLast: "Reopen Last Project",
recentProjects: "Recent Projects",
noRecent: "No Recent Projects",
clearRecent: "Clear Recent Projects",
edit: "Edit",
undo: "Undo",
redo: "Redo",
cut: "Cut",
copy: "Copy",
paste: "Paste",
pasteMatch: "Paste and Match Style",
delete: "Delete",
selectAll: "Select All",
view: "View",
reload: "Reload",
forceReload: "Force Reload",
devTools: "Toggle Developer Tools",
resetZoom: "Actual Size",
zoomIn: "Zoom In",
zoomOut: "Zoom Out",
fullscreen: "Toggle Full Screen",
window: "Window",
minimize: "Minimize",
zoom: "Zoom",
front: "Bring All to Front",
close: "Close Window",
quit: "Quit Rescript",
about: "About Rescript",
services: "Services",
hide: "Hide Rescript",
hideOthers: "Hide Others",
unhide: "Show All",
restart: "Restart",
later: "Later",
updateTitle: "Update available",
updateMessage: "Rescript {version} is ready to install.",
updateDetail: "Restart now to apply the update.",
} as const;

export type DesktopMessageKey = keyof typeof en;
Loading
Loading