diff --git a/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts b/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts index b93fb3d9..41ad7bc2 100644 --- a/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts +++ b/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts @@ -4,7 +4,8 @@ import { TweakValuesShouldMatchedTemplate, TweakValuesTemplate, IncompatibleChanges, - confName, + configurationNames, + statusDisplay, type TweakValues, type ObsidianLiveSyncSettings, type RemoteDBSettings, @@ -15,11 +16,21 @@ import { } from "@vrtmrz/livesync-commonlib/compat/common/types"; import { escapeMarkdownValue } from "@vrtmrz/livesync-commonlib/compat/common/utils"; import { AbstractModule } from "@/modules/AbstractModule.ts"; -import { $msg } from "@/common/translation"; +import { $msg, translateIfAvailable } from "@/common/translation"; import type { InjectableServiceHub } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableServiceHub"; import type { LiveSyncCore } from "@/main.ts"; import { REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const"; +/** + * Localised counterpart of Commonlib's `confName()`, which takes no translator. + * Same shape: label plus status suffix, and an empty string for an unknown key. + */ +function localisedConfName(key: keyof ObsidianLiveSyncSettings): string { + const info = configurationNames[key]; + if (!info) return ""; + return `${translateIfAvailable(info.name)}${statusDisplay(info.status)}`; +} + function valueToString(value: string | number | boolean | object | undefined): string { if (typeof value === "boolean") { return value ? "true" : "false"; @@ -158,7 +169,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule { // table += `| ${confName(key)} | ${valueMine} | ${valuePreferred} | \n`; tableRows.push( $msg("TweakMismatchResolve.Table.Row", { - name: confName(key), + name: localisedConfName(key), self: valueToString(valueMine), remote: valueToString(valuePreferred), }) @@ -342,7 +353,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule { } tableRows.push( $msg("TweakMismatchResolve.Table.Row", { - name: confName(key), + name: localisedConfName(key), self: currentValueForDisplay, remote: remoteValueForDisplay, }) diff --git a/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.unit.spec.ts b/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.unit.spec.ts index 0f394d8b..e0fd1dc8 100644 --- a/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.unit.spec.ts +++ b/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.unit.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_SETTINGS, REMOTE_COUCHDB, @@ -6,6 +6,7 @@ import { type TweakValues, } from "@vrtmrz/livesync-commonlib/compat/common/types"; import { ModuleResolvingMismatchedTweaks } from "./ModuleResolveMismatchedTweaks"; +import { setLang } from "@/common/translation"; function createModule(settingsOverride: Partial = {}) { const askSelectStringDialogue = vi.fn(async (..._args: unknown[]): Promise => undefined); @@ -255,3 +256,43 @@ describe("ModuleResolvingMismatchedTweaks", () => { expect(calls).toEqual(["save", "reinitialise", "set-preferred"]); }); }); + +describe("ModuleResolvingMismatchedTweaks setting labels", () => { + afterEach(() => setLang("def")); + + async function renderMismatchTable() { + const { module, askSelectStringDialogue } = createModule({ + autoAcceptCompatibleTweak: true, + hashAlg: "xxhash64", + encrypt: false, + tweakModified: 100, + }); + const preferred = { + ...(DEFAULT_SETTINGS as unknown as TweakValues), + hashAlg: "xxhash32", + encrypt: true, + tweakModified: 200, + } as Partial; + + await module._checkAndAskResolvingMismatchedTweaks(preferred); + + return String(askSelectStringDialogue.mock.calls[0]?.[0] ?? ""); + } + + it("localises the setting names and keeps the status suffix", async () => { + setLang("zh-tw"); + + const message = await renderMismatchTable(); + + expect(message).toContain("chunk ID 的雜湊演算法 (Experimental)"); + expect(message).toContain("端對端加密"); + expect(message).not.toContain("The Hash algorithm for chunk IDs"); + }); + + it("leaves English unchanged", async () => { + const message = await renderMismatchTable(); + + expect(message).toContain("The Hash algorithm for chunk IDs (Experimental)"); + expect(message).toContain("End-to-End Encryption"); + }); +}); diff --git a/src/modules/features/SettingDialogue/settingConstants.ts b/src/modules/features/SettingDialogue/settingConstants.ts index c24d9a27..495f8f76 100644 --- a/src/modules/features/SettingDialogue/settingConstants.ts +++ b/src/modules/features/SettingDialogue/settingConstants.ts @@ -1 +1,37 @@ -export * from "@vrtmrz/livesync-commonlib/compat/common/settingConstants"; +export { + AllSettingDefault, + OnDialogSettingsDefault, + SettingInformation, +} from "@vrtmrz/livesync-commonlib/compat/common/settingConstants"; +export type { + AllSettings, + AllSettingItemKey, + AllStringItemKey, + AllNumericItemKey, + AllBooleanItemKey, + OnDialogSettings, + ValueOf, +} from "@vrtmrz/livesync-commonlib/compat/common/settingConstants"; + +import { + getConfig as getCommonlibConfig, + getConfName as getCommonlibConfName, + type AllSettingItemKey, +} from "@vrtmrz/livesync-commonlib/compat/common/settingConstants"; +import type { MessageTranslator } from "@vrtmrz/livesync-commonlib/context"; +import { translateLiveSyncMessage } from "@/common/translation"; + +// Commonlib defaults `translate` to its English-only translator, so every caller which omits +// it silently renders English regardless of `displayLanguage`. Default it to the LiveSync +// catalogue instead, and re-export these wrappers under the original names so that no call +// site has to remember the second argument. + +/** `getConfig` with the LiveSync catalogue applied by default. */ +export function getConfig(key: AllSettingItemKey, translate: MessageTranslator = translateLiveSyncMessage) { + return getCommonlibConfig(key, translate); +} + +/** `getConfName` with the LiveSync catalogue applied by default. See `getConfig`. */ +export function getConfName(key: AllSettingItemKey, translate: MessageTranslator = translateLiveSyncMessage) { + return getCommonlibConfName(key, translate); +} diff --git a/src/modules/features/SettingDialogue/settingConstants.unit.spec.ts b/src/modules/features/SettingDialogue/settingConstants.unit.spec.ts new file mode 100644 index 00000000..ca194579 --- /dev/null +++ b/src/modules/features/SettingDialogue/settingConstants.unit.spec.ts @@ -0,0 +1,33 @@ +import { afterEach, describe, expect, it } from "vitest"; + +import { setLang } from "@/common/translation"; +import { getConfig, getConfName } from "./settingConstants"; + +describe("setting manifest labels", () => { + afterEach(() => setLang("def")); + + it("renders names and descriptions in the selected display language", () => { + setLang("zh-tw"); + + expect(getConfName("liveSync")).toBe("同步模式"); + expect(getConfig("couchDB_URI")).toMatchObject({ name: "伺服器 URI" }); + expect(getConfig("encrypt")).toMatchObject({ + name: "端對端加密", + desc: "加密遠端資料庫中的內容。如果你使用外掛的同步功能,建議啟用此選項。", + }); + }); + + it("leaves English untouched, so that the catalogue key and its English value stay interchangeable", () => { + expect(getConfName("liveSync")).toBe("Sync Mode"); + expect(getConfig("encrypt")).toMatchObject({ + name: "End-to-End Encryption", + desc: "Encrypt contents on the remote database. If you use the plugin's synchronization feature, enabling this is recommended.", + }); + }); + + it("passes through labels which Commonlib owns but the catalogue does not carry", () => { + setLang("zh-tw"); + + expect(getConfName("chunkSplitterVersion")).toBe("Chunk Splitter"); + }); +});