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
+46 -31
View File
@@ -23,6 +23,7 @@
import { HiddenFileSync } from "@/features/HiddenFileSync/CmdHiddenFileSync.ts";
import { LOG_LEVEL_NOTICE, Logger } from "octagonal-wheels/common/logger";
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts";
import { $msg as translateMessage } from "@/common/translation";
export let plugin: ObsidianLiveSyncPlugin;
export let core :LiveSyncBaseCore;
// $: core = plugin.core;
@@ -32,15 +33,17 @@
const addOn = core.getAddOn<ConfigSync>(ConfigSync.name)!;
if (!addOn) {
const msg =
"AddOn Module (ConfigSync) has not been loaded. This is very unexpected situation. Please report this issue.";
const msg = translateMessage(
"AddOn Module (ConfigSync) has not been loaded. This is very unexpected situation. Please report this issue."
);
Logger(msg, LOG_LEVEL_NOTICE);
throw new Error(msg);
}
const addOnHiddenFileSync = core.getAddOn<HiddenFileSync>(HiddenFileSync.name) as HiddenFileSync;
if (!addOnHiddenFileSync) {
const msg =
"AddOn Module (HiddenFileSync) has not been loaded. This is very unexpected situation. Please report this issue.";
const msg = translateMessage(
"AddOn Module (HiddenFileSync) has not been loaded. This is very unexpected situation. Please report this issue."
);
Logger(msg, LOG_LEVEL_NOTICE);
throw new Error(msg);
}
@@ -92,9 +95,9 @@
}
const displays = {
CONFIG: "Configuration",
THEME: "Themes",
SNIPPET: "Snippets",
CONFIG: translateMessage("Configuration"),
THEME: translateMessage("Themes"),
SNIPPET: translateMessage("Snippets"),
};
async function scanAgain() {
await addOn.scanAllConfigFiles(true);
@@ -156,20 +159,20 @@
}
function askOverwriteModeForAutomatic(evt: MouseEvent, key: string) {
const menu = new Menu();
menu.addItem((item) => item.setTitle("Initial Action").setIsLabel(true));
menu.addItem((item) => item.setTitle(translateMessage("Initial Action")).setIsLabel(true));
menu.addSeparator();
menu.addItem((item) => {
item.setTitle(`↑: Overwrite Remote`).onClick((e) => {
item.setTitle(translateMessage("↑: Overwrite Remote")).onClick((e) => {
applyAutomaticSync(key, "pushForce");
});
})
.addItem((item) => {
item.setTitle(`↓: Overwrite Local`).onClick((e) => {
item.setTitle(translateMessage("↓: Overwrite Local")).onClick((e) => {
applyAutomaticSync(key, "pullForce");
});
})
.addItem((item) => {
item.setTitle(`⇅: Use newer`).onClick((e) => {
item.setTitle(translateMessage("⇅: Use newer")).onClick((e) => {
applyAutomaticSync(key, "safe");
});
});
@@ -201,10 +204,10 @@
[MODE_SHINY]: ICON_EMOJI_FLAGGED,
};
const TITLES: { [key: number]: string } = {
[MODE_SELECTIVE]: "Selective",
[MODE_PAUSED]: "Ignore",
[MODE_AUTOMATIC]: "Automatic",
[MODE_SHINY]: "Flagged Selective",
[MODE_SELECTIVE]: translateMessage("Selective"),
[MODE_PAUSED]: translateMessage("Ignore"),
[MODE_AUTOMATIC]: translateMessage("Automatic"),
[MODE_SHINY]: translateMessage("Flagged Selective"),
};
const PREFIX_PLUGIN_ALL = "PLUGIN_ALL";
const PREFIX_PLUGIN_DATA = "PLUGIN_DATA";
@@ -329,28 +332,30 @@
<div class="buttonsWrap">
<div class="buttons">
<button on:click={() => scanAgain()}>Scan changes</button>
<button on:click={() => replicate()}>Sync once</button>
<button on:click={() => requestUpdate()}>Refresh</button>
<button on:click={() => scanAgain()}>{translateMessage("Scan changes")}</button>
<button on:click={() => replicate()}>{translateMessage("Sync once")}</button>
<button on:click={() => requestUpdate()}>{translateMessage("Refresh")}</button>
{#if isMaintenanceMode}
<button on:click={() => requestReload()}>Reload</button>
<button on:click={() => requestReload()}>{translateMessage("Reload")}</button>
{/if}
</div>
<div class="buttons">
<button on:click={() => selectAllNewest(true)}>Select All Shiny</button>
<button on:click={() => selectAllNewest(false)}>{ICON_EMOJI_FLAGGED} Select Flagged Shiny</button>
<button on:click={() => resetSelectNewest()}>Deselect all</button>
<button on:click={() => applyAll()} class="mod-cta">Apply All Selected</button>
<button on:click={() => selectAllNewest(true)}>{translateMessage("Select All Shiny")}</button>
<button on:click={() => selectAllNewest(false)}
>{ICON_EMOJI_FLAGGED} {translateMessage("Select Flagged Shiny")}</button
>
<button on:click={() => resetSelectNewest()}>{translateMessage("Deselect all")}</button>
<button on:click={() => applyAll()} class="mod-cta">{translateMessage("Apply All Selected")}</button>
</div>
</div>
<div class="loading">
{#if loading || $pluginV2Progress !== 0}
<span>Updating list...{$pluginV2Progress == 0 ? "" : ` (${$pluginV2Progress})`}</span>
<span>{translateMessage("Updating list...")}{$pluginV2Progress == 0 ? "" : ` (${$pluginV2Progress})`}</span>
{/if}
</div>
<div class="list">
{#if list.length == 0}
<div class="center">No Items.</div>
<div class="center">{translateMessage("No Items.")}</div>
{:else}
{#each displayEntries as [key, label]}
<div>
@@ -382,7 +387,7 @@
</div>
{/each}
<div>
<h3>Plugins</h3>
<h3>{translateMessage("Plugins")}</h3>
{#each pluginEntries as [name, listX]}
{@const bindKeyAll = `${PREFIX_PLUGIN_ALL}/${name}`}
{@const modeAll = automaticListDisp.get(bindKeyAll) ?? MODE_SELECTIVE}
@@ -464,7 +469,7 @@
>
{getIcon(modeEtc)}
</button>
<span class="name">Other files</span>
<span class="name">{translateMessage("Other files")}</span>
</div>
<div class="body">
{#if modeEtc == MODE_SELECTIVE || modeEtc == MODE_SHINY}
@@ -492,9 +497,9 @@
{#if isMaintenanceMode}
<div class="buttons">
<div>
<h3>Maintenance Commands</h3>
<h3>{translateMessage("Maintenance Commands")}</h3>
<div class="maintenancerow">
<label for="">Delete All of </label>
<label for="">{translateMessage("Delete All of")} </label>
<select bind:value={deleteTerm}>
{#each allTerms as term}
<option value={term}>{term}</option>
@@ -513,10 +518,20 @@
</div>
{/if}
<div class="buttons">
<label><span>Hide not applicable items</span><input type="checkbox" bind:checked={hideEven} /></label>
<label
><span>{translateMessage("Hide not applicable items")}</span><input
type="checkbox"
bind:checked={hideEven}
/></label
>
</div>
<div class="buttons">
<label><span>Maintenance mode</span><input type="checkbox" bind:checked={isMaintenanceMode} /></label>
<label
><span>{translateMessage("Maintenance mode")}</span><input
type="checkbox"
bind:checked={isMaintenanceMode}
/></label
>
</div>
<style>