feat(i18n): convert Settings sync section to t() (Batch E)
This commit is contained in:
@@ -706,16 +706,18 @@
|
||||
{t("nav.about")}
|
||||
</button>
|
||||
</nav>
|
||||
<button class="close" onclick={onClose} aria-label="Close" title="Close (Esc)">
|
||||
<button
|
||||
class="close"
|
||||
onclick={onClose}
|
||||
aria-label={t("settings.close")}
|
||||
title={t("settings.close_title")}
|
||||
>
|
||||
<X size={18} aria-hidden="true" />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{#if settings.backendStub && section === "sync"}
|
||||
<p class="stub">
|
||||
Preview mode — the sync backend isn't implemented yet (Phase 9). Changes are kept in the UI
|
||||
only.
|
||||
</p>
|
||||
<p class="stub">{t("settings.sync.stub")}</p>
|
||||
{/if}
|
||||
|
||||
{#if section === "recording"}
|
||||
@@ -1261,51 +1263,52 @@
|
||||
</section>
|
||||
{:else if section === "sync"}
|
||||
<section>
|
||||
<h3>Sync & upload</h3>
|
||||
<h3>{t("settings.sync.title")}</h3>
|
||||
<label class="row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.settings.sync_enabled}
|
||||
onchange={(e) => settings.setSyncEnabled((e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
<span>Enable uploading meeting artifacts to configured targets</span>
|
||||
<span>{t("settings.sync.enable_label")}</span>
|
||||
</label>
|
||||
<p class="muted">
|
||||
Off by default. Nothing is uploaded unless this is on and a target is enabled. Self-hosted
|
||||
targets keep data on your own server; third-party clouds are clearly labeled.
|
||||
</p>
|
||||
<p class="muted">{t("settings.sync.enable_hint")}</p>
|
||||
|
||||
<h4>Targets</h4>
|
||||
<h4>{t("settings.sync.targets_title")}</h4>
|
||||
{#if settings.targets.length === 0}
|
||||
<p class="muted">No targets yet. Add one below.</p>
|
||||
<p class="muted">{t("settings.sync.no_targets")}</p>
|
||||
{:else}
|
||||
<ul class="targets">
|
||||
{#each settings.targets as t (t.id)}
|
||||
{#each settings.targets as target (target.id)}
|
||||
<li>
|
||||
<label class="tg">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={t.enabled}
|
||||
onchange={() => settings.toggleTargetEnabled(t)}
|
||||
checked={target.enabled}
|
||||
onchange={() => settings.toggleTargetEnabled(target)}
|
||||
/>
|
||||
<span class="name">{t.name || t.kind}</span>
|
||||
<span class="name">{target.name || target.kind}</span>
|
||||
</label>
|
||||
<span class="badge" class:third={t.third_party}
|
||||
>{t.third_party ? "third-party" : "your server"}</span
|
||||
<span class="badge" class:third={target.third_party}
|
||||
>{target.third_party
|
||||
? t("settings.sync.third_party")
|
||||
: t("settings.sync.your_server")}</span
|
||||
>
|
||||
<span class="host">{t.host ?? t.kind}</span>
|
||||
{#if !t.third_party}
|
||||
<button class="link" onclick={() => startEdit(t)}>Edit</button>
|
||||
<span class="host">{target.host ?? target.kind}</span>
|
||||
{#if !target.third_party}
|
||||
<button class="link" onclick={() => startEdit(target)}
|
||||
>{t("settings.sync.edit")}</button
|
||||
>
|
||||
{/if}
|
||||
<button class="link danger" onclick={() => settings.removeTarget(t.id)}
|
||||
>Remove</button
|
||||
<button class="link danger" onclick={() => settings.removeTarget(target.id)}
|
||||
>{t("settings.sync.remove")}</button
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
<h4>{editingId ? "Edit target" : "Add a target"}</h4>
|
||||
<h4>{editingId ? t("settings.sync.edit_target") : t("settings.sync.add_target")}</h4>
|
||||
{#if !editingId}
|
||||
<div class="kinds">
|
||||
{#each ["webdav", "onedrive", "dropbox", "box"] as k (k)}
|
||||
@@ -1317,20 +1320,22 @@
|
||||
{/if}
|
||||
|
||||
{#if form.kind === "webdav"}
|
||||
<p class="muted">
|
||||
WebDAV covers Nextcloud, ownCloud, Cloudreve, Seafile, and Synology. (Seafile: enable
|
||||
SeafDAV server-side.)
|
||||
</p>
|
||||
<p class="muted">{t("settings.sync.webdav_hint")}</p>
|
||||
<div class="grid">
|
||||
<label>Name<input bind:value={form.name} placeholder="Home Nextcloud" /></label>
|
||||
<label
|
||||
>Provider
|
||||
>{t("settings.sync.name")}<input
|
||||
bind:value={form.name}
|
||||
placeholder={t("settings.sync.name_placeholder")}
|
||||
/></label
|
||||
>
|
||||
<label
|
||||
>{t("settings.sync.provider")}
|
||||
<select bind:value={form.provider_hint}>
|
||||
{#each PROVIDERS as p (p)}<option value={p}>{p}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label class="wide"
|
||||
>Server URL<input
|
||||
>{t("settings.sync.server_url")}<input
|
||||
bind:value={form.base_url}
|
||||
placeholder={davAutoPath
|
||||
? "https://cloud.example.com"
|
||||
@@ -1338,36 +1343,46 @@
|
||||
/>
|
||||
{#if davAutoPath}
|
||||
<small class="muted"
|
||||
>Just your server URL — WhispAssist adds
|
||||
<code>/remote.php/dav/files/<username>/</code> automatically.</small
|
||||
>{t("settings.sync.server_url_note_1")}
|
||||
<code>/remote.php/dav/files/<username>/</code>
|
||||
{t("settings.sync.server_url_note_2")}</small
|
||||
>
|
||||
{/if}
|
||||
</label>
|
||||
<label>Remote folder<input bind:value={form.remote_base_path} /></label>
|
||||
<label>Username<input bind:value={form.username} /></label>
|
||||
<label
|
||||
>App password<input
|
||||
>{t("settings.sync.remote_folder")}<input bind:value={form.remote_base_path} /></label
|
||||
>
|
||||
<label>{t("settings.sync.username")}<input bind:value={form.username} /></label>
|
||||
<label
|
||||
>{t("settings.sync.app_password")}<input
|
||||
type="password"
|
||||
bind:value={form.secret}
|
||||
placeholder={editingId
|
||||
? "leave blank to keep current password"
|
||||
: "stored in OS credential store"}
|
||||
placeholder={editingId ? t("settings.sync.pw_keep") : t("settings.sync.pw_store")}
|
||||
/></label
|
||||
>
|
||||
</div>
|
||||
<fieldset class="artifacts">
|
||||
<legend>Upload</legend>
|
||||
<label><input type="checkbox" bind:checked={form.upload_transcript} /> transcript</label
|
||||
>
|
||||
<label><input type="checkbox" bind:checked={form.upload_notes} /> notes</label>
|
||||
<label><input type="checkbox" bind:checked={form.upload_summary} /> summary</label>
|
||||
<legend>{t("settings.sync.upload_legend")}</legend>
|
||||
<label
|
||||
><input type="checkbox" bind:checked={form.upload_recording} /> recording (.wav, if retained)</label
|
||||
><input type="checkbox" bind:checked={form.upload_transcript} />
|
||||
{t("settings.sync.artifact_transcript")}</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" bind:checked={form.upload_notes} />
|
||||
{t("settings.sync.artifact_notes")}</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" bind:checked={form.upload_summary} />
|
||||
{t("settings.sync.artifact_summary")}</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" bind:checked={form.upload_recording} />
|
||||
{t("settings.sync.artifact_recording")}</label
|
||||
>
|
||||
</fieldset>
|
||||
<label class="row small"
|
||||
><input type="checkbox" bind:checked={form.allow_plaintext_lan} /> Allow plaintext http for
|
||||
a LAN address (not recommended)</label
|
||||
><input type="checkbox" bind:checked={form.allow_plaintext_lan} />
|
||||
{t("settings.sync.allow_plaintext")}</label
|
||||
>
|
||||
<label class="row small">
|
||||
<input
|
||||
@@ -1375,16 +1390,19 @@
|
||||
bind:checked={form.encrypt_before_upload}
|
||||
disabled={!vault?.unlocked}
|
||||
/>
|
||||
Encrypt before upload (destination stores only ciphertext)
|
||||
{#if !vault?.unlocked}<span class="muted">— requires an unlocked vault</span>{/if}
|
||||
{t("settings.sync.encrypt_before")}
|
||||
{#if !vault?.unlocked}<span class="muted">{t("settings.sync.requires_vault")}</span
|
||||
>{/if}
|
||||
</label>
|
||||
<div class="actions">
|
||||
<button onclick={testConnection}>Test connection</button>
|
||||
<button onclick={testConnection}>{t("settings.sync.test_connection")}</button>
|
||||
<button class="primary" onclick={saveTarget} disabled={!form.name || !form.base_url}
|
||||
>{editingId ? "Save changes" : "Add target"}</button
|
||||
>{editingId
|
||||
? t("settings.sync.save_changes")
|
||||
: t("settings.sync.add_target_btn")}</button
|
||||
>
|
||||
{#if editingId}
|
||||
<button class="link" onclick={cancelEdit}>Cancel</button>
|
||||
<button class="link" onclick={cancelEdit}>{t("settings.sync.cancel")}</button>
|
||||
{/if}
|
||||
{#if testResult}
|
||||
<span class="test" class:ok={testResult.ok} class:fail={!testResult.ok}>
|
||||
@@ -1399,27 +1417,28 @@
|
||||
{:else}
|
||||
<div class="banner">
|
||||
<AlertTriangle size={14} aria-hidden="true" />
|
||||
{form.kind} is a third-party cloud — uploading sends your data off your device to {form.kind}.
|
||||
{t("settings.sync.third_party_banner", { kind: form.kind })}
|
||||
</div>
|
||||
<div class="grid">
|
||||
<label class="wide">Name<input bind:value={form.name} placeholder={form.kind} /></label>
|
||||
<label class="wide"
|
||||
>{t("settings.sync.name")}<input
|
||||
bind:value={form.name}
|
||||
placeholder={form.kind}
|
||||
/></label
|
||||
>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="primary" onclick={linkOAuth}>Link {form.kind} account…</button>
|
||||
<span class="muted"
|
||||
>Opens an OAuth sign-in (loopback redirect); the token is stored in your OS credential
|
||||
store.</span
|
||||
<button class="primary" onclick={linkOAuth}
|
||||
>{t("settings.sync.link_account", { kind: form.kind })}</button
|
||||
>
|
||||
<span class="muted">{t("settings.sync.oauth_hint")}</span>
|
||||
</div>
|
||||
{#if settings.linkMessage}
|
||||
<p class="muted">{settings.linkMessage}</p>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<p class="muted footnote">
|
||||
Credentials are never written to settings or the database — only the OS credential store.
|
||||
TLS is required for non-LAN targets.
|
||||
</p>
|
||||
<p class="muted footnote">{t("settings.sync.footnote")}</p>
|
||||
</section>
|
||||
{:else if section === "ai"}
|
||||
<section>
|
||||
|
||||
Reference in New Issue
Block a user