feat: i18n

This commit is contained in:
2023-07-26 23:41:13 +02:00
parent 6b09cbfbec
commit 88c7f057c9
24 changed files with 473 additions and 309 deletions

View File

@@ -3,4 +3,4 @@
</script>
<h1>{$page.status}</h1>
<pre>{$page.error.message}</pre>
<pre>{$page.error?.message}</pre>

View File

@@ -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({

View File

@@ -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>

View File

@@ -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"

View File

@@ -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}

View 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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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). */