feat: start working on internationalization
This commit is contained in:
parent
21660101af
commit
a9dda69fef
17 changed files with 466 additions and 60 deletions
20
src/App.tsx
20
src/App.tsx
|
@ -5,6 +5,7 @@ import "./app.css";
|
|||
import { ColorModeProvider, ColorModeScript, createLocalStorageManager } from "@kobalte/core";
|
||||
import { MetaProvider } from "@solidjs/meta";
|
||||
import { Portal } from "solid-js/web";
|
||||
import * as m from "~/paraglide/messages";
|
||||
import { Callout, CalloutContent, CalloutTitle } from "./components/ui/callout";
|
||||
import { ModeToggle } from "./components/ui/mode-toggle";
|
||||
import { dbLoaded } from "./db";
|
||||
|
@ -44,10 +45,9 @@ const App: Component = () => {
|
|||
<Portal>
|
||||
<div class="fixed inset-0 mx-4 flex flex-col items-center justify-center backdrop-blur-lg">
|
||||
<Callout variant="error">
|
||||
Your browser does not support WebAssembly, which is required for this site to work with the
|
||||
big amount of data a signal backup contains.
|
||||
{m.grassy_early_hawk_find()}
|
||||
<br />
|
||||
Please try a different browser.
|
||||
{m.simple_round_nuthatch_aim()}
|
||||
</Callout>
|
||||
</div>
|
||||
</Portal>
|
||||
|
@ -57,8 +57,7 @@ const App: Component = () => {
|
|||
fallback={
|
||||
<Show when={!dbLoaded() && hasCashedData()}>
|
||||
<Callout variant="default" class="m-4">
|
||||
There is currently no backup database loaded, but you can watch statistics that have been
|
||||
cached, meaning only chats you already opened or chats that were preloaded.
|
||||
{m.home_fun_peacock_prosper()}
|
||||
<br />
|
||||
<A
|
||||
href="/overview"
|
||||
|
@ -66,19 +65,18 @@ const App: Component = () => {
|
|||
umami.track("Watch cached statistics");
|
||||
}}
|
||||
>
|
||||
Watch cached statistics
|
||||
{m.every_tasty_toad_dream()}
|
||||
</A>
|
||||
</Callout>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<Callout variant="warning" class="m-4">
|
||||
<CalloutTitle>You are watching cached statistics</CalloutTitle>
|
||||
<CalloutTitle>{m.glad_fun_polecat_trim()}</CalloutTitle>
|
||||
<CalloutContent>
|
||||
Currently there is no backup database loaded. You can only watch statistics that have been
|
||||
cached, meaning only chats you already opened or chats that were preloaded.
|
||||
{m.curly_broad_insect_grow()}
|
||||
<br />
|
||||
<A href="/">Load a backup</A>
|
||||
<A href="/">{m.noble_muddy_elephant_lead()}</A>
|
||||
</CalloutContent>
|
||||
</Callout>
|
||||
</Show>
|
||||
|
@ -86,7 +84,7 @@ const App: Component = () => {
|
|||
<Suspense>{props.children}</Suspense>
|
||||
</main>
|
||||
<footer class="mt-4 flex flex-row justify-end bg-muted p-8">
|
||||
<A href="/privacy">Privacy policy</A>
|
||||
<A href="/privacy">{m.few_helpful_kestrel_swim()}</A>
|
||||
</footer>
|
||||
</ColorModeProvider>
|
||||
</>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Heading } from "~/components/ui/heading";
|
|||
import { SELF_ID, dmPartnerRecipientQuery, threadMostUsedWordsQuery, threadSentMessagesOverviewQuery } from "~/db";
|
||||
import { getNameFromRecipient } from "~/lib/get-name-from-recipient";
|
||||
import { createMessageStatsSources } from "~/lib/messages";
|
||||
import * as m from "~/paraglide/messages";
|
||||
import type { MessageOverview } from "~/types";
|
||||
import { DmMessagesPerDate } from "./dm-messages-per-date";
|
||||
import { DmMessagesPerDaytime } from "./dm-messages-per-daytime";
|
||||
|
@ -90,53 +91,57 @@ export const DmId: Component<RouteSectionProps> = (props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Title>Dm with {dmPartner()?.name}</Title>
|
||||
<Title>
|
||||
{m.petty_best_bobcat_affirm()} {dmPartner()?.name}
|
||||
</Title>
|
||||
<div class="flex flex-col items-center">
|
||||
<Heading level={1}>DM with {dmPartner()?.name}</Heading>
|
||||
<Heading level={2}>Chat timeline</Heading>
|
||||
<Heading level={1}>
|
||||
{m.petty_best_bobcat_affirm()} {dmPartner()?.name}
|
||||
</Heading>
|
||||
<Heading level={2}>{m.legal_inclusive_lionfish_zap()}</Heading>
|
||||
<Suspense
|
||||
fallback={
|
||||
<Flex alignItems="center" justifyContent="center" class="h-64">
|
||||
<p class="text-4xl">Loading...</p>
|
||||
<p class="text-4xl">{m.mealy_wacky_toucan_spark()}</p>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<DmMessagesPerDate dateStats={dmMessageStats()?.date} recipients={recipients()} />
|
||||
</Suspense>
|
||||
<DmOverview messages={dmMessagesOverview()} />
|
||||
<Heading level={2}>Messages per</Heading>
|
||||
<Heading level={2}>{m.fresh_grand_millipede_twirl()}</Heading>
|
||||
|
||||
<Suspense
|
||||
fallback={
|
||||
<Flex alignItems="center" justifyContent="center" class="h-64">
|
||||
<p class="text-4xl">Loading...</p>
|
||||
<p class="text-4xl">{m.mealy_wacky_toucan_spark()}</p>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<Grid cols={1} colsMd={2} class="gap-x-16 gap-y-16">
|
||||
<div>
|
||||
<Heading level={3}>Person</Heading>
|
||||
<Heading level={3}>{m.top_brief_sparrow_boost()}</Heading>
|
||||
<DmMessagesPerRecipient personStats={dmMessageStats()?.person} recipients={recipients()} />
|
||||
</div>
|
||||
<div>
|
||||
<Heading level={3}>Daytime</Heading>
|
||||
<Heading level={3}>{m.cool_cozy_dingo_mop()}</Heading>
|
||||
<DmMessagesPerDaytime daytimeStats={dmMessageStats()?.daytime} recipients={recipients()} />
|
||||
</div>
|
||||
<div>
|
||||
<Heading level={3}>Month</Heading>
|
||||
<Heading level={3}>{m.funny_wise_mink_boil()}</Heading>
|
||||
<DmMessagesPerMonth monthStats={dmMessageStats()?.month} recipients={recipients()} />
|
||||
</div>
|
||||
<div>
|
||||
<Heading level={3}>Weekday</Heading>
|
||||
<Heading level={3}>{m.wise_house_bobcat_boil()}</Heading>
|
||||
<DmMessagesPerWeekday weekdayStats={dmMessageStats()?.weekday} recipients={recipients()} />
|
||||
</div>
|
||||
</Grid>
|
||||
</Suspense>
|
||||
<Heading level={2}>Word cloud</Heading>
|
||||
<Heading level={2}>{m.north_green_goat_arise()}</Heading>
|
||||
<Suspense
|
||||
fallback={
|
||||
<Flex alignItems="center" justifyContent="center" class="h-64">
|
||||
<p class="text-4xl">Loading...</p>
|
||||
<p class="text-4xl">{m.mealy_wacky_toucan_spark()}</p>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { type Component, Show } from "solid-js";
|
|||
import { Flex } from "~/components/ui/flex";
|
||||
import { Grid } from "~/components/ui/grid";
|
||||
import { getDistanceBetweenDatesInDays } from "~/lib/date";
|
||||
import * as m from "~/paraglide/messages";
|
||||
import type { MessageOverview } from "~/types";
|
||||
|
||||
export const DmOverview: Component<{
|
||||
|
@ -29,7 +30,7 @@ export const DmOverview: Component<{
|
|||
<CalendarArrowDown class="h-8 w-8" />
|
||||
</Flex>
|
||||
<Flex flexDirection="col" justifyContent="around" class="flex-1">
|
||||
<span>Your first message is from</span>
|
||||
<span>{m.knotty_wild_rat_aim()}</span>
|
||||
<Show when={dmOverview()}>
|
||||
{(currentDmOverview) => (
|
||||
<span class="font-semibold text-2xl">{currentDmOverview().firstMessageDate.toDateString()}</span>
|
||||
|
@ -42,7 +43,7 @@ export const DmOverview: Component<{
|
|||
<CalendarArrowUp class="h-8 w-8" />
|
||||
</Flex>
|
||||
<Flex flexDirection="col" justifyContent="around" class="flex-1">
|
||||
<span>Your last message is from</span>
|
||||
<span>{m.bald_helpful_millipede_intend()}</span>
|
||||
<Show when={dmOverview()}>
|
||||
{(currentDmOverview) => (
|
||||
<span class="font-semibold text-2xl">{currentDmOverview().lastMessageDate.toDateString()}</span>
|
||||
|
@ -55,7 +56,7 @@ export const DmOverview: Component<{
|
|||
<CalendarClock class="h-8 w-8" />
|
||||
</Flex>
|
||||
<Flex flexDirection="col" justifyContent="around" class="flex-1">
|
||||
<span>You have been chatting for</span>
|
||||
<span>{m.trite_civil_niklas_laugh()}</span>
|
||||
<Show when={dmOverview()}>
|
||||
{(currentDmOverview) => (
|
||||
<span class="font-semibold text-2xl">
|
||||
|
@ -66,7 +67,7 @@ export const DmOverview: Component<{
|
|||
</span>
|
||||
)}
|
||||
</Show>
|
||||
<span>days</span>
|
||||
<span>{m.careful_real_deer_buzz()}</span>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex flexDirection="row" justifyContent="evenly" class="bg-pink-200 p-2 text-pink-900">
|
||||
|
@ -74,13 +75,13 @@ export const DmOverview: Component<{
|
|||
<MessagesSquare class="h-8 w-8" />
|
||||
</Flex>
|
||||
<Flex flexDirection="col" justifyContent="around" class="flex-1">
|
||||
<span>You have written</span>
|
||||
<span>{m.due_gaudy_lark_grip()}</span>
|
||||
<Show when={dmOverview()}>
|
||||
{(currentDmOverview) => (
|
||||
<span class="font-semibold text-2xl">{currentDmOverview().messageCount.toString()}</span>
|
||||
)}
|
||||
</Show>
|
||||
<span>messages</span>
|
||||
<span>{m.weary_noble_ostrich_cry()}</span>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Grid>
|
||||
|
|
|
@ -10,6 +10,7 @@ import { Progress, ProgressLabel, ProgressValueLabel } from "~/components/ui/pro
|
|||
import { TextField, TextFieldInput, TextFieldLabel } from "~/components/ui/text-field";
|
||||
import { loadDb } from "~/db";
|
||||
import { decryptBackup } from "~/lib/backup-decryptor";
|
||||
import * as m from "~/paraglide/messages";
|
||||
|
||||
export const Home: Component<RouteSectionProps> = () => {
|
||||
const navigate = useNavigate();
|
||||
|
@ -83,7 +84,7 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
}}
|
||||
>
|
||||
<Show when={decryptionProgress() !== undefined}>
|
||||
<p class="font-bold text-2xl">Decrypting backup</p>
|
||||
<p class="font-bold text-2xl">{m.polite_brief_tern_promise()}</p>
|
||||
<Progress
|
||||
value={decryptionProgress()}
|
||||
minValue={0}
|
||||
|
@ -92,13 +93,13 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
class="w-[300px] space-y-1"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<ProgressLabel>Decrypting...</ProgressLabel>
|
||||
<ProgressLabel>{m.awake_best_millipede_dazzle()}</ProgressLabel>
|
||||
<ProgressValueLabel />
|
||||
</div>
|
||||
</Progress>
|
||||
</Show>
|
||||
<Show when={totalStatements() !== 0}>
|
||||
<p class="font-bold text-2xl">Loading database</p>
|
||||
<p class="font-bold text-2xl">{m.ideal_wise_poodle_leap()}</p>
|
||||
<Progress
|
||||
value={executedStatements()}
|
||||
minValue={0}
|
||||
|
@ -107,7 +108,7 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
class="w-[300px] space-y-1"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<ProgressLabel>Loading...</ProgressLabel>
|
||||
<ProgressLabel>{m.mealy_wacky_toucan_spark()}</ProgressLabel>
|
||||
<ProgressValueLabel />
|
||||
</div>
|
||||
</Progress>
|
||||
|
@ -118,7 +119,7 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
<form class="mx-auto flex w-full flex-col gap-y-8 p-8 md:w-fit" onSubmit={onSubmit}>
|
||||
<Flex flexDirection="row" class="w-full gap-x-2 md:w-sm" alignItems="end">
|
||||
<TextField onChange={(value) => setPassphrase(value)} class="grow">
|
||||
<TextFieldLabel>Passphrase</TextFieldLabel>
|
||||
<TextFieldLabel>{m.big_actual_osprey_jump()}</TextFieldLabel>
|
||||
<TextFieldInput type={showPassphrase() ? "text" : "password"} />
|
||||
</TextField>
|
||||
<Button variant="ghost" onClick={() => setShowPassphrase((oldValue) => !oldValue)}>
|
||||
|
@ -143,7 +144,7 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
})
|
||||
}
|
||||
>
|
||||
Select backup file
|
||||
{m.same_heroic_robin_grow()}
|
||||
</Button>
|
||||
<span
|
||||
class="absolute bottom-2"
|
||||
|
@ -151,11 +152,11 @@ export const Home: Component<RouteSectionProps> = () => {
|
|||
"text-muted-foreground": !backupFile(),
|
||||
}}
|
||||
>
|
||||
{backupFile() ? backupFile()?.name : "or drop the file here"}
|
||||
{backupFile() ? backupFile()?.name : m.neat_real_squid_cure()}
|
||||
</span>
|
||||
</Flex>
|
||||
<Button type="submit" class="max-w-72 self-end md:w-sm">
|
||||
Decrypt and load backup
|
||||
{m.green_awake_oryx_aid()}
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { RouteSectionProps } from "@solidjs/router";
|
|||
import { type Component, Show, createResource } from "solid-js";
|
||||
import { SELF_ID, allThreadsOverviewQuery, overallSentMessagesQuery } from "~/db";
|
||||
import { getNameFromRecipient } from "~/lib/get-name-from-recipient";
|
||||
import * as m from "~/paraglide/messages";
|
||||
import { OverviewTable, type RoomOverview } from "./overview-table";
|
||||
|
||||
export const Overview: Component<RouteSectionProps> = () => {
|
||||
|
@ -36,11 +37,12 @@ export const Overview: Component<RouteSectionProps> = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Title>Signal statistics overview</Title>
|
||||
|
||||
<Title>{m.minor_ideal_chipmunk_slide()}</Title>
|
||||
<div>
|
||||
<p>All messages: {allSelfSentMessagesCount()?.messageCount as number}</p>
|
||||
<Show when={!roomOverview.loading && roomOverview()} fallback="Loading...">
|
||||
<p>
|
||||
{m.grassy_tidy_wallaby_explore()} {allSelfSentMessagesCount()?.messageCount as number}
|
||||
</p>
|
||||
<Show when={!roomOverview.loading && roomOverview()} fallback={m.mealy_wacky_toucan_spark()}>
|
||||
{(currentRoomOverview) => <OverviewTable data={currentRoomOverview()} />}
|
||||
</Show>
|
||||
</div>
|
||||
|
|
|
@ -24,6 +24,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~
|
|||
import { TextField, TextFieldInput } from "~/components/ui/text-field";
|
||||
import { dbLoaded, threadSentMessagesOverviewQuery } from "~/db";
|
||||
import { cn } from "~/lib/utils";
|
||||
import * as m from "~/paraglide/messages";
|
||||
|
||||
export interface RoomOverview {
|
||||
threadId: number;
|
||||
|
@ -95,7 +96,7 @@ export const columns = [
|
|||
props.column.toggleSorting();
|
||||
}}
|
||||
>
|
||||
Name
|
||||
{m.stout_busy_bumblebee_praise()}
|
||||
<SortingDisplay sorting={sorting()} class="ml-2 h-4 w-4" activeClass="text-info-foreground" />
|
||||
</Button>
|
||||
);
|
||||
|
@ -114,17 +115,17 @@ export const columns = [
|
|||
<Flex flexDirection="row" class="ml-auto gap-2">
|
||||
<Show when={isArchived}>
|
||||
<Badge variant="outline" class="ml-auto">
|
||||
Archived
|
||||
{m.front_shy_gecko_climb()}
|
||||
</Badge>
|
||||
</Show>
|
||||
<Show when={isGroup}>
|
||||
<Badge variant="outline" class="ml-auto">
|
||||
Group
|
||||
{m.broad_pretty_donkey_burn()}
|
||||
</Badge>
|
||||
</Show>
|
||||
<Show when={isNotAvailable}>
|
||||
<Badge variant="outline" class="ml-auto">
|
||||
Not available
|
||||
{m.every_ornate_tuna_slurp()}
|
||||
</Badge>
|
||||
</Show>
|
||||
</Flex>
|
||||
|
@ -146,7 +147,7 @@ export const columns = [
|
|||
props.column.toggleSorting();
|
||||
}}
|
||||
>
|
||||
Number of messages
|
||||
{m.bland_tidy_rooster_aid()}
|
||||
<SortingDisplay sorting={sorting()} class="ml-2 h-4 w-4" activeClass="text-info-foreground" />
|
||||
</Button>
|
||||
);
|
||||
|
@ -165,7 +166,7 @@ export const columns = [
|
|||
props.column.toggleSorting();
|
||||
}}
|
||||
>
|
||||
Date of last message
|
||||
{m.silly_bland_buzzard_glow()}
|
||||
<SortingDisplay sorting={sorting()} class="ml-2 h-4 w-4" activeClass="text-info-foreground" />
|
||||
</Button>
|
||||
);
|
||||
|
@ -186,7 +187,7 @@ export const columns = [
|
|||
cell: (props) => {
|
||||
return (
|
||||
<Show when={props.cell.getValue()}>
|
||||
<Badge>Archived</Badge>
|
||||
<Badge>{m.front_shy_gecko_climb()}</Badge>
|
||||
</Show>
|
||||
);
|
||||
},
|
||||
|
@ -197,7 +198,7 @@ export const columns = [
|
|||
cell: (props) => {
|
||||
return (
|
||||
<Show when={props.cell.getValue()}>
|
||||
<Badge>Group</Badge>
|
||||
<Badge>{m.broad_pretty_donkey_burn()}</Badge>
|
||||
</Show>
|
||||
);
|
||||
},
|
||||
|
@ -274,7 +275,7 @@ export const OverviewTable = (props: OverviewTableProps) => {
|
|||
table.getColumn("name")?.setFilterValue(value);
|
||||
}}
|
||||
>
|
||||
<TextFieldInput placeholder="Filter by name..." class="max-w-sm" />
|
||||
<TextFieldInput placeholder={m.home_white_wren_offer()} class="max-w-sm" />
|
||||
</TextField>
|
||||
</div>
|
||||
<div class="flex items-start space-x-2">
|
||||
|
@ -287,7 +288,7 @@ export const OverviewTable = (props: OverviewTableProps) => {
|
|||
}}
|
||||
/>
|
||||
<div class="grid gap-1.5 leading-none">
|
||||
<Label for="show-archived">Show archived chats</Label>
|
||||
<Label for="show-archived">{m.light_safe_crow_treasure()}</Label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start space-x-2">
|
||||
|
@ -300,7 +301,7 @@ export const OverviewTable = (props: OverviewTableProps) => {
|
|||
}}
|
||||
/>
|
||||
<div class="grid gap-1.5 leading-none">
|
||||
<Label for="show-groups">Show group chats (detailed analysis not implemented)</Label>
|
||||
<Label for="show-groups">{m.proof_heavy_dingo_bubble()}</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,11 +2,12 @@ import type { RouteSectionProps } from "@solidjs/router";
|
|||
import type { Component } from "solid-js";
|
||||
import { A } from "~/components/ui/A";
|
||||
import { Heading } from "~/components/ui/heading";
|
||||
import * as m from "~/paraglide/messages";
|
||||
|
||||
export const Privacy: Component<RouteSectionProps> = () => {
|
||||
return (
|
||||
<div class="m-auto mb-8 max-w-4xl [&>:is(p,ul)]:mb-4">
|
||||
<Heading level={1}>Privacy policy</Heading>
|
||||
<Heading level={1}>{m.few_helpful_kestrel_swim()}</Heading>
|
||||
<Heading level={2}>Introduction</Heading>
|
||||
<p>
|
||||
This project ("signalstats", "I", "my") was built with the intention to not harm the privacy of its users. This
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue