diff --git a/src/workspaces/officerdev/src/apps/Preview/PreviewApp.tsx b/src/workspaces/officerdev/src/apps/Preview/PreviewApp.tsx
index 81167101..b08f3a9d 100644
--- a/src/workspaces/officerdev/src/apps/Preview/PreviewApp.tsx
+++ b/src/workspaces/officerdev/src/apps/Preview/PreviewApp.tsx
@@ -3,6 +3,7 @@ import { Globe } from 'lucide-react';
import { useClient } from 'hooks/useClient';
import { useWorkspace } from '../../components/Workspace';
import { normalizeUrl, usePreview } from './use-preview';
+import { useTranslation } from '../../locales';
/**
* A live app, in a panel.
@@ -19,6 +20,7 @@ import { normalizeUrl, usePreview } from './use-preview';
* machine; pretending otherwise with an attribute that does not hold is worse than not claiming it.
*/
export const PreviewApp = ({ panelId }: { panelId: string }) => {
+ const { t } = useTranslation();
const { workspace } = useWorkspace();
const { url, target, setUrl, reportLocation, reloadToken } = usePreview(panelId, workspace);
const [draft, setDraft] = useState('');
@@ -34,7 +36,7 @@ export const PreviewApp = ({ panelId }: { panelId: string }) => {
}}
>
- Point this panel at a running app
+ {t('preview.pointAtApp')}
{
disabled={!normalizeUrl(draft)}
className="rounded-md bg-duck-teal px-3 py-1.5 text-sm text-duck-yellow transition-opacity hover:opacity-90 disabled:opacity-40"
>
- Open
+ {t('preview.open')}
-
- Anything the browser can reach. It stays on this panel and survives a reload.
-
+
{t('preview.hint')}
);
}
@@ -86,6 +86,7 @@ type DevProxyFrameProps = {
};
export const DevProxyFrame = ({ entry, target, reloadToken, onLocation }: DevProxyFrameProps) => {
+ const { t } = useTranslation();
const { get } = useClient();
const needsSession = entry.startsWith('/api/dev-proxy/');
const [ready, setReady] = useState(!needsSession);
@@ -95,7 +96,7 @@ export const DevProxyFrame = ({ entry, target, reloadToken, onLocation }: DevPro
* The session cookie, and why this effect must NOT watch the URL.
*
* It used to depend on the full composed URL, which changes every time the app navigates. Each change
- * re-ran this, and `setReady(false)` tears the frame down to show "Connecting…" — so a route change was
+ * re-ran this, and `setReady(false)` tears the frame down to show t('preview.connecting') — so a route change was
* followed, one debounce later, by the whole app reloading. Reported as "I change routes and a second
* later the page refreshes"; the second was the 800ms debounce on writing the location down.
*
@@ -128,7 +129,9 @@ export const DevProxyFrame = ({ entry, target, reloadToken, onLocation }: DevPro
);
}
if (!ready) {
- return
Connecting…
;
+ return (
+
{t('preview.connecting')}
+ );
}
// Keyed on the ENTRY url as well as the reload counter. Freezing `src` at mount stopped the app's own
// navigation from reloading the frame — and stopped a newly typed URL from loading at all, since nothing
@@ -159,6 +162,7 @@ export const DevProxyFrame = ({ entry, target, reloadToken, onLocation }: DevPro
* two panels are never pointed at the same port.
*/
const PreviewFrame = ({ initialSrc, onLocation }: { initialSrc: string; onLocation: (path: string) => void }) => {
+ const { t } = useTranslation();
const [src] = useState(initialSrc);
const frame = useRef(null);
@@ -174,5 +178,5 @@ const PreviewFrame = ({ initialSrc, onLocation }: { initialSrc: string; onLocati
return () => window.removeEventListener('message', onMessage);
}, [onLocation]);
- return ;
+ return ;
};
diff --git a/src/workspaces/officerdev/src/apps/Preview/PreviewHeader.tsx b/src/workspaces/officerdev/src/apps/Preview/PreviewHeader.tsx
index 79a51faf..8716bcec 100644
--- a/src/workspaces/officerdev/src/apps/Preview/PreviewHeader.tsx
+++ b/src/workspaces/officerdev/src/apps/Preview/PreviewHeader.tsx
@@ -2,17 +2,19 @@ import { useEffect, useState } from 'react';
import { Globe, RefreshCw, ExternalLink, Pencil, Check, X } from 'lucide-react';
import { useWorkspace } from '../../components/Workspace';
import { normalizeUrl, usePreview } from './use-preview';
+import { useTranslation } from '../../locales';
/**
* The preview panel's header: where it points, reload, open in a real tab, and change it.
*
- * "Open in a new tab" is not decoration. A framed app gets PARTITIONED storage — the browser keys it by
+ * t('preview.openNewTab') is not decoration. A framed app gets PARTITIONED storage — the browser keys it by
* top-level site — so the login inside this panel is separate from the login in a tab, and a page that
* refuses to be framed at all (`X-Frame-Options`, CSP `frame-ancestors`) renders blank here with only a
* console error. When either happens, the tab is the answer, and it should be one click away rather than
* something you rebuild by copying the URL out.
*/
export const PreviewHeader = ({ panelId }: { panelId?: string }) => {
+ const { t, tUi } = useTranslation();
const { workspace } = useWorkspace();
const { url, target, display, setUrl, reload } = usePreview(panelId, workspace);
@@ -46,13 +48,13 @@ export const PreviewHeader = ({ panelId }: { panelId?: string }) => {
}}
className="min-w-0 flex-1 rounded border border-duck-dark/20 bg-transparent px-1.5 py-0.5 text-xs"
/>
-