refactor(i18n): route remaining Obsidian UI text through the message catalogue

Continues the source-key migration inside the application boundary introduced
in 1.0.0, where LiveSync owns its catalogue and consumes Commonlib as a
published package.

- Replace hardcoded user-visible strings in the Obsidian UI (Setup Wizard
  dialogues, P2P panes, Customisation Sync panes, Global History, the JSON
  conflict pane and the remote-configuration menu) with `$msg` calls, keeping
  the English source string as the key.
- Wire up strings whose translations already existed in the catalogue but were
  still rendered as literals, for example the whole Intro dialogue.
- Add the new entries to `src/common/messagesYAML/en.yaml` and `es.yaml`, then
  regenerate `messagesJson/` and `combinedMessages.prod.ts` through the
  documented `i18n:bake` pipeline.
- No Commonlib gitlink or catalogue is involved; every change is
  LiveSync-owned.

Regenerating the catalogue also normalises three pre-existing entries each in
`ko.json` and `zh.json`, where the committed JSON kept a trailing space before
a newline that YAML cannot represent.

Verified with tsc-check, tsc-check:apps, svelte-check and lint.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zeedif
2026-07-30 18:59:19 -06:00
co-authored by Claude Opus 5
parent c385bd7ce7
commit b6746be73e
37 changed files with 3624 additions and 499 deletions
+29 -18
View File
@@ -11,6 +11,7 @@
import type ObsidianLiveSyncPlugin from "@/main";
// import { askString } from "../../common/utils";
import { Menu } from "@/deps.ts";
import { $msg as translateMessage } from "@/common/translation";
export let list: IPluginDataExDisplay[] = [];
export let thisTerm = "";
@@ -61,25 +62,25 @@
// NO OP. what's happened?
freshness = "";
} else if (local && !remote) {
freshness = "Local only";
freshness = translateMessage("Local only");
} else if (remote && !local) {
freshness = "Remote only";
freshness = translateMessage("Remote only");
canApply = true;
} else {
const dtDiff = (local?.mtime ?? 0) - (remote?.mtime ?? 0);
const diff = timeDeltaToHumanReadable(Math.abs(dtDiff));
if (dtDiff / 1000 < -10) {
// freshness = "✓ Newer";
freshness = `Newer (${diff})`;
freshness = translateMessage("Newer (${diff})", { diff });
canApply = true;
contentCheck = true;
} else if (dtDiff / 1000 > 10) {
// freshness = "⚠ Older";
freshness = `Older (${diff})`;
freshness = translateMessage("Older (${diff})", { diff });
canApply = true;
contentCheck = true;
} else {
freshness = "Same";
freshness = translateMessage("Same");
canApply = false;
contentCheck = true;
}
@@ -89,11 +90,17 @@
if (local?.version || remote?.version) {
const compare = `${localVersionStr}`.localeCompare(remoteVersionStr, undefined, { numeric: true });
if (compare == 0) {
version = "Same";
version = translateMessage("Same");
} else if (compare < 0) {
version = `Lower (${localVersionStr} < ${remoteVersionStr})`;
version = translateMessage("Lower (${local} < ${remote})", {
local: localVersionStr,
remote: remoteVersionStr,
});
} else if (compare > 0) {
version = `Higher (${localVersionStr} > ${remoteVersionStr})`;
version = translateMessage("Higher (${local} > ${remote})", {
local: localVersionStr,
remote: remoteVersionStr,
});
}
}
@@ -135,19 +142,19 @@
})
.reduce((p, c) => p | (c as number), 0 as number);
if (matchingStatus == 0b0000100) {
equivalency = "Same";
equivalency = translateMessage("Same");
canApply = false;
} else if (matchingStatus <= 0b0000100) {
equivalency = "Same or local only";
equivalency = translateMessage("Same or local only");
canApply = false;
} else if (matchingStatus == 0b0010000) {
canApply = true;
canCompare = true;
equivalency = "Different";
equivalency = translateMessage("Different");
} else {
canApply = true;
canCompare = true;
equivalency = "Mixed";
equivalency = translateMessage("Mixed");
}
return { equivalency, canApply, canCompare };
}
@@ -244,7 +251,7 @@
if (selected == "") {
// NO OP.
} else if (selected == thisTerm) {
freshness = "This device";
freshness = translateMessage("This device");
canApply = false;
} else {
const local = list.find((e) => e.term == thisTerm);
@@ -304,11 +311,11 @@
if (!local) return;
if (!selectedItem) return;
const menu = new Menu();
menu.addItem((item) => item.setTitle("Compare file").setIsLabel(true));
menu.addItem((item) => item.setTitle(translateMessage("Compare file")).setIsLabel(true));
menu.addSeparator();
const files = unique(local.files.map((e) => e.filename).concat(selectedItem.files.map((e) => e.filename)));
const convDate = (dt: PluginDataExFile | undefined) => {
if (!dt) return "(Missing)";
if (!dt) return translateMessage("(Missing)");
const d = new Date(dt.mtime);
return d.toLocaleString();
};
@@ -335,10 +342,14 @@
Logger(`Could not find local item`, LOG_LEVEL_VERBOSE);
return;
}
const duplicateTermName = await core.confirm.askString("Duplicate", "device name", "");
const duplicateTermName = await core.confirm.askString(
translateMessage("Duplicate"),
translateMessage("device name"),
""
);
if (duplicateTermName) {
if (duplicateTermName.contains("/")) {
Logger(`We can not use "/" to the device name`, LOG_LEVEL_NOTICE);
Logger(translateMessage('We can not use "/" to the device name'), LOG_LEVEL_NOTICE);
return;
}
const key = `${plugin.core.services.API.getSystemConfigDir()}/${local.files[0].filename}`;
@@ -391,7 +402,7 @@
{/if}
{:else}
<span class="spacer"></span>
<span class="message even">All the same or non-existent</span>
<span class="message even">{translateMessage("All the same or non-existent")}</span>
<!-- svelte-ignore a11y_consider_explicit_label -->
<button disabled></button>
<!-- svelte-ignore a11y_consider_explicit_label -->