aboutsummaryrefslogtreecommitdiff
path: root/src/routes
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-05-24 13:22:34 +0000
committerFuwn <[email protected]>2026-05-24 13:22:34 +0000
commit56a7a7851b09cb30a5cd543c8cb4f926109b4290 (patch)
treea620f908405fa48fd601580c5a48432831ec5c33 /src/routes
parentfix(layout): preserve list panel when clicking action buttons in summary (diff)
downloaddue.moe-56a7a7851b09cb30a5cd543c8cb4f926109b4290.tar.xz
due.moe-56a7a7851b09cb30a5cd543c8cb4f926109b4290.zip
refactor(locale): move hardcoded UI strings into english locale
Adds optional namespaces (common, errors, commandPalette, headTitle, notifications, schedule, events, home, reader, routes, badgePreview, badgeWall) and extends existing ones (settings.*, lists.*, tools.*, user.*, hololive.*) on the Locale interface. New fields are optional so japanese.ts can omit them; svelte-i18n's fallbackLocale handles the runtime miss. HeadTitle gains an optional routeKey prop for type-safe lookup. defaultActions becomes a factory so the command palette re-reads locale on language toggle. The existing JP feedback translation in routes/settings is preserved via japanese.ts. Out of scope (kept hardcoded): service-worker.ts, app.html, Landing*.svelte, tools.ts registry, Easter Event 2025 pages.
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/+error.svelte5
-rw-r--r--src/routes/+layout.svelte10
-rw-r--r--src/routes/completed/+page.svelte4
-rw-r--r--src/routes/events/+page.svelte7
-rw-r--r--src/routes/events/group/[group]/+page.svelte23
-rw-r--r--src/routes/events/groups/+page.svelte11
-rw-r--r--src/routes/girls/+page.svelte13
-rw-r--r--src/routes/girls/[language]/+page.svelte5
-rw-r--r--src/routes/hololive/[[stream]]/+page.svelte14
-rw-r--r--src/routes/reader/+page.svelte16
-rw-r--r--src/routes/schedule/+page.svelte9
-rw-r--r--src/routes/settings/+page.svelte30
-rw-r--r--src/routes/tools/+page.svelte7
-rw-r--r--src/routes/tools/[tool]/+page.svelte7
-rw-r--r--src/routes/updates/+page.svelte11
-rw-r--r--src/routes/user/+page.svelte2
-rw-r--r--src/routes/user/[user]/+page.svelte45
-rw-r--r--src/routes/user/[user]/badges/+page.svelte103
18 files changed, 173 insertions, 149 deletions
diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte
index 71482ffb..a67cdbbf 100644
--- a/src/routes/+error.svelte
+++ b/src/routes/+error.svelte
@@ -2,6 +2,7 @@
import { page } from "$app/stores";
import { closest } from "$lib/Error/path";
import Popup from "$lib/Layout/Popup.svelte";
+import locale from "$stores/locale";
$: suggestion = closest($page.url.pathname.replace("/", ""), [
"birthdays",
@@ -18,11 +19,11 @@ $: suggestion = closest($page.url.pathname.replace("/", ""), [
<Popup>
<p style="text-align: center;">
- <a href={$page.url.pathname}>{$page.url.pathname}</a> not found
+ <a href={$page.url.pathname}>{$page.url.pathname}</a> {$locale().errors?.routeNotFound}
</p>
<blockquote style="margin: 0 0 0 1.5rem;">
- Did you mean "<a
+ {$locale().errors?.didYouMean} "<a
href={suggestion}
style={suggestion === '...' ? 'pointer-events: none; color: inherit;' : ''}
>{suggestion.charAt(0).toUpperCase() + suggestion.slice(1)}</a
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 71d74adb..b1f9fc87 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -325,7 +325,7 @@ $: {
<CommandPalette
items={[
- ...defaultActions,
+ ...defaultActions(),
...toolsAsCommandPaletteActions(),
...authActions(data.user),
...syncActions($userIdentity.id, $settings.settingsSync),
@@ -356,7 +356,7 @@ $: {
<button
type="button"
class="menu-toggle"
- aria-label="Menu"
+ aria-label={$locale().navigation.menu}
aria-expanded={isMenuOpen}
aria-controls="primary-nav"
onclick={() => (isMenuOpen = !isMenuOpen)}
@@ -434,7 +434,11 @@ $: {
</a>
{:else if data.user}
<a href={root(`/user/${$userIdentity.name}`)} class="header-item">
- <img class="avatar" src={$userIdentity.avatar} alt="Avatar" />
+ <img
+ class="avatar"
+ src={$userIdentity.avatar}
+ alt={$locale().navigation.avatar}
+ />
</a>
{/if}
</div>
diff --git a/src/routes/completed/+page.svelte b/src/routes/completed/+page.svelte
index bcad912b..3720bf60 100644
--- a/src/routes/completed/+page.svelte
+++ b/src/routes/completed/+page.svelte
@@ -64,14 +64,14 @@ onMount(async () => {
onDestroy(() => removeHeightObserver?.());
</script>
-<HeadTitle route="Completed" path="/completed" />
+<HeadTitle routeKey="completed" path="/completed" />
{#if LastActivityComponent}
<LastActivityComponent user={data.user} />
{/if}
{#if data.user === undefined}
- <div class="card">Please log in to view completed media.</div>
+ <div class="card">{$locale().errors?.completedLoginPrompt}</div>
<Spacer />
diff --git a/src/routes/events/+page.svelte b/src/routes/events/+page.svelte
index 88a3da9e..0474852b 100644
--- a/src/routes/events/+page.svelte
+++ b/src/routes/events/+page.svelte
@@ -4,13 +4,14 @@ import Event from "$lib/Events/Event.svelte";
import Message from "$lib/Loading/Message.svelte";
import root from "$lib/Utility/root";
+import locale from "$stores/locale";
</script>
{#await fetch(root(`/api/events`))}
- <Message message="Loading events ..." />
+ <Message message={$locale().events?.loadingEvents} />
{:then eventsResponse}
{#await eventsResponse.json()}
- <Message message="Parsing events ..." />
+ <Message message={$locale().events?.parsingEvents} />
{:then events}
{#if events}
{#each events as rawEvent, i}
@@ -22,6 +23,6 @@ import root from "$lib/Utility/root";
{/each}
{/if}
{:catch}
- <Message message="" loader="ripple" slot withReload>Error parsing events.</Message>
+ <Message message="" loader="ripple" slot withReload>{$locale().events?.errorParsingEvents}</Message>
{/await}
{/await}
diff --git a/src/routes/events/group/[group]/+page.svelte b/src/routes/events/group/[group]/+page.svelte
index c02c1b51..34db0fae 100644
--- a/src/routes/events/group/[group]/+page.svelte
+++ b/src/routes/events/group/[group]/+page.svelte
@@ -7,6 +7,7 @@ import root from "$lib/Utility/root";
import { onMount } from "svelte";
import Group from "$lib/Events/Group.svelte";
import Event from "$lib/Events/Event.svelte";
+import locale from "$stores/locale";
import type { PageData } from "./$types";
export let data: PageData;
@@ -23,16 +24,16 @@ const asEvent = (event: unknown) => event as EventType;
</script>
{#await groupsResponse}
- <Message message="Loading group ..." />
+ <Message message={$locale().events?.loadingGroup} />
{:then group}
{#if group}
{#await group.json()}
- <Message message="Parsing group ..." />
+ <Message message={$locale().events?.parsingGroup} />
{:then json}
{#if json === null}
<Message message="" loader="ripple" slot>
- This group may not exist. Please
- <a href={'#'} onclick={() => location.reload()}>try again</a> later.
+ {$locale().events?.groupNotExistPrefix}
+ <a href={'#'} onclick={() => location.reload()}>{$locale().common?.tryAgain}</a>{$locale().events?.groupNotExistSuffix}
</Message>
{:else}
{@const group = asGroup(json)}
@@ -42,13 +43,13 @@ const asEvent = (event: unknown) => event as EventType;
<Spacer />
<details open>
- <summary>Events</summary>
+ <summary>{$locale().events?.summary}</summary>
{#await fetch(root(`/api/events?group=${data.group}`))}
- <Message message="Loading events ..." />
+ <Message message={$locale().events?.loadingEvents} />
{:then eventsResponse}
{#await eventsResponse.json()}
- <Message message="Parsing events ..." />
+ <Message message={$locale().events?.parsingEvents} />
{:then events}
{#if events}
{#each events as rawEvent, i}
@@ -60,17 +61,17 @@ const asEvent = (event: unknown) => event as EventType;
{/each}
{/if}
{:catch}
- <Message message="" loader="ripple" slot withReload>Error parsing events.</Message>
+ <Message message="" loader="ripple" slot withReload>{$locale().events?.errorParsingEvents}</Message>
{/await}
{/await}
</details>
{/if}
{:catch}
- <Message message="" loader="ripple" slot withReload>Error parsing group.</Message>
+ <Message message="" loader="ripple" slot withReload>{$locale().events?.errorParsingGroup}</Message>
{/await}
{:else}
- <Message message="Parsing groups ..." />
+ <Message message={$locale().events?.parsingGroups} />
{/if}
{:catch}
- <Message message="" loader="ripple" slot withReload>Error loading group.</Message>
+ <Message message="" loader="ripple" slot withReload>{$locale().events?.errorLoadingGroup}</Message>
{/await}
diff --git a/src/routes/events/groups/+page.svelte b/src/routes/events/groups/+page.svelte
index b6181ad8..198c637b 100644
--- a/src/routes/events/groups/+page.svelte
+++ b/src/routes/events/groups/+page.svelte
@@ -5,6 +5,7 @@ import Message from "$lib/Loading/Message.svelte";
import root from "$lib/Utility/root";
import { onMount } from "svelte";
import Group from "$lib/Events/Group.svelte";
+import locale from "$stores/locale";
let groupsResponse: Promise<Response>;
@@ -16,11 +17,11 @@ const asGroup = (group: unknown) => group as GroupType;
</script>
{#await groupsResponse}
- <Message message="Loading groups ..." />
+ <Message message={$locale().events?.loadingGroups} />
{:then groups}
{#if groups}
{#await groups.json()}
- <Message message="Parsing groups ..." />
+ <Message message={$locale().events?.parsingGroups} />
{:then json}
{#each json as rawGroup, i}
{@const group = asGroup(rawGroup)}
@@ -34,11 +35,11 @@ const asGroup = (group: unknown) => group as GroupType;
{/if}
{/each}
{:catch}
- <Message message="" loader="ripple" slot withReload>Error parsing groups.</Message>
+ <Message message="" loader="ripple" slot withReload>{$locale().events?.errorParsingGroups}</Message>
{/await}
{:else}
- <Message message="Parsing groups ..." />
+ <Message message={$locale().events?.parsingGroups} />
{/if}
{:catch}
- <Message message="" loader="ripple" slot withReload>Error loading groups.</Message>
+ <Message message="" loader="ripple" slot withReload>{$locale().events?.errorLoadingGroups}</Message>
{/await}
diff --git a/src/routes/girls/+page.svelte b/src/routes/girls/+page.svelte
index bbe57ac9..e45dad4d 100644
--- a/src/routes/girls/+page.svelte
+++ b/src/routes/girls/+page.svelte
@@ -5,28 +5,29 @@ import HeadTitle from "$lib/Home/HeadTitle.svelte";
import Message from "$lib/Loading/Message.svelte";
import Skeleton from "$lib/Loading/Skeleton.svelte";
import root from "$lib/Utility/root";
+import locale from "$stores/locale";
import "$styles/girls.scss";
</script>
-<HeadTitle route="Anime Girls Holding Programming Books" path="/girls" />
+<HeadTitle routeKey="girls" path="/girls" />
<div class="card">
<div class="split">
<div>
{#await Senpy.getRandomImage()}
- <Message message="Loading image ..." />
+ <Message message={$locale().routes?.girlsLoadingImage} />
<Skeleton grid={true} count={1} width="49%" height="16.25em" />
{:then randomImage}
<div class="preview">
<a href={randomImage.image} target="_blank">
- <img src={randomImage.image} alt="A random anime girl holding a programming book" />
+ <img src={randomImage.image} alt={$locale().routes?.girlsRandomAlt} />
</a>
</div>
{/await}
</div>
<div>
- The Senpy Club <span class="opaque">|</span> Anime Girls Holding Programming Books
+ {$locale().routes?.girlsIntroLeft} <span class="opaque">|</span> {$locale().routes?.girlsIntroRight}
<Spacer />
@@ -69,10 +70,10 @@ import "$styles/girls.scss";
<Spacer />
<details class="languages" open>
- <summary>Languages</summary>
+ <summary>{$locale().routes?.girlsLanguages}</summary>
{#await Senpy.getLanguages()}
- <Message message="Loading languages ..." />
+ <Message message={$locale().routes?.girlsLoadingLanguages} />
<Skeleton
card={false}
diff --git a/src/routes/girls/[language]/+page.svelte b/src/routes/girls/[language]/+page.svelte
index 2a97605f..4bef7f29 100644
--- a/src/routes/girls/[language]/+page.svelte
+++ b/src/routes/girls/[language]/+page.svelte
@@ -2,6 +2,7 @@
import Senpy from "$lib/Data/senpy";
import Message from "$lib/Loading/Message.svelte";
import Skeleton from "$lib/Loading/Skeleton.svelte";
+import locale from "$stores/locale";
import "$styles/girls.scss";
import type { PageData } from "./$types";
@@ -10,7 +11,7 @@ export let data: PageData;
<div class="card">
{#await Senpy.getImages(data.language)}
- <Message message="Loading images ..." />
+ <Message message={$locale().routes?.girlsLoadingImages} />
<Skeleton grid={true} count={1} width="49%" height="16.25em" />
{:then images}
@@ -18,7 +19,7 @@ export let data: PageData;
{#each images as image}
<a href={image} target="_blank">
<div class="preview">
- <img src={image} alt="An anime girl holding a programming book" />
+ <img src={image} alt={$locale().routes?.girlsSingleAlt} />
</div>
</a>
{/each}
diff --git a/src/routes/hololive/[[stream]]/+page.svelte b/src/routes/hololive/[[stream]]/+page.svelte
index 573c16aa..250ce22a 100644
--- a/src/routes/hololive/[[stream]]/+page.svelte
+++ b/src/routes/hololive/[[stream]]/+page.svelte
@@ -49,16 +49,16 @@ const getPinnedStreams = () => {
};
</script>
-<HeadTitle route="hololive Schedule" path="/hololive" />
+<HeadTitle routeKey="hololiveSchedule" path="/hololive" />
{#await schedulePromise}
- <Message message="Loading schedule ..." />
+ <Message message={$locale().hololive.loadingSchedule} />
<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{:then scheduleResponse}
{#if scheduleResponse}
{#await scheduleResponse.text()}
- <Message message="Parsing schedule ..." />
+ <Message message={$locale().hololive.parsingSchedule} />
<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{:then untypedSchedule}
@@ -68,17 +68,17 @@ const getPinnedStreams = () => {
{:catch}
<Message loader="ripple" slot>
{$locale().hololive.parseError}
- <a href={'#'} onclick={() => location.reload()}>Try again?</a>
+ <a href={'#'} onclick={() => location.reload()}>{$locale().hololive.tryAgainQuestion}</a>
</Message>
{/await}
{:else}
- <Message message="Loading schedule ..." />
+ <Message message={$locale().hololive.loadingSchedule} />
<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{/if}
{:catch}
<Message loader="ripple" slot>
- {$locale().hololive.loadError} Please
- <a href={'#'} onclick={() => location.reload()}>try again</a> later.
+ {$locale().hololive.loadError} {$locale().hololive.pleasePrefix}
+ <a href={'#'} onclick={() => location.reload()}>{$locale().common?.tryAgain}</a> {$locale().hololive.laterSuffix}
</Message>
{/await}
diff --git a/src/routes/reader/+page.svelte b/src/routes/reader/+page.svelte
index b6cce066..f279a58e 100644
--- a/src/routes/reader/+page.svelte
+++ b/src/routes/reader/+page.svelte
@@ -10,16 +10,22 @@ import {
Resource,
} from "$lib/Reader/resource";
import InputTemplate from "$lib/Tools/InputTemplate.svelte";
+import locale from "$stores/locale";
let submission = "";
$: resourceIdentity = identify(submission);
</script>
-<InputTemplate field="Manga URL" bind:submission submitText="Read" preserveCase>
+<InputTemplate
+ field={$locale().reader?.mangaUrl ?? 'Manga URL'}
+ bind:submission
+ submitText={$locale().reader?.read ?? 'Read'}
+ preserveCase
+>
{#if resourceIdentity}
{#await fetchResource(submission)}
- <Message message="Loading chapters ..." />
+ <Message message={$locale().reader?.loadingChapters} />
{:then response}
{#if response.ok}
{#await decodeResource(response, submission) then data}
@@ -32,12 +38,12 @@ $: resourceIdentity = identify(submission);
<Notice>{error}</Notice>
{/await}
{:else}
- <Notice>Failed to fetch data</Notice>
+ <Notice>{$locale().reader?.fetchFailed}</Notice>
{/if}
{:catch}
- <Notice>An unknown error has occurred.</Notice>
+ <Notice>{$locale().reader?.unknownError}</Notice>
{/await}
{:else}
- <Notice>Invalid URL</Notice>
+ <Notice>{$locale().reader?.invalidUrl}</Notice>
{/if}
</InputTemplate>
diff --git a/src/routes/schedule/+page.svelte b/src/routes/schedule/+page.svelte
index 139f333e..9dcda20a 100644
--- a/src/routes/schedule/+page.svelte
+++ b/src/routes/schedule/+page.svelte
@@ -13,6 +13,7 @@ import Days from "$lib/Schedule/Days.svelte";
import Skeleton from "$lib/Loading/Skeleton.svelte";
import Message from "$lib/Loading/Message.svelte";
import subsPlease from "$stores/subsPlease";
+import locale from "$stores/locale";
import type { PageData } from "./$types";
export let data: PageData;
@@ -33,7 +34,7 @@ onMount(async () => {
});
</script>
-<HeadTitle route="Schedule" path="/schedule" />
+<HeadTitle routeKey="schedule" path="/schedule" />
<!-- <blockquote>
<select
@@ -62,12 +63,12 @@ onMount(async () => {
<Spacer /> -->
{#if !$subsPlease}
- <Message message="Loading subtitle schedule ..." />
+ <Message message={$locale().schedule?.loadingSubtitle} />
<Skeleton grid={true} count={7} height="15em" width="49.5%" />
{:else}
{#await scheduledMediaPromise}
- <Message message="Loading schedule ..." />
+ <Message message={$locale().schedule?.loadingSchedule} />
<Skeleton grid={true} count={7} height="15em" width="49.5%" />
{:then scheduledMedia}
@@ -76,7 +77,7 @@ onMount(async () => {
<Days subsPlease={$subsPlease} {scheduledMedia} {forceListMode} user={data.user} />
</div>
{:else}
- <Message message="Loading schedule ..." />
+ <Message message={$locale().schedule?.loadingSchedule} />
<Skeleton grid={true} count={7} height="15em" width="49.5%" />
{/if}
diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte
index 054a126b..f9a2ee87 100644
--- a/src/routes/settings/+page.svelte
+++ b/src/routes/settings/+page.svelte
@@ -10,7 +10,6 @@ import Cache from "$lib/Settings/Categories/Cache.svelte";
import Category from "$lib/Settings/Category.svelte";
import tooltip from "$lib/Tooltip/tooltip";
import locale from "$stores/locale.js";
-import settings from "$stores/settings";
import LogInRestricted from "$lib/Error/LogInRestricted.svelte";
import SettingSync from "$lib/Settings/Categories/SettingSync.svelte";
import RssFeeds from "$lib/Settings/Categories/RSSFeeds.svelte";
@@ -28,28 +27,17 @@ export let data: PageData;
// };
</script>
-<HeadTitle route="Settings" path="/settings" />
+<HeadTitle routeKey="settings" path="/settings" />
<blockquote>
- {#if $settings.displayLanguage == 'en'}
- Have feedback or suggestions? Send a private message to
- <a
- href="https://anilist.co/user/fuwn"
- target="_blank"
- title={$locale().settings.tooltips.author}
- use:tooltip>@fuwn</a
- >
- on AniList!
- {:else if $settings.displayLanguage == 'ja'}
- フィードバックや提案はありますか?AniListで
- <a
- href="https://anilist.co/user/fuwn"
- target="_blank"
- title={$locale().settings.tooltips.author}
- use:tooltip>@fuwn</a
- >
- にDMを送ってください!
- {/if}
+ {$locale().routes?.settingsFeedbackPrefix}
+ <a
+ href="https://anilist.co/user/fuwn"
+ target="_blank"
+ title={$locale().settings.tooltips.author}
+ use:tooltip>@fuwn</a
+ >
+ {$locale().routes?.settingsFeedbackSuffix}
<!-- <Spacer />
diff --git a/src/routes/tools/+page.svelte b/src/routes/tools/+page.svelte
index 572f4fc5..3589fa9f 100644
--- a/src/routes/tools/+page.svelte
+++ b/src/routes/tools/+page.svelte
@@ -4,6 +4,7 @@ import HeadTitle from "$lib/Home/HeadTitle.svelte";
import Picker from "$lib/Tools/Picker.svelte";
import { tools } from "$lib/Tools/tools.js";
import root from "$lib/Utility/root";
+import locale from "$stores/locale";
let tool = "default";
</script>
@@ -36,9 +37,9 @@ let tool = "default";
<Spacer />
<blockquote style="margin: 0 0 0 1.5rem;">
- Have any requests for cool tools that you think others might find useful? Send a private message
- to
- <a href="https://anilist.co/user/fuwn" target="_blank" rel="noopener">@fuwn</a> on AniList!
+ {$locale().routes?.toolsFeedbackPrefix}
+ <a href="https://anilist.co/user/fuwn" target="_blank" rel="noopener">@fuwn</a>
+ {$locale().routes?.toolsFeedbackSuffix}
</blockquote>
</div>
diff --git a/src/routes/tools/[tool]/+page.svelte b/src/routes/tools/[tool]/+page.svelte
index ff764add..b74952a5 100644
--- a/src/routes/tools/[tool]/+page.svelte
+++ b/src/routes/tools/[tool]/+page.svelte
@@ -20,6 +20,7 @@ import Popup from "$lib/Layout/Popup.svelte";
import SequelCatcher from "$lib/Tools/SequelCatcher/Tool.svelte";
import Tracker from "$lib/Tools/Tracker/Tool.svelte";
import BirthdaysTemplate from "$lib/Tools/BirthdaysTemplate.svelte";
+import locale from "$stores/locale";
import type { PageData } from "./$types";
export let data: PageData;
@@ -38,15 +39,15 @@ $: if (tool === "girls") goto(root("/girls"));
<Picker bind:tool />
{#if !Object.keys(tools).includes(tool)}
- <HeadTitle route="Tools" path="/tools" />
+ <HeadTitle routeKey="tools" path="/tools" />
<Popup>
<p style="text-align: center;">
- Tool "<a href={root(`/tools/${tool}`)}>{tool}</a>" not found
+ {$locale().errors?.toolNotFoundPrefix}<a href={root(`/tools/${tool}`)}>{tool}</a>{$locale().errors?.toolNotFoundSuffix}
</p>
<blockquote style="margin: 0 0 0 1.5rem;">
- Did you mean "<a
+ {$locale().errors?.didYouMean} "<a
href={root(`/tools/${tools[suggestion].id}`)}
onclick={() => (tool = suggestion)}
style={suggestion === '...' ? 'pointer-events: none; color: inherit;' : ''}
diff --git a/src/routes/updates/+page.svelte b/src/routes/updates/+page.svelte
index 7dc628ca..71a6d2e3 100644
--- a/src/routes/updates/+page.svelte
+++ b/src/routes/updates/+page.svelte
@@ -5,6 +5,7 @@ import Skeleton from "$lib/Loading/Skeleton.svelte";
import { createHeightObserver } from "$lib/Utility/html";
import root from "$lib/Utility/root";
import { onDestroy, onMount } from "svelte";
+import locale from "$stores/locale";
let feed:
| { items: { title: string; link: string; content: string }[] }
@@ -64,17 +65,17 @@ const chapterTitle = (title: string) =>
title.replace(/^(.*?) (Vol\.|Ch\.|\bOneshot\b)/, "$2");
</script>
-<HeadTitle route="Updates" path="/updates" />
+<HeadTitle routeKey="updates" path="/updates" />
<div class="list-container">
<details open class="list">
<summary>
- Manga
+ {$locale().routes?.updatesManga}
<small class="opaque">{mangaEndTime ? mangaEndTime / 1000 : '...'}s</small>
</summary>
{#if feed === null}
- Failed to load feed
+ {$locale().routes?.updatesFailedToLoad}
{:else if feed !== undefined}
<ul>
{#each feed.items as item}
@@ -103,12 +104,12 @@ const chapterTitle = (title: string) =>
<details open class="list">
<summary>
- Novels
+ {$locale().routes?.updatesNovels}
<small class="opaque">{novelEndTime ? novelEndTime / 1000 : '...'}s</small>
</summary>
{#if novelFeed === null}
- Failed to load feed
+ {$locale().routes?.updatesFailedToLoad}
{:else if novelFeed !== undefined}
<ul>
{#each novelFeed.data.items as item}
diff --git a/src/routes/user/+page.svelte b/src/routes/user/+page.svelte
index 20a8d390..3fdf1dc6 100644
--- a/src/routes/user/+page.svelte
+++ b/src/routes/user/+page.svelte
@@ -29,4 +29,4 @@ onMount(async () => {
});
</script>
-<HeadTitle route="Profile" path="/user" />
+<HeadTitle routeKey="profile" path="/user" />
diff --git a/src/routes/user/[user]/+page.svelte b/src/routes/user/[user]/+page.svelte
index 1bcedc52..bfcdd87b 100644
--- a/src/routes/user/[user]/+page.svelte
+++ b/src/routes/user/[user]/+page.svelte
@@ -237,27 +237,29 @@ const toggleCategory = () => {
// 8.5827814569536423841e0
</script>
-<HeadTitle route={`${data.username}'s Profile`} path={`/user/${data.username}`} />
+<HeadTitle
+ route={$locale({ values: { username: data.username } }).headTitle?.userProfile}
+ path={`/user/${data.username}`}
+/>
{#if error}
<AnimeRateLimited>
- <a href={`https://anilist.co/user/${data.username}`} target="_blank">@{data.username}</a>'s
- profile could not be loaded.
+ <a href={`https://anilist.co/user/${data.username}`} target="_blank">@{data.username}</a>{$locale().errors?.profileCouldNotBeLoaded?.split('@{username}')[1]}
</AnimeRateLimited>
{:else}
{#if userData === null}
<Message slot withReload>
<p>
- Could not load user profile for <a
+ {$locale().user.profile.notLoaded?.split('@{username}')[0]}<a
href={`https://anilist.co/user/${data.username}`}
target="_blank">@{data.username}</a
- >.
+ >{$locale().user.profile.notLoaded?.split('@{username}')[1]}
</p>
</Message>
{:else if userData === undefined}
<Skeleton card={false} bigCard count={1} height="224px" />
- <Message message="Loading user profile ..." />
+ <Message message={$locale().user.profile.loadingProfile} />
{:else}
<div class="card card-small">
<div
@@ -297,10 +299,10 @@ const toggleCategory = () => {
</a>
{#if userData && authorisedUsers.includes(userData.id)}
&#8204;
- <button class="unclickable-button button-badge badge-rainbow">Owner</button>
+ <button class="unclickable-button button-badge badge-rainbow">{$locale().user.profile.owner}</button>
{/if}
<span class="click-item separator opaque">•</span>
- <a href={root(`/user/${userData.name}/badges`)}>Badge Wall</a>
+ <a href={root(`/user/${userData.name}/badges`)}>{$locale().user.profile.badgeWallLink}</a>
</p>
{#if preferences && preferences.biography && preferences.biography.length > 0}
@@ -362,7 +364,7 @@ const toggleCategory = () => {
>
<a href={root(`/hololive/${encodeURIComponent(stream)}`)}>
<div class="user-grid-hololive-badges">
- <ParallaxImage source={avatar} alternativeText="Avatar" />
+ <ParallaxImage source={avatar} alternativeText={$locale().navigation.avatar ?? 'Avatar'} />
</div>
</a>
</LinkedTooltip>
@@ -401,7 +403,7 @@ const toggleCategory = () => {
<Spacer />
- Pinned Categories
+ {$locale().user.profile.pinnedCategories}
<div class="pinned-categories">
{#each ownerPreferences.pinned_badge_wall_categories as category}
@@ -423,23 +425,28 @@ const toggleCategory = () => {
<button
onclick={() => {
if (userData) toggleCategoryQuery.mutate({ category }).then();
- }}>Remove</button
+ }}>{$locale().common?.remove}</button
>
</div>
{/each}
<span class="card card-small pinned-category">
<span class="pinned-category-name">
- <input type="text" id="category" placeholder="Category" style="width: 10em;" />
+ <input
+ type="text"
+ id="category"
+ placeholder={$locale().user.profile.categoryPlaceholder}
+ style="width: 10em;"
+ />
</span>
- <button class="button-lined" onclick={toggleCategory}>Add</button>
+ <button class="button-lined" onclick={toggleCategory}>{$locale().common?.add}</button>
</span>
</div>
<Spacer />
- Biography
+ {$locale().user.profile.biography}
<button
onclick={() => {
@@ -449,19 +456,19 @@ const toggleCategory = () => {
biography: getBiography()
})
.then();
- }}>Save</button
+ }}>{$locale().common?.save}</button
>
<textarea
value={ownerPreferences.biography}
rows="5"
cols="100"
id="biography"
- placeholder="Markdown supported!"
+ placeholder={$locale().user.profile.markdownPlaceholder}
></textarea>
<Spacer />
- Badge Wall Custom CSS
+ {$locale().user.profile.badgeWallCustomCss}
<button
onclick={() => {
@@ -471,14 +478,14 @@ const toggleCategory = () => {
css: getBadgeWallCSS()
})
.then();
- }}>Save</button
+ }}>{$locale().common?.save}</button
>
<textarea
value={ownerPreferences.badge_wall_css}
rows="10"
cols="100"
id="badgeWallCSS"
- placeholder="/* Use classes and IDs such as .badges, #badges, .badge, or standard elements like body and details, or anything, as long as it's valid CSS! */"
+ placeholder={$locale().user.profile.customCssPlaceholder}
></textarea>
</details>
{/if}
diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index 9ff81118..de55b456 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -12,6 +12,7 @@ import {
} from "$lib/Utility/time";
import proxy from "$lib/Utility/proxy";
import locale from "$stores/locale";
+import { get } from "svelte/store";
import Skeleton from "$lib/Loading/Skeleton.svelte";
import Message from "$lib/Loading/Message.svelte";
import Dropdown from "$lib/Layout/Dropdown.svelte";
@@ -209,7 +210,9 @@ type GroupedBadges = { [key: string]: IndexedBadge[] };
const setShadowHide = () => {
if (!badger) {
- loadError = "Something went wrong. Try refreshing.";
+ loadError =
+ get(locale)().badgeWall?.page?.somethingWentWrong ??
+ "Something went wrong. Try refreshing.";
return;
}
@@ -547,7 +550,9 @@ const shadowHideBadge = () => {
if (!selectedBadge && !authorised) return;
if (!badger) {
- loadError = "Something went wrong. Try refreshing.";
+ loadError =
+ get(locale)().badgeWall?.page?.somethingWentWrong ??
+ "Something went wrong. Try refreshing.";
return;
}
@@ -561,7 +566,10 @@ const shadowHideBadge = () => {
};
</script>
-<HeadTitle route={`${data.username}'s Badge Wall`} path={`/user/${data.username}`} />
+<HeadTitle
+ route={$locale({ values: { username: data.username } }).headTitle?.userBadgeWall}
+ path={`/user/${data.username}`}
+/>
{#if loadError}
<Popup fullscreen locked>
@@ -571,11 +579,11 @@ const shadowHideBadge = () => {
{@const isOwner = $identity && (isId ? $identity.id : $identity.name) === data.username}
{#if $BadgeWallUser.fetching || !$BadgeWallUser.data}
- <Message message="Loading badges ..." />
+ <Message message={$locale().badgeWall?.page?.loadingBadges} />
<Skeleton grid={true} count={100} width="150px" height="170px" />
{:else if !$BadgeWallUser.data.User}
- <Message message="No badges yet." />
+ <Message message={$locale().badgeWall?.page?.noBadgesYet} />
{:else}
{@const ungroupedBadges = castBadgesToIndexedBadges($BadgeWallUser.data.User.badges)}
{@const isBadgeSelected =
@@ -591,7 +599,7 @@ const shadowHideBadge = () => {
{/if}
{#if ungroupedBadges === null}
- <Message message="Loading badges ..." />
+ <Message message={$locale().badgeWall?.page?.loadingBadges} />
<Skeleton grid={true} count={10} width="150px" height="170px" />
{:else}
@@ -605,23 +613,17 @@ const shadowHideBadge = () => {
{#if shadowHidden}
<div class="card">
- <b>Notice:</b> The Badge Wall overseer system has detected badges containing
- AI-generated material on your wall. {shadowHiddenCount} of your badges have been shadow
- hidden.
+ <b>{$locale().badgeWall?.page?.notice}</b>
+ {$locale({ values: { count: shadowHiddenCount } }).badgeWall?.page?.shadowHideNotice1}
<Spacer />
- You may use the "Un-shadow Hide Badges" button to unhide these badges, from where you will
- be required to use the hide feature to hide these badges from the public, while allowing
- them to stay visible to you as the account holder.
+ {$locale().badgeWall?.page?.shadowHideNotice2}
</div>
{:else if false && !noticeDismissed}
<div class="card">
- <b>Notice:</b> AniList has begun purging outbound links which contain AI-generated
- material, this includes Badge Wall. If you have collected badges with AI-generated
- elements, kindly use the hide feature to hide these badges from the public, while
- allowing them to stay visible to you as the account holder.
+ <b>{$locale().badgeWall?.page?.notice}</b>
+ {$locale().badgeWall?.page?.aiNotice1}
<Spacer />
- Failure to comply with this request at your earliest convenience will result in the hiding
- of all badges from your Badge Wall.
+ {$locale().badgeWall?.page?.aiNotice2}
<Spacer />
<button
onclick={async () => {
@@ -630,7 +632,7 @@ const shadowHideBadge = () => {
await localforage.setItem('badgeWallNoticeDismissed', 'true');
}}
>
- Dismiss
+ {$locale().badgeWall?.page?.dismiss}
</button>
</div>
{/if}
@@ -639,7 +641,7 @@ const shadowHideBadge = () => {
<div class="card">
{#if authorised}
- <button onclick={setShadowHide}>Shadow Hide Badges</button>
+ <button onclick={setShadowHide}>{$locale().badgeWall?.page?.shadowHide}</button>
{/if}
{#if isOwner && authorised}
@@ -675,7 +677,7 @@ const shadowHideBadge = () => {
migrateMode = !migrateMode;
}}
>
- Migrate Category
+ {$locale().badgeWall?.page?.migrateCategory}
</button>
<span style="margin: 0 0.625rem;">•</span>
<button
@@ -684,14 +686,14 @@ const shadowHideBadge = () => {
hideMode = !hideMode;
}}
>
- Hide Category
+ {$locale().badgeWall?.page?.hideCategory}
</button>
<!-- <!-- <span style="margin: 0 0.625rem;">•</span> -->
<!-- <button onclick={() => exportBadges(groupedBadges)}>Export Badges</button> -->
{#if shadowHidden}
<span style="margin: 0 0.625rem;">•</span>
- <button onclick={setShadowHide}>Un-shadow Hide Badges</button>
+ <button onclick={setShadowHide}>{$locale().badgeWall?.page?.unshadowHide}</button>
{/if}
{#if editMode && isOwner}
@@ -784,7 +786,7 @@ const shadowHideBadge = () => {
? dateToInputTime(databaseTimeToDate(selectedBadge.time))
: ''}
/>
- <small>Must be full date and time, defaults to now if any fields empty</small>
+ <small>{$locale().badgeWall?.page?.dateTimeHint}</small>
</span>
<Spacer />
@@ -827,13 +829,17 @@ const shadowHideBadge = () => {
</Dropdown>
<Dropdown
items={[false, true].map((hidden) => ({
- name: hidden ? 'Hidden' : 'Shown',
+ name: hidden
+ ? ($locale().badgeWall?.page?.hidden ?? 'Hidden')
+ : ($locale().badgeWall?.page?.shown ?? 'Shown'),
url: '#',
onClick: () => {
const hiddenInput = document.querySelector('input[name="hidden"]');
if (hiddenInput instanceof HTMLInputElement)
- hiddenInput.value = hidden ? 'Hidden' : 'Shown';
+ hiddenInput.value = hidden
+ ? ($locale().badgeWall?.page?.hidden ?? 'Hidden')
+ : ($locale().badgeWall?.page?.shown ?? 'Shown');
}
}))}
header={false}
@@ -842,16 +848,16 @@ const shadowHideBadge = () => {
<span slot="title">
<input
type="text"
- placeholder="Shown"
+ placeholder={$locale().badgeWall?.page?.shown}
name="hidden"
minlength="1"
maxlength="1000"
size="15"
value={selectedBadge
? selectedBadge.hidden
- ? 'Hidden'
- : 'Shown'
- : 'Shown'}
+ ? ($locale().badgeWall?.page?.hidden ?? 'Hidden')
+ : ($locale().badgeWall?.page?.shown ?? 'Shown')
+ : ($locale().badgeWall?.page?.shown ?? 'Shown')}
/>
</span>
</Dropdown>
@@ -879,9 +885,11 @@ const shadowHideBadge = () => {
{#if loadQueryParameter === 'none'}
<div class="card">
- <b>Notice:</b>
- {ungroupedBadges.length} badges have been loaded successfully, but they are not being displayed
- due to your preferences (<code>load=none</code>).
+ <b>{$locale().badgeWall?.page?.notice}</b>
+ {$locale({
+ values: { count: ungroupedBadges.length }
+ }).badgeWall?.page?.loadNoneNoticePrefix}<code>load=none</code>{$locale().badgeWall
+ ?.page?.loadNoneNoticeSuffix}
</div>
{:else}
<Badges
@@ -919,10 +927,12 @@ const shadowHideBadge = () => {
{#if authorised}
<button onclick={shadowHideBadge}>
{#if selectedBadge && selectedBadge.shadow_hidden}
- Un-shadow
+ {$locale({ values: { id: selectedBadge.id } }).badgeWall?.page?.unshadowHideBadge}
{:else}
- Shadow
- {/if} Hide Badge ({selectedBadge ? selectedBadge.id : 0})
+ {$locale({
+ values: { id: selectedBadge ? selectedBadge.id : 0 }
+ }).badgeWall?.page?.shadowHideBadge}
+ {/if}
</button>
{/if}
</Popup>
@@ -1026,13 +1036,13 @@ const shadowHideBadge = () => {
{/if}
<Popup fullscreen onLeave={() => (migrateMode = false)} show={migrateMode}>
- Migrate Category
+ {$locale().badgeWall?.page?.migrateCategory}
<Spacer />
<input
type="text"
- placeholder="Original Category"
+ placeholder={$locale().badgeWall?.page?.originalCategoryPlaceholder}
id="migrate_original"
minlength="1"
maxlength="1000"
@@ -1040,13 +1050,13 @@ const shadowHideBadge = () => {
/>
<input
type="text"
- placeholder="New Category"
+ placeholder={$locale().badgeWall?.page?.newCategoryPlaceholder}
id="migrate_new"
minlength="1"
maxlength="1000"
size="20"
/>
- <SettingHint lineBreak>Leave category empty to migrate all to or from uncategorised.</SettingHint>
+ <SettingHint lineBreak>{$locale().badgeWall?.page?.migrateAllHint}</SettingHint>
<Spacer />
@@ -1060,29 +1070,28 @@ const shadowHideBadge = () => {
{$locale().user.badges.importMode.cancel}
</button>
<button onclick={() => migrateCategory()} class="button-lined" style="float: right;">
- Migrate
+ {$locale().badgeWall?.page?.migrateAction}
</button>
</Popup>
<Popup fullscreen onLeave={() => (hideMode = false)} show={hideMode}>
- Hide Category
+ {$locale().badgeWall?.page?.hideCategory}
<SettingHint lineBreak>
- If the majority of the badges in a category are shown, the category will be hidden, and vice
- versa.
+ {$locale().badgeWall?.page?.hideVisibilityHint}
</SettingHint>
<Spacer />
<input
type="text"
- placeholder="Category"
+ placeholder={$locale().badgeWall?.page?.categoryPlaceholder}
id="category_hide"
minlength="1"
maxlength="1000"
size="20"
/>
- <SettingHint lineBreak>Leave category field empty to hide all.</SettingHint>
+ <SettingHint lineBreak>{$locale().badgeWall?.page?.hideAllHint}</SettingHint>
<Spacer />
@@ -1096,6 +1105,6 @@ const shadowHideBadge = () => {
{$locale().user.badges.importMode.cancel}
</button>
<button onclick={() => hideCategory()} class="button-lined" style="float: right;"
- >Toggle Visibility</button
+ >{$locale().badgeWall?.page?.toggleVisibility}</button
>
</Popup>