mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-21 09:23:00 +00:00
feat: i18n
This commit is contained in:
@@ -3,4 +3,4 @@
|
||||
</script>
|
||||
|
||||
<h1>{$page.status}</h1>
|
||||
<pre>{$page.error.message}</pre>
|
||||
<pre>{$page.error?.message}</pre>
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
import "tippy.js/dist/tippy.css"
|
||||
import tippy from "tippy.js"
|
||||
import {theme, userPreferences} from "$lib/preferences.js"
|
||||
import {setLocale} from "../i18n/i18n-svelte"
|
||||
import {loadLocale} from "../i18n/i18n-util.sync"
|
||||
import {detectLocale} from "../i18n/i18n-util"
|
||||
import type {Locales} from "../i18n/i18n-types"
|
||||
|
||||
const locale = ((browser && localStorage.getItem("locale")) as Locales) || detectLocale()
|
||||
loadLocale(locale)
|
||||
setLocale(locale)
|
||||
|
||||
if (browser) {
|
||||
tippy.setDefaultProps({
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
<script lang="ts">
|
||||
import {getSharableUrl, parseCompressed, stringifyCompressed} from "$lib/serial/serialization"
|
||||
import {parseCompressed, stringifyCompressed} from "$lib/serial/serialization"
|
||||
import {chords, layout} from "$lib/serial/connection"
|
||||
import {preference} from "$lib/preferences"
|
||||
import type {Chord} from "$lib/serial/chord"
|
||||
import type {CharaLayout} from "$lib/serialization/layout"
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
|
||||
interface Backup {
|
||||
isCharaBackup: string
|
||||
chords: Chord[]
|
||||
layout: CharaLayout
|
||||
}
|
||||
|
||||
async function downloadBackup() {
|
||||
const downloadUrl = URL.createObjectURL(
|
||||
@@ -22,7 +31,7 @@
|
||||
async function restoreBackup(event: InputEvent) {
|
||||
const input = (event.target as HTMLInputElement).files![0]
|
||||
if (!input) return
|
||||
const backup = await parseCompressed(input)
|
||||
const backup = await parseCompressed<Backup>(input)
|
||||
if (backup.isCharaBackup !== "v1.0") throw new Error("Invalid Backup")
|
||||
if (backup.chords) {
|
||||
$chords = backup.chords
|
||||
@@ -34,14 +43,17 @@
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<h2><label><input type="checkbox" use:preference={"backup"} />Local Backup</label></h2>
|
||||
<h2><label><input type="checkbox" use:preference={"backup"} />{$LL.backup.TITLE()}</label></h2>
|
||||
<p class="disclaimer">
|
||||
<i>Backups remain on your computer and are never shared or uploaded to our servers.</i>
|
||||
<i>{$LL.backup.DISCLAIMER()}</i>
|
||||
</p>
|
||||
<div class="save">
|
||||
<button class="primary" on:click={downloadBackup}><span class="icon">save</span> Download Backup</button>
|
||||
<button class="primary" on:click={downloadBackup}
|
||||
><span class="icon">save</span>{$LL.backup.DOWNLOAD()}</button
|
||||
>
|
||||
<label class="button"
|
||||
><input on:input={restoreBackup} type="file" /><span class="icon">settings_backup_restore</span> Restore</label
|
||||
><input on:input={restoreBackup} type="file" /><span class="icon">settings_backup_restore</span
|
||||
>{$LL.backup.RESTORE()}</label
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
<script>
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
</script>
|
||||
|
||||
<dialog open>
|
||||
<h1>Warning</h1>
|
||||
<h1>{$LL.browserWarning.TITLE()}</h1>
|
||||
<p>
|
||||
Your current browser is not supported due to this site's unique requirement for <a
|
||||
{$LL.browserWarning.INFO_SERIAL_PREFIX()}<a
|
||||
class="normal"
|
||||
target="_blank"
|
||||
href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility"
|
||||
>serial connections</a
|
||||
>. Though all <b>chromium-based desktop</b> browsers fulfill this requirement, some derivations such as
|
||||
Brave
|
||||
>{$LL.browserWarning.INFO_SERIAL_INFIX()}</a
|
||||
>{$LL.browserWarning.INFO_SERIAL_SUFFIX()}
|
||||
{$LL.browserWarning.INFO_BROWSER_PREFIX()}
|
||||
<a href="https://github.com/brave/brave-browser/issues/13902" target="_blank"
|
||||
>have been known to disable the API intentionally</a
|
||||
>.
|
||||
>{$LL.browserWarning.INFO_BROWSER_INFIX()}</a
|
||||
>{$LL.browserWarning.INFO_BROWSER_SUFFIX()}
|
||||
</p>
|
||||
<div>
|
||||
<a href="https://www.chromium.org/getting-involved/download-chromium/" target="_blank" class="chrome"
|
||||
>Download Chromium</a
|
||||
>{$LL.browserWarning.DOWNLOAD_CHROMIUM()}</a
|
||||
>
|
||||
</div>
|
||||
<h5>Other popular options include</h5>
|
||||
<h5>{$LL.browserWarning.OTHER_OPTIONS()}</h5>
|
||||
<div>
|
||||
<a href="https://www.google.com/chrome/" target="_blank" class="chrome">Chrome</a>
|
||||
<a href="https://www.microsoft.com/en-us/edge/download?form=MA13FJ" target="_blank" class="edge"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import {browser} from "$app/environment"
|
||||
import {slide, fade} from "svelte/transition"
|
||||
import {preference} from "$lib/preferences"
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
|
||||
let terminal = false
|
||||
let powerDialog = false
|
||||
@@ -10,8 +11,8 @@
|
||||
|
||||
<section>
|
||||
<div class="row">
|
||||
<h2>Device</h2>
|
||||
<label>Auto-Connect<input type="checkbox" use:preference={"autoConnect"} /></label>
|
||||
<h2>{$LL.deviceManager.TITLE()}</h2>
|
||||
<label>{$LL.deviceManager.AUTO_CONNECT()}<input type="checkbox" use:preference={"autoConnect"} /></label>
|
||||
</div>
|
||||
|
||||
{#if $serialPort}
|
||||
@@ -32,22 +33,24 @@
|
||||
on:click={() => {
|
||||
$serialPort.forget()
|
||||
$serialPort = undefined
|
||||
}}><span class="icon">usb_off</span>Disconnect</button
|
||||
}}><span class="icon">usb_off</span>{$LL.deviceManager.DISCONNECT()}</button
|
||||
>
|
||||
{:else}
|
||||
<button class="error" on:click={() => initSerial(true)}><span class="icon">usb</span>Connect</button>
|
||||
<button class="error" on:click={() => initSerial(true)}
|
||||
><span class="icon">usb</span>{$LL.deviceManager.CONNECT()}</button
|
||||
>
|
||||
{/if}
|
||||
<div class="row" style="justify-content: flex-end">
|
||||
<a
|
||||
href="/terminal"
|
||||
title="Terminal"
|
||||
title={$LL.deviceManager.TERMINAL()}
|
||||
class="icon"
|
||||
disabled={$serialPort === undefined}
|
||||
on:click={() => (terminal = !terminal)}>terminal</a
|
||||
>
|
||||
<button
|
||||
class="icon"
|
||||
title="Boot Menu"
|
||||
title={$LL.deviceManager.bootMenu.TITLE()}
|
||||
disabled={$serialPort === undefined}
|
||||
on:click={() => (powerDialog = !powerDialog)}>settings_power</button
|
||||
>
|
||||
@@ -56,18 +59,18 @@
|
||||
{#if powerDialog}
|
||||
<div class="backdrop" transition:fade={{duration: 250}} on:click={() => (powerDialog = !powerDialog)} />
|
||||
<dialog open transition:slide={{duration: 250}}>
|
||||
<h3>Boot Menu</h3>
|
||||
<h3>{$LL.deviceManager.bootMenu.TITLE()}</h3>
|
||||
<button
|
||||
on:click={() => {
|
||||
$serialPort.reboot()
|
||||
$serialPort = undefined
|
||||
}}><span class="icon">restart_alt</span>Reboot</button
|
||||
}}><span class="icon">restart_alt</span>{$LL.deviceManager.bootMenu.REBOOT()}</button
|
||||
>
|
||||
<button
|
||||
on:click={() => {
|
||||
$serialPort.bootloader()
|
||||
$serialPort = undefined
|
||||
}}><span class="icon">rule_settings</span>Bootloader</button
|
||||
}}><span class="icon">rule_settings</span>{$LL.deviceManager.bootMenu.BOOTLOADER()}</button
|
||||
>
|
||||
</dialog>
|
||||
{/if}
|
||||
|
||||
18
src/routes/Languages.svelte
Normal file
18
src/routes/Languages.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import {locales} from "../i18n/i18n-util"
|
||||
import type {Locales} from "../i18n/i18n-types"
|
||||
import {loadLocaleAsync} from "../i18n/i18n-util.async"
|
||||
import {setLocale} from "../i18n/i18n-svelte"
|
||||
|
||||
async function applyLocale(locale: Locales) {
|
||||
localStorage.setItem("locale", locale)
|
||||
await loadLocaleAsync(locale)
|
||||
setLocale(locale)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ul>
|
||||
{#each locales as locale}
|
||||
<li><button on:click={() => applyLocale(locale)}>{locale}</button></li>
|
||||
{/each}
|
||||
</ul>
|
||||
@@ -10,7 +10,8 @@
|
||||
import {browser} from "$app/environment"
|
||||
import {userPreferences} from "$lib/preferences"
|
||||
import Theme from "./Theme.svelte"
|
||||
import {i} from "@inlang/sdk-js"
|
||||
import Languages from "./Languages.svelte"
|
||||
import LL from "../i18n/i18n-svelte"
|
||||
|
||||
const training = [
|
||||
{slug: "cpm", title: "CPM - Characters Per Minute", icon: "music_note"},
|
||||
@@ -29,7 +30,7 @@
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<a href="/" class="title">{i("title")}</a>
|
||||
<a href="/" class="title">{$LL.TITLE()}</a>
|
||||
|
||||
<div class="steps">
|
||||
{#each training as { slug, title, icon }}
|
||||
@@ -51,7 +52,7 @@
|
||||
<PwaStatus />
|
||||
{/await}
|
||||
{#if $serialPort}
|
||||
<button title="Backup & Restore" use:popup={BackupPopup} class="icon {$syncStatus}">
|
||||
<button title={$LL.backup.TITLE()} use:popup={BackupPopup} class="icon {$syncStatus}">
|
||||
{#if $syncStatus === "downloading"}
|
||||
backup
|
||||
{:else if $syncStatus === "uploading"}
|
||||
@@ -63,6 +64,7 @@
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
<button class="icon" use:popup={Languages}>translate</button>
|
||||
<button
|
||||
bind:this={connectButton}
|
||||
title="Devices"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script>
|
||||
import {page} from "$app/stores"
|
||||
import LL from "../../i18n/i18n-svelte"
|
||||
|
||||
const paths = [
|
||||
{href: "/config/chords/", title: "Chords", icon: "piano"},
|
||||
{href: "/config/layout/", title: "Layout", icon: "keyboard"},
|
||||
{href: "/config/settings/", title: "Settings", icon: "settings"},
|
||||
{href: "/config/chords/", title: $LL.configure.chords.TITLE(), icon: "piano"},
|
||||
{href: "/config/layout/", title: $LL.configure.layout.TITLE(), icon: "keyboard"},
|
||||
{href: "/config/settings/", title: $LL.configure.settings.TITLE(), icon: "settings"},
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {chords, serialPort} from "$lib/serial/connection"
|
||||
import {chords} from "$lib/serial/connection"
|
||||
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
|
||||
import FlexSearch from "flexsearch"
|
||||
import type {Index} from "flexsearch"
|
||||
@@ -7,6 +7,8 @@
|
||||
import type {Chord} from "$lib/serial/chord"
|
||||
import tippy from "tippy.js"
|
||||
import {calculateChordCoverage} from "$lib/chords/coverage"
|
||||
import type {MouseEventHandler} from "svelte/elements"
|
||||
import LL from "../../../i18n/i18n-svelte"
|
||||
|
||||
$: searchIndex = $chords?.length > 0 ? buildIndex($chords) : undefined
|
||||
|
||||
@@ -28,11 +30,11 @@
|
||||
})
|
||||
}
|
||||
|
||||
function sort(event: InputEvent) {
|
||||
const sort: MouseEventHandler<HTMLButtonElement> = function (event) {
|
||||
tippy(event.target, {})
|
||||
}
|
||||
|
||||
$: items = searchFilter?.map(it => [$chords[it], it]) ?? $chords.map((it, i) => [it, i])
|
||||
$: items = searchFilter?.map(it => [$chords[it], it] as const) ?? $chords.map((it, i) => [it, i] as const)
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -40,7 +42,11 @@
|
||||
</svelte:head>
|
||||
|
||||
{#if searchIndex}
|
||||
<input on:input={search} type="search" placeholder="Search {$chords.length} chords" />
|
||||
<input
|
||||
on:input={search}
|
||||
type="search"
|
||||
placeholder={$LL.configure.chords.search.PLACEHOLDER($chords.length)}
|
||||
/>
|
||||
{/if}
|
||||
<button class="icon" on:click={sort}>sort</button>
|
||||
<button class="icon">filter_list</button>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/* This file was created by inlang.
|
||||
It is needed in order to circumvent a current limitation of SvelteKit. See https://github.com/inlang/inlang/issues/647
|
||||
You can remove this comment and modify the file as you like. We just need to make sure it exists.
|
||||
Please do not delete it (inlang will recreate it if needed). */
|
||||
Reference in New Issue
Block a user