diff options
| author | Fuwn <[email protected]> | 2026-05-24 13:22:34 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-05-24 13:22:34 +0000 |
| commit | 56a7a7851b09cb30a5cd543c8cb4f926109b4290 (patch) | |
| tree | a620f908405fa48fd601580c5a48432831ec5c33 /src/routes | |
| parent | fix(layout): preserve list panel when clicking action buttons in summary (diff) | |
| download | due.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.svelte | 5 | ||||
| -rw-r--r-- | src/routes/+layout.svelte | 10 | ||||
| -rw-r--r-- | src/routes/completed/+page.svelte | 4 | ||||
| -rw-r--r-- | src/routes/events/+page.svelte | 7 | ||||
| -rw-r--r-- | src/routes/events/group/[group]/+page.svelte | 23 | ||||
| -rw-r--r-- | src/routes/events/groups/+page.svelte | 11 | ||||
| -rw-r--r-- | src/routes/girls/+page.svelte | 13 | ||||
| -rw-r--r-- | src/routes/girls/[language]/+page.svelte | 5 | ||||
| -rw-r--r-- | src/routes/hololive/[[stream]]/+page.svelte | 14 | ||||
| -rw-r--r-- | src/routes/reader/+page.svelte | 16 | ||||
| -rw-r--r-- | src/routes/schedule/+page.svelte | 9 | ||||
| -rw-r--r-- | src/routes/settings/+page.svelte | 30 | ||||
| -rw-r--r-- | src/routes/tools/+page.svelte | 7 | ||||
| -rw-r--r-- | src/routes/tools/[tool]/+page.svelte | 7 | ||||
| -rw-r--r-- | src/routes/updates/+page.svelte | 11 | ||||
| -rw-r--r-- | src/routes/user/+page.svelte | 2 | ||||
| -rw-r--r-- | src/routes/user/[user]/+page.svelte | 45 | ||||
| -rw-r--r-- | src/routes/user/[user]/badges/+page.svelte | 103 |
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)} ‌ - <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> |