Skip to content
Draft
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
9 changes: 6 additions & 3 deletions apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import "unfonts.css";
import "./styles/theme.css";

import { CapErrorBoundary } from "./components/CapErrorBoundary";
import { I18nProvider } from "./i18n";
import { generalSettingsStore } from "./store";
import { initAnonymousUser } from "./utils/analytics";
import { type AppTheme, commands } from "./utils/tauri";
Expand Down Expand Up @@ -89,9 +90,11 @@ const queryClient = new QueryClient({
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Suspense>
<Inner />
</Suspense>
<I18nProvider>
<Suspense>
<Inner />
</Suspense>
</I18nProvider>
</QueryClientProvider>
);
}
Expand Down
370 changes: 370 additions & 0 deletions apps/desktop/src/i18n.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createEffect,
createSignal,
} from "solid-js";
import { useI18n } from "~/i18n";
import { trackEvent } from "~/utils/analytics";
import { createCurrentRecordingQuery } from "~/utils/queries";
import {
Expand Down Expand Up @@ -48,6 +49,7 @@ export function CameraSelectBase(props: {
iconClass: string;
permissions?: OSPermissionsCheck;
}) {
const { t } = useI18n();
const currentRecording = createCurrentRecordingQuery();
const requestPermission = useRequestPermission();
const [cameraWindowOpen, setCameraWindowOpen] = createSignal(false);
Expand Down Expand Up @@ -116,7 +118,7 @@ export function CameraSelectBase(props: {

Promise.all([
CheckMenuItem.new({
text: NO_CAMERA,
text: t(NO_CAMERA),
checked: props.value === null,
action: () => onChange(null),
}),
Expand All @@ -138,7 +140,7 @@ export function CameraSelectBase(props: {
>
<IconCapCamera class={props.iconClass} />
<p class="flex-1 text-sm text-left truncate">
{props.value?.display_name ?? NO_CAMERA}
{props.value?.display_name ?? t(NO_CAMERA)}
</p>
<div class="flex items-center gap-1">
{showHiddenIndicator() && (
Expand All @@ -147,7 +149,7 @@ export function CameraSelectBase(props: {
onClick={openCameraWindow}
onPointerDown={(e) => e.stopPropagation()}
class="flex items-center justify-center px-2 py-1 rounded-full bg-gray-6 text-gray-11 hover:bg-gray-7 transition-colors"
title="Show camera preview"
title={t("Show camera preview")}
>
<IconLucideEyeOff class="size-3.5" />
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { getCurrentWindow } from "@tauri-apps/api/window";
import { createEffect, createResource } from "solid-js";
import { createStore } from "solid-js/store";
import Tooltip from "~/components/Tooltip";
import { useI18n } from "~/i18n";
import { commands } from "~/utils/tauri";
import { apiClient } from "~/utils/web-api";
import IconLucideBell from "~icons/lucide/bell";

const ChangelogButton = () => {
const { t } = useI18n();
const [changelogState, setChangelogState] = makePersisted(
createStore({
hasUpdate: false,
Expand Down Expand Up @@ -64,7 +66,7 @@ const ChangelogButton = () => {
});

return (
<Tooltip openDelay={0} content="Changelog">
<Tooltip openDelay={0} content={t("Changelog")}>
<button
type="button"
onClick={handleChangelogClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
createSignal,
Show,
} from "solid-js";
import { useI18n } from "~/i18n";
import { trackEvent } from "~/utils/analytics";
import { createTauriEventListener } from "~/utils/createEventListener";
import { createCurrentRecordingQuery } from "~/utils/queries";
Expand Down Expand Up @@ -48,6 +49,7 @@ export function MicrophoneSelectBase(props: {
>;
permissions?: OSPermissionsCheck;
}) {
const { t } = useI18n();
const DB_SCALE = 40;

const currentRecording = createCurrentRecordingQuery();
Expand Down Expand Up @@ -103,7 +105,7 @@ export function MicrophoneSelectBase(props: {

Promise.all([
CheckMenuItem.new({
text: NO_MICROPHONE,
text: t(NO_MICROPHONE),
checked: props.value === null,
action: () => handleMicrophoneChange(null),
}),
Expand Down Expand Up @@ -135,7 +137,7 @@ export function MicrophoneSelectBase(props: {
</Show>
<IconCapMicrophone class={props.iconClass} />
<p class="flex-1 text-sm text-left truncate">
{props.value ?? NO_MICROPHONE}
{props.value ?? t(NO_MICROPHONE)}
</p>
<TargetSelectInfoPill
PillComponent={props.PillComponent}
Expand Down
24 changes: 14 additions & 10 deletions apps/desktop/src/routes/(window-chrome)/new-main/ModeInfoPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cx } from "cva";
import { For } from "solid-js";
import { Transition } from "solid-transition-group";
import { useI18n } from "~/i18n";
import { commands, type RecordingMode } from "~/utils/tauri";
import IconLucideArrowLeft from "~icons/lucide/arrow-left";
import { useRecordingOptions } from "../OptionsContext";
Expand All @@ -12,28 +13,29 @@ interface ModeInfoPanelProps {
const modeOptions = [
{
mode: "instant" as RecordingMode,
title: "Instant",
description:
titleKey: "Instant",
descriptionKey:
"Share instantly with a link. Your recording uploads as you record, so you can share it immediately when you're done.",
icon: IconCapInstant,
},
{
mode: "studio" as RecordingMode,
title: "Studio",
description:
titleKey: "Studio",
descriptionKey:
"Record locally in the highest quality for editing later. Perfect for creating polished content with effects and transitions.",
icon: IconCapFilmCut,
},
{
mode: "screenshot" as RecordingMode,
title: "Screenshot",
description:
titleKey: "Screenshot",
descriptionKey:
"Capture and annotate screenshots instantly. Great for quick captures, bug reports, and visual communication.",
icon: IconCapScreenshot,
},
];

export default function ModeInfoPanel(props: ModeInfoPanelProps) {
const { t } = useI18n();
const { rawOptions, setOptions } = useRecordingOptions();

const handleModeSelect = (mode: RecordingMode) => {
Expand All @@ -52,9 +54,11 @@ export default function ModeInfoPanel(props: ModeInfoPanelProps) {
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-9 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-1"
>
<IconLucideArrowLeft class="size-3 text-gray-11" />
<span class="font-medium text-gray-12">Back</span>
<span class="font-medium text-gray-12">{t("Back")}</span>
</div>
<span class="text-xs font-medium text-gray-11">Recording Modes</span>
<span class="text-xs font-medium text-gray-11">
{t("Recording Modes")}
</span>
</div>
<div class="flex flex-col flex-1 min-h-0 pt-4">
<div class="px-1 custom-scroll flex-1 overflow-y-auto">
Expand Down Expand Up @@ -105,10 +109,10 @@ export default function ModeInfoPanel(props: ModeInfoPanelProps) {
isSelected() ? "text-blue-11" : "text-gray-12",
)}
>
{option.title}
{t(option.titleKey)}
</h3>
<p class="text-xs leading-relaxed text-gray-11">
{option.description}
{t(option.descriptionKey)}
</p>
</div>
</div>
Expand Down
11 changes: 6 additions & 5 deletions apps/desktop/src/routes/(window-chrome)/new-main/SystemAudio.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createQuery } from "@tanstack/solid-query";
import type { Component, ComponentProps, JSX } from "solid-js";
import { Dynamic } from "solid-js/web";

import { useI18n } from "~/i18n";
import {
createCurrentRecordingQuery,
isSystemAudioSupported,
Expand Down Expand Up @@ -31,6 +31,7 @@ export function SystemAudioToggleRoot(
icon: JSX.Element;
},
) {
const { t } = useI18n();
const { rawOptions, setOptions } = useRecordingOptions();
const currentRecording = createCurrentRecordingQuery();
const systemAudioSupported = createQuery(() => isSystemAudioSupported);
Expand All @@ -39,7 +40,7 @@ export function SystemAudioToggleRoot(
!!currentRecording.data || systemAudioSupported.data === false;
const tooltipMessage = () => {
if (systemAudioSupported.data === false) {
return "System audio capture requires macOS 13.0 or later";
return t("System audio capture requires macOS 13.0 or later");
}
return undefined;
};
Expand All @@ -58,14 +59,14 @@ export function SystemAudioToggleRoot(
{props.icon}
<p class="flex-1 text-sm text-left truncate">
{rawOptions.captureSystemAudio
? "Record System Audio"
: "No System Audio"}
? t("Record System Audio")
: t("No System Audio")}
</p>
<Dynamic
component={props.PillComponent}
variant={rawOptions.captureSystemAudio ? "blue" : "red"}
>
{rawOptions.captureSystemAudio ? "On" : "Off"}
{rawOptions.captureSystemAudio ? t("On") : t("Off")}
</Dynamic>
</button>
);
Expand Down
Loading