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
@@ -12,6 +12,7 @@
import type { LiveSyncTrysteroReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/LiveSyncTrysteroReplicator";
import { delay, fireAndForget } from "octagonal-wheels/promises";
import P2PServerStatusCard from "./P2PServerStatusCard.svelte";
import { $msg as translateMessage } from "@/common/translation";
interface Props {
liveSyncReplicator: LiveSyncTrysteroReplicator;
@@ -84,11 +85,11 @@
}
function getAcceptanceStatus(peer: P2PServerInfo["knownAdvertisements"][number]) {
if (peer.isTemporaryAccepted === true) return "ACCEPTED (in session)";
if (peer.isAccepted === true) return "ACCEPTED";
if (peer.isTemporaryAccepted === false) return "DENIED (in session)";
if (peer.isAccepted === false) return "DENIED";
return "NEW";
if (peer.isTemporaryAccepted === true) return translateMessage("ACCEPTED (in session)");
if (peer.isAccepted === true) return translateMessage("ACCEPTED");
if (peer.isTemporaryAccepted === false) return translateMessage("DENIED (in session)");
if (peer.isAccepted === false) return translateMessage("DENIED");
return translateMessage("NEW");
}
function getAcceptanceStatusClass(peer: P2PServerInfo["knownAdvertisements"][number]) {
@@ -102,7 +103,7 @@
<P2PServerStatusCard {getLiveSyncReplicator} showBroadcastToggle={false} />
<div class="peers-section">
<h3>Available Peers</h3>
<h3>{translateMessage("Available Peers")}</h3>
{#if serverInfo && serverInfo.knownAdvertisements.length > 0}
<div class="peers-list">
{#each serverInfo.knownAdvertisements as peer (peer.peerId)}
@@ -126,14 +127,18 @@
disabled={syncingPeerId !== null}
onclick={() => handleSync(peer.peerId)}
>
{syncingPeerId === peer.peerId ? "Syncing..." : "Sync"}
{syncingPeerId === peer.peerId
? translateMessage("Syncing...")
: translateMessage("Sync")}
</button>
<button
class="btn {rebuildMode ? 'btn-primary' : 'btn-secondary'}"
disabled={syncingPeerId !== null}
onclick={() => handleSyncThenClose(peer.peerId)}
>
{syncingPeerId === peer.peerId ? "Syncing..." : "Start Sync & Close"}
{syncingPeerId === peer.peerId
? translateMessage("Syncing...")
: translateMessage("Start Sync & Close")}
</button>
{:else}
<button
@@ -141,7 +146,9 @@
disabled={syncingPeerId !== null}
onclick={() => handleSyncThenClose(peer.peerId)}
>
{syncingPeerId === peer.peerId ? "Syncing..." : "Sync"}
{syncingPeerId === peer.peerId
? translateMessage("Syncing...")
: translateMessage("Sync")}
</button>
{/if}
</div>
@@ -149,16 +156,22 @@
{/each}
</div>
{:else if serverInfo}
<p class="no-peers">No devices available. Waiting for other devices to connect...</p>
<p class="no-peers">
{translateMessage("No devices available. Waiting for other devices to connect...")}
</p>
{/if}
</div>
<div class="footer">
{#if rebuildMode}
<button class="btn btn-cancel" onclick={onClose} disabled={syncingPeerId !== null}>Skip and close</button>
<button class="btn btn-cancel" onclick={onClose} disabled={syncingPeerId !== null}
>{translateMessage("Skip and close")}</button
>
{:else}
<button class="btn btn-cancel" onclick={onClose}>Close</button>
<button class="btn btn-cancel" onclick={onCloseAndDisconnect}>Close & Disconnect</button>
<button class="btn btn-cancel" onclick={onClose}>{translateMessage("Close")}</button>
<button class="btn btn-cancel" onclick={onCloseAndDisconnect}
>{translateMessage("Close & Disconnect")}</button
>
{/if}
</div>
</div>