Type the i18n seam and sort the locale file

This commit is contained in:
Anthony Stirling
2026-08-25 10:37:25 +01:00
parent d7b7168e0e
commit 56e8498eae
4 changed files with 28 additions and 11 deletions
@@ -4102,6 +4102,12 @@ deviceHint = "Removes the local copy. The cloud copy is kept."
everywhere = "Everywhere"
everywhereHint = "Deletes the file from this device and the cloud."
[filesPage.diskLink]
conflict = "Disk changed"
conflictHint = "The file on disk changed while you had unsaved edits. Your version is shown - saving will overwrite the one on disk."
orphaned = "Not on disk"
orphanedHint = "The original at {{path}} is gone. This copy is only here - saving it will ask for a new location."
[filesPage.empty]
hint = "Drop PDFs anywhere on this page to upload, or use the New folder button to organize your files."
newFolderCta = "Create folder"
@@ -4189,12 +4195,6 @@ newFolderPlaceholder = "Folder name"
newFolderToggle = "Create new folder…"
title = "Move to folder"
[filesPage.diskLink]
conflict = "Disk changed"
conflictHint = "The file on disk changed while you had unsaved edits. Your version is shown - saving will overwrite the one on disk."
orphaned = "Not on disk"
orphanedHint = "The original at {{path}} is gone. This copy is only here - saving it will ask for a new location."
[filesPage.origin]
all = "All sources"
cloud = "Cloud"
@@ -31,8 +31,11 @@ vi.mock("@app/services/fileStorage", () => ({
const downloadFileWithPolicy = vi.hoisted(() =>
vi.fn(
async (_request: Record<string, unknown>) =>
({ savedPath: "C:/elsewhere/report.pdf" }) as any,
async (
_request: Record<string, unknown>,
): Promise<{ savedPath?: string; cancelled?: boolean }> => ({
savedPath: "C:/elsewhere/report.pdf",
}),
),
);
vi.mock("@app/services/exportWithPolicy", () => ({ downloadFileWithPolicy }));
@@ -274,6 +274,19 @@ export async function saveOrphanAsCopy(
}
}
/** The bit of i18next this module needs, if it has been initialised at all. */
interface Translator {
t: (key: string, options?: Record<string, unknown>) => string;
}
function isTranslator(value: unknown): value is Translator {
return (
typeof value === "object" &&
value !== null &&
typeof (value as Translator).t === "function"
);
}
/**
* Best-effort translation without a hard dependency on i18n being initialised —
* this module runs from storage/hydration paths that have no React context. The
@@ -286,8 +299,8 @@ function translate(
params?: Record<string, string>,
): string {
try {
const i18next = (globalThis as Record<string, any>)?.i18next;
if (i18next && typeof i18next.t === "function") {
const i18next = (globalThis as Record<string, unknown>).i18next;
if (isTranslator(i18next)) {
return i18next.t(key, { defaultValue, ...params });
}
} catch {
@@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import type { StirlingFileStub } from "@app/types/fileContext";
import type { FileId } from "@app/types/file";
import type { ToolId } from "@app/types/toolId";
const disk = vi.hoisted(() => ({
supported: true,
@@ -94,7 +95,7 @@ describe("pruneMissingRecentFiles", () => {
id: "edited" as FileId,
localFilePath: "C:/docs/gone.pdf",
versionNumber: 3,
toolHistory: [{ toolId: "split" as any, timestamp: 1 }],
toolHistory: [{ toolId: "split" as ToolId, timestamp: 1 }],
}),
];
const result = await pruneMissingRecentFiles(stubs);