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
@@ -1,6 +1,7 @@
<script lang="ts">
import { AcceptedStatus, type PeerStatus } from "@vrtmrz/livesync-commonlib/compat/replication/trystero/P2PReplicatorPaneCommon";
import type { P2PReplicatorHandle } from "./P2PReplicatorPaneHost";
import { $msg as translateMessage } from "@/common/translation";
interface Props {
peerStatus: PeerStatus;
@@ -27,6 +28,11 @@
peer.isSending ? ["SENDING"] : [],
].flat()
);
const chipLabels: Record<string, string> = {
WATCHING: translateMessage("WATCHING"),
FETCHING: translateMessage("FETCHING"),
SENDING: translateMessage("SENDING"),
};
let acceptedStatusChip = $derived.by(() =>
select(
peer.accepted.toString(),
@@ -40,6 +46,13 @@
""
) ?? ""
);
const acceptedStatusLabels: Record<string, string> = {
ACCEPTED: translateMessage("ACCEPTED"),
"ACCEPTED (in session)": translateMessage("ACCEPTED (in session)"),
"DENIED (in session)": translateMessage("DENIED (in session)"),
DENIED: translateMessage("DENIED"),
NEW: translateMessage("NEW"),
};
const classList = {
["SENDING"]: "connected",
["FETCHING"]: "connected",
@@ -75,13 +88,13 @@
const peerAttrLabels = $derived.by(() => {
const attrs = [];
if (peer.syncOnConnect) {
attrs.push("✔ SYNC");
attrs.push(translateMessage("✔ SYNC"));
}
if (peer.watchOnConnect) {
attrs.push("✔ WATCH");
attrs.push(translateMessage("✔ WATCH"));
}
if (peer.syncOnReplicationCommand) {
attrs.push("✔ SELECT");
attrs.push(translateMessage("✔ SELECT"));
}
return attrs;
});
@@ -113,12 +126,14 @@
</div>
<div class="status-chips">
<div class="row">
<span class="chip {select(acceptedStatusChip, classList)}">{acceptedStatusChip}</span>
<span class="chip {select(acceptedStatusChip, classList)}"
>{acceptedStatusLabels[acceptedStatusChip] ?? acceptedStatusChip}</span
>
</div>
{#if isAccepted}
<div class="row">
{#each statusChips as chip}
<span class="chip {select(chip, classList)}">{chip}</span>
<span class="chip {select(chip, classList)}">{chipLabels[chip] ?? chip}</span>
{/each}
</div>
{/if}
@@ -134,15 +149,25 @@
<div class="row">
{#if isNew}
{#if !isAccepted}
<button class="button" onclick={() => makeDecision(true, true)}>Accept in session</button>
<button class="button mod-cta" onclick={() => makeDecision(true, false)}>Accept</button>
<button class="button" onclick={() => makeDecision(true, true)}
>{translateMessage("Accept in session")}</button
>
<button class="button mod-cta" onclick={() => makeDecision(true, false)}
>{translateMessage("Accept")}</button
>
{/if}
{#if !isDenied}
<button class="button" onclick={() => makeDecision(false, true)}>Deny in session</button>
<button class="button mod-warning" onclick={() => makeDecision(false, false)}>Deny</button>
<button class="button" onclick={() => makeDecision(false, true)}
>{translateMessage("Deny in session")}</button
>
<button class="button mod-warning" onclick={() => makeDecision(false, false)}
>{translateMessage("Deny")}</button
>
{/if}
{:else}
<button class="button mod-warning" onclick={() => revokeDecision()}>Revoke</button>
<button class="button mod-warning" onclick={() => revokeDecision()}
>{translateMessage("Revoke")}</button
>
{/if}
</div>
</div>
@@ -155,9 +180,9 @@
<!-- <button class="button" onclick={replicateFrom} disabled={peer.isFetching}>📥</button>
<button class="button" onclick={replicateTo} disabled={peer.isSending}>📤</button> -->
{#if peer.isWatching}
<button class="button" onclick={stopWatching}>Stop ⚡</button>
<button class="button" onclick={stopWatching}>{translateMessage("Stop ⚡")}</button>
{:else}
<button class="button" onclick={startWatching} title="live"></button>
<button class="button" onclick={startWatching} title={translateMessage("live")}></button>
{/if}
{#if showPeerMenu}
<button class="button" onclick={moreMenu}>...</button>