fix: expose i18n instance

This commit is contained in:
Shusui MOYATANI
2024-01-22 12:38:53 +09:00
parent f9aab378bd
commit 2423f742e2
35 changed files with 246 additions and 247 deletions

View File

@@ -130,7 +130,7 @@ const ReactionAction = (props: { event: NostrEvent }) => {
}} }}
> >
<button <button
class="h-4 w-4" class="size-4"
onClick={handleReaction} onClick={handleReaction}
disabled={publishReactionMutation.isPending} disabled={publishReactionMutation.isPending}
> >
@@ -156,7 +156,7 @@ const ReactionAction = (props: { event: NostrEvent }) => {
> >
<button <button
ref={emojiPickerPopup.targetRef} ref={emojiPickerPopup.targetRef}
class="h-4 w-4" class="size-4"
onClick={() => emojiPickerPopup.open()} onClick={() => emojiPickerPopup.open()}
> >
<Plus /> <Plus />
@@ -217,7 +217,7 @@ const RepostAction = (props: { event: NostrEvent }) => {
}} }}
> >
<button onClick={handleRepost} disabled={publishRepostMutation.isPending}> <button onClick={handleRepost} disabled={publishRepostMutation.isPending}>
<span class="flex h-4 w-4"> <span class="flex size-4">
<ArrowPathRoundedSquare /> <ArrowPathRoundedSquare />
</span> </span>
</button> </button>
@@ -359,11 +359,11 @@ const Actions: Component<ActionProps> = (props) => {
const succeeded = results.filter((res) => res.status === 'fulfilled').length; const succeeded = results.filter((res) => res.status === 'fulfilled').length;
const failed = results.length - succeeded; const failed = results.length - succeeded;
if (succeeded === results.length) { if (succeeded === results.length) {
window.alert(i18n()('post.deletedSuccessfully')); window.alert(i18n.t('post.deletedSuccessfully'));
} else if (succeeded > 0) { } else if (succeeded > 0) {
window.alert(i18n()('post.failedToDeletePartially', { count: failed })); window.alert(i18n.t('post.failedToDeletePartially', { count: failed }));
} else { } else {
window.alert(i18n()('post.failedToDelete')); window.alert(i18n.t('post.failedToDelete'));
} }
}, },
onError: (err) => { onError: (err) => {
@@ -374,7 +374,7 @@ const Actions: Component<ActionProps> = (props) => {
const otherActionsPopup = useContextMenu(() => ({ const otherActionsPopup = useContextMenu(() => ({
menu: [ menu: [
{ {
content: i18n()('post.copyEventId'), content: i18n.t('post.copyEventId'),
onSelect: () => { onSelect: () => {
navigator.clipboard navigator.clipboard
.writeText(noteEncode(props.event.id)) .writeText(noteEncode(props.event.id))
@@ -382,31 +382,31 @@ const Actions: Component<ActionProps> = (props) => {
}, },
}, },
{ {
content: i18n()('post.showJSON'), content: i18n.t('post.showJSON'),
onSelect: () => { onSelect: () => {
setModal('EventDebugModal'); setModal('EventDebugModal');
}, },
}, },
{ {
content: i18n()('post.showReposts'), content: i18n.t('post.showReposts'),
onSelect: () => { onSelect: () => {
setModal('Reposts'); setModal('Reposts');
}, },
}, },
{ {
content: i18n()('post.showReactions'), content: i18n.t('post.showReactions'),
onSelect: () => { onSelect: () => {
setModal('Reactions'); setModal('Reactions');
}, },
}, },
{ {
when: () => props.event.pubkey === pubkey(), when: () => props.event.pubkey === pubkey(),
content: <span class="text-red-500">{i18n()('post.deletePost')}</span>, content: <span class="text-red-500">{i18n.t('post.deletePost')}</span>,
onSelect: () => { onSelect: () => {
const p = pubkey(); const p = pubkey();
if (p == null) return; if (p == null) return;
if (!window.confirm(i18n()('post.confirmDelete'))) return; if (!window.confirm(i18n.t('post.confirmDelete'))) return;
deleteMutation.mutate({ deleteMutation.mutate({
relayUrls: config().relayUrls, relayUrls: config().relayUrls,
pubkey: p, pubkey: p,
@@ -428,21 +428,21 @@ const Actions: Component<ActionProps> = (props) => {
props.onClickReply(); props.onClickReply();
}} }}
> >
<span class="flex h-4 w-4"> <span class="flex size-4">
<ChatBubbleLeft /> <ChatBubbleLeft />
</span> </span>
</button> </button>
<RepostAction event={props.event} /> <RepostAction event={props.event} />
<ReactionAction event={props.event} /> <ReactionAction event={props.event} />
<button type="button" onClick={() => setModal('ZapRequest')}> <button type="button" onClick={() => setModal('ZapRequest')}>
<span class="flex h-4 w-4 text-fg-tertiary hover:text-r-zap"> <span class="flex size-4 text-fg-tertiary hover:text-r-zap">
<Bolt /> <Bolt />
</span> </span>
</button> </button>
<button <button
ref={otherActionsPopup.targetRef} ref={otherActionsPopup.targetRef}
type="button" type="button"
class="h-4 w-4 shrink-0 text-fg-tertiary hover:text-fg-tertiary/70" class="size-4 shrink-0 text-fg-tertiary hover:text-fg-tertiary/70"
onClick={() => otherActionsPopup.open()} onClick={() => otherActionsPopup.open()}
> >
<EllipsisHorizontal /> <EllipsisHorizontal />

View File

@@ -17,24 +17,24 @@ const DomainTransferInfo: Component<{ children: JSX.Element }> = (props) => {
when={isPermittedDomain(window.location) || showContent()} when={isPermittedDomain(window.location) || showContent()}
fallback={ fallback={
<div class="flex h-svh w-screen shrink-0 flex-col items-center justify-center border-b border-border bg-bg-tertiary text-fg"> <div class="flex h-svh w-screen shrink-0 flex-col items-center justify-center border-b border-border bg-bg-tertiary text-fg">
<h3 class="text-2xl font-bold">{i18n()('domainTransfer.announcementHead')}</h3> <h3 class="text-2xl font-bold">{i18n.t('domainTransfer.announcementHead')}</h3>
<div>{i18n()('domainTransfer.announcementDescription')}</div> <div>{i18n.t('domainTransfer.announcementDescription')}</div>
<SafeLink class="text-lg text-link underline" href="https://rabbit.syusui.net/" /> <SafeLink class="text-lg text-link underline" href="https://rabbit.syusui.net/" />
<SafeLink <SafeLink
class="mt-4 text-sm text-link underline" class="mt-4 text-sm text-link underline"
href="https://scrapbox.io/nostr/Rabbit#659be5fa1246d700005facb8" href="https://scrapbox.io/nostr/Rabbit#659be5fa1246d700005facb8"
> >
{i18n()('domainTransfer.howToMigrateSettings')} {i18n.t('domainTransfer.howToMigrateSettings')}
</SafeLink> </SafeLink>
<button <button
type="button" type="button"
class="mt-4 flex items-center text-fg-secondary" class="mt-4 flex items-center text-fg-secondary"
onClick={() => setShowContent(true)} onClick={() => setShowContent(true)}
> >
<span class="inline-block h-5 w-5"> <span class="inline-block size-5">
<XMark /> <XMark />
</span> </span>
{i18n()('domainTransfer.close')} {i18n.t('domainTransfer.close')}
</button> </button>
</div> </div>
} }

View File

@@ -112,10 +112,10 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
const placeholder = (mode: NotePostFormProps['mode']) => { const placeholder = (mode: NotePostFormProps['mode']) => {
switch (mode) { switch (mode) {
case 'reply': case 'reply':
return i18n()('posting.placeholderReply'); return i18n.t('posting.placeholderReply');
case 'normal': case 'normal':
default: default:
return i18n()('posting.placeholder'); return i18n.t('posting.placeholder');
} }
}; };
@@ -163,7 +163,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
if (failed.length > 0) { if (failed.length > 0) {
const filenames = failed.map((f) => f.name).join(', '); const filenames = failed.map((f) => f.name).join(', ');
window.alert(i18n()('posting.failedToUploadFile', { filenames })); window.alert(i18n.t('posting.failedToUploadFile', { filenames }));
} }
}, },
})); }));
@@ -205,7 +205,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
if (publishTextNoteMutation.isPending) return; if (publishTextNoteMutation.isPending) return;
if (/nsec1[0-9a-zA-Z]+/.test(text())) { if (/nsec1[0-9a-zA-Z]+/.test(text())) {
window.alert(i18n()('posting.forbiddenToIncludeNsec')); window.alert(i18n.t('posting.forbiddenToIncludeNsec'));
return; return;
} }
@@ -353,7 +353,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
<div class="p-1"> <div class="p-1">
<Show when={props.replyTo != null}> <Show when={props.replyTo != null}>
<div> <div>
{i18n()('posting.replyToPre')} {i18n.t('posting.replyToPre')}
<For each={notifyPubkeys()}> <For each={notifyPubkeys()}>
{(pubkey, index) => ( {(pubkey, index) => (
<> <>
@@ -362,7 +362,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
</> </>
)} )}
</For> </For>
{i18n()('posting.replyToPost')} {i18n.t('posting.replyToPost')}
</div> </div>
</Show> </Show>
<form class="flex flex-col gap-1" onSubmit={handleSubmit}> <form class="flex flex-col gap-1" onSubmit={handleSubmit}>
@@ -371,7 +371,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
ref={contentWarningReasonRef} ref={contentWarningReasonRef}
type="text" type="text"
class="rounded-md border border-border bg-bg ring-border placeholder:text-fg-secondary focus:border-border focus:ring-primary" class="rounded-md border border-border bg-bg ring-border placeholder:text-fg-secondary focus:border-border focus:ring-primary"
placeholder={i18n()('posting.contentWarningReason')} placeholder={i18n.t('posting.contentWarningReason')}
maxLength={32} maxLength={32}
onInput={(ev) => setContentWarningReason(ev.currentTarget.value)} onInput={(ev) => setContentWarningReason(ev.currentTarget.value)}
value={contentWarningReason()} value={contentWarningReason()}
@@ -406,7 +406,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
}} }}
onClick={() => close()} onClick={() => close()}
> >
<span class="inline-block h-5 w-5 text-fg-secondary/70"> <span class="inline-block size-5 text-fg-secondary/70">
<XMark /> <XMark />
</span> </span>
</button> </button>
@@ -442,8 +442,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
'p-[6px]': mode() === 'reply', 'p-[6px]': mode() === 'reply',
}} }}
type="button" type="button"
aria-label={i18n()('posting.contentWarning')} aria-label={i18n.t('posting.contentWarning')}
title={i18n()('posting.contentWarning')} title={i18n.t('posting.contentWarning')}
onClick={() => { onClick={() => {
setContentWarning((e) => !e); setContentWarning((e) => !e);
contentWarningReasonRef?.focus(); contentWarningReasonRef?.focus();
@@ -464,8 +464,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
'p-[6px]': mode() === 'reply', 'p-[6px]': mode() === 'reply',
}} }}
type="button" type="button"
title={i18n()('posting.uploadImage')} title={i18n.t('posting.uploadImage')}
aria-label={i18n()('posting.uploadImage')} aria-label={i18n.t('posting.uploadImage')}
disabled={fileUploadDisabled()} disabled={fileUploadDisabled()}
onClick={() => fileInputRef?.click()} onClick={() => fileInputRef?.click()}
> >
@@ -482,8 +482,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
'w-7': mode() === 'reply', 'w-7': mode() === 'reply',
}} }}
type="submit" type="submit"
aria-label={i18n()('posting.submit')} aria-label={i18n.t('posting.submit')}
title={i18n()('posting.submit')} title={i18n.t('posting.submit')}
disabled={submitDisabled()} disabled={submitDisabled()}
> >
<PaperAirplane /> <PaperAirplane />

View File

@@ -36,7 +36,7 @@ const Post: Component<PostProps> = (props) => {
<div class="flex w-full gap-1"> <div class="flex w-full gap-1">
<button <button
type="button" type="button"
class="author-icon h-10 w-10 shrink-0 overflow-hidden rounded" class="author-icon size-10 shrink-0 overflow-hidden rounded"
onClick={(ev) => { onClick={(ev) => {
ev.preventDefault(); ev.preventDefault();
props.onShowProfile?.(); props.onShowProfile?.();
@@ -50,7 +50,7 @@ const Post: Component<PostProps> = (props) => {
src={thumbnailUrl(url, 'icon')} src={thumbnailUrl(url, 'icon')}
alt="icon" alt="icon"
referrerpolicy="no-referrer" referrerpolicy="no-referrer"
class="h-full w-full object-cover" class="size-full object-cover"
/> />
)} )}
</LazyLoad> </LazyLoad>
@@ -113,8 +113,8 @@ const Post: Component<PostProps> = (props) => {
setShowOverflow((current) => !current); setShowOverflow((current) => !current);
}} }}
> >
<Show when={!showOverflow()} fallback={i18n()('post.hideOverflow')}> <Show when={!showOverflow()} fallback={i18n.t('post.hideOverflow')}>
{i18n()('post.showOverflow')} {i18n.t('post.showOverflow')}
</Show> </Show>
</button> </button>
</Show> </Show>

View File

@@ -33,7 +33,7 @@ const BookmarkColumn: Component<BookmarkColumnDisplayProps> = (props) => {
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.bookmark')} name={props.column.name ?? i18n.t('column.bookmark')}
icon={<BookmarkIcon />} icon={<BookmarkIcon />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -44,7 +44,7 @@ const ChannelColumn: Component<ChannelColumnProps> = (props) => {
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.channel')} name={props.column.name ?? i18n.t('column.channel')}
icon={<ChatBubbleLeftRight />} icon={<ChatBubbleLeftRight />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -73,10 +73,10 @@ const Column: Component<ColumnProps> = (props) => {
class="flex w-full items-center gap-1" class="flex w-full items-center gap-1"
onClick={() => timelineState?.clearTimeline()} onClick={() => timelineState?.clearTimeline()}
> >
<div class="inline-block h-4 w-4"> <div class="inline-block size-4">
<ArrowLeft /> <ArrowLeft />
</div> </div>
<div>{i18n()('column.back')}</div> <div>{i18n.t('column.back')}</div>
</button> </button>
</div> </div>
<div class="scrollbar flex max-h-full flex-col overflow-y-scroll scroll-smooth pb-16"> <div class="scrollbar flex max-h-full flex-col overflow-y-scroll scroll-smooth pb-16">

View File

@@ -42,7 +42,7 @@ const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
return ( return (
<div class="flex flex-col border-t border-border"> <div class="flex flex-col border-t border-border">
<ColumnSettingsSection title={i18n()('column.config.columnWidth')}> <ColumnSettingsSection title={i18n.t('column.config.columnWidth')}>
<div class="scrollbar flex h-9 gap-2 overflow-x-auto"> <div class="scrollbar flex h-9 gap-2 overflow-x-auto">
<For each={['widest', 'wide', 'medium', 'narrow'] as const}> <For each={['widest', 'wide', 'medium', 'narrow'] as const}>
{(width) => ( {(width) => (
@@ -56,7 +56,7 @@ const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
}} }}
onClick={() => setColumnWidth(width)} onClick={() => setColumnWidth(width)}
> >
{i18n()(`column.config.${width}`)} {i18n.t(`column.config.${width}`)}
</button> </button>
)} )}
</For> </For>
@@ -65,29 +65,29 @@ const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
<div class="flex h-10 items-center gap-2"> <div class="flex h-10 items-center gap-2">
<button <button
class="py-4 pl-2" class="py-4 pl-2"
title={i18n()('column.config.moveLeft')} title={i18n.t('column.config.moveLeft')}
onClick={() => move(props.columnIndex - 1)} onClick={() => move(props.columnIndex - 1)}
> >
<span class="inline-block h-4 w-4"> <span class="inline-block size-4">
<ChevronLeft /> <ChevronLeft />
</span> </span>
</button> </button>
<button <button
class="py-4 pr-2" class="py-4 pr-2"
title={i18n()('column.config.moveRight')} title={i18n.t('column.config.moveRight')}
onClick={() => move(props.columnIndex + 1)} onClick={() => move(props.columnIndex + 1)}
> >
<span class="inline-block h-4 w-4"> <span class="inline-block size-4">
<ChevronRight /> <ChevronRight />
</span> </span>
</button> </button>
<div class="flex-1" /> <div class="flex-1" />
<button <button
class="px-2 py-4 text-danger hover:text-rose-600" class="px-2 py-4 text-danger hover:text-rose-600"
title={i18n()('column.config.removeColumn')} title={i18n.t('column.config.removeColumn')}
onClick={() => removeColumn(props.column.id)} onClick={() => removeColumn(props.column.id)}
> >
<span class="inline-block h-4 w-4" aria-label={i18n()('column.config.removeColumn')}> <span class="inline-block size-4" aria-label={i18n.t('column.config.removeColumn')}>
<Trash /> <Trash />
</span> </span>
</button> </button>

View File

@@ -70,7 +70,7 @@ const FollowingColumn: Component<FollowingColumnDisplayProps> = (props) => {
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.home')} name={props.column.name ?? i18n.t('column.home')}
icon={<Home />} icon={<Home />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -91,7 +91,7 @@ const LoadMore: Component<LoadMoreProps> = (props) => {
class="flex h-12 w-full flex-col items-center justify-center hover:text-fg-secondary" class="flex h-12 w-full flex-col items-center justify-center hover:text-fg-secondary"
onClick={() => props.loadMore.loadLatest()} onClick={() => props.loadMore.loadLatest()}
> >
<span>{i18n()('column.loadLatest')}</span> <span>{i18n.t('column.loadLatest')}</span>
</button> </button>
</ColumnItem> </ColumnItem>
</Show> </Show>
@@ -102,7 +102,7 @@ const LoadMore: Component<LoadMoreProps> = (props) => {
disabled={!props.eose} disabled={!props.eose}
onClick={() => props.loadMore.loadOld()} onClick={() => props.loadMore.loadOld()}
> >
<span>{i18n()('column.loadOld')}</span> <span>{i18n.t('column.loadOld')}</span>
</button> </button>
</ColumnItem> </ColumnItem>
</> </>

View File

@@ -49,7 +49,7 @@ const NotificationColumn: Component<NotificationColumnDisplayProps> = (props) =>
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.notification')} name={props.column.name ?? i18n.t('column.notification')}
icon={<Bell />} icon={<Bell />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -49,7 +49,7 @@ const PostsColumn: Component<PostsColumnDisplayProps> = (props) => {
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.posts')} name={props.column.name ?? i18n.t('column.posts')}
icon={<User />} icon={<User />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -49,7 +49,7 @@ const ReactionsColumn: Component<ReactionsColumnDisplayProps> = (props) => {
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.reactions')} name={props.column.name ?? i18n.t('column.reactions')}
icon={<Heart />} icon={<Heart />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -50,7 +50,7 @@ const RelaysColumn: Component<RelaysColumnDisplayProps> = (props) => {
<Column <Column
header={ header={
<BasicColumnHeader <BasicColumnHeader
name={props.column.name ?? i18n()('column.relay')} name={props.column.name ?? i18n.t('column.relay')}
icon={<GlobeAlt />} icon={<GlobeAlt />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />} settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
onClose={() => removeColumn(props.column.id)} onClose={() => removeColumn(props.column.id)}

View File

@@ -30,14 +30,14 @@ const EventDisplay: Component<EventDisplayProps> = (props) => {
<Switch <Switch
fallback={ fallback={
<div> <div>
<span>{i18n()('post.unsupportedKind', { kind: props.event.kind })}</span> <span>{i18n.t('post.unsupportedKind', { kind: props.event.kind })}</span>
<EventLink eventId={props.event.id} kind={props.event.kind} /> <EventLink eventId={props.event.id} kind={props.event.kind} />
</div> </div>
} }
> >
<Match when={!isAllowedKind()} keyed> <Match when={!isAllowedKind()} keyed>
<div> <div>
<span>{i18n()('post.unexpectedKind', { kind: props.event.kind })}</span> <span>{i18n.t('post.unexpectedKind', { kind: props.event.kind })}</span>
<EventLink eventId={props.event.id} kind={props.event.kind} /> <EventLink eventId={props.event.id} kind={props.event.kind} />
</div> </div>
</Match> </Match>

View File

@@ -33,7 +33,7 @@ const EventDisplayById: Component<EventDisplayByIdProps> = (props) => {
<Switch <Switch
fallback={ fallback={
<span> <span>
{i18n()('post.failedToFetchEvent')} {i18n.t('post.failedToFetchEvent')}
{props.eventId} {props.eventId}
</span> </span>
} }
@@ -45,7 +45,7 @@ const EventDisplayById: Component<EventDisplayByIdProps> = (props) => {
<Match when={eventQuery.isLoading && localProps.eventId} keyed> <Match when={eventQuery.isLoading && localProps.eventId} keyed>
{(id) => ( {(id) => (
<div class="truncate"> <div class="truncate">
{i18n()('general.loading')} <EventLink eventId={id} /> {i18n.t('general.loading')} <EventLink eventId={id} />
</div> </div>
)} )}
</Match> </Match>

View File

@@ -47,14 +47,14 @@ const ReactionDisplay: Component<ReactionDisplayProps> = (props) => {
<EmojiDisplay reactionTypes={event().toReactionTypes()} /> <EmojiDisplay reactionTypes={event().toReactionTypes()} />
</div> </div>
<div class="notification-user flex flex-1 gap-1 overflow-hidden"> <div class="notification-user flex flex-1 gap-1 overflow-hidden">
<div class="author-icon h-5 w-5 shrink-0 overflow-hidden rounded"> <div class="author-icon size-5 shrink-0 overflow-hidden rounded">
<Show when={profile()?.picture} keyed> <Show when={profile()?.picture} keyed>
{(url) => ( {(url) => (
<img <img
src={thumbnailUrl(url, 'icon')} src={thumbnailUrl(url, 'icon')}
alt="icon" alt="icon"
// TODO autofit // TODO autofit
class="h-full w-full object-cover" class="size-full object-cover"
/> />
)} )}
</Show> </Show>
@@ -66,7 +66,7 @@ const ReactionDisplay: Component<ReactionDisplayProps> = (props) => {
> >
<UserDisplayName pubkey={props.event.pubkey} /> <UserDisplayName pubkey={props.event.pubkey} />
</button> </button>
<span class="shrink-0">{i18n()('notification.reacted')}</span> <span class="shrink-0">{i18n.t('notification.reacted')}</span>
</div> </div>
</div> </div>
<div class="text-xs">{formatDate(event().createdAtAsDate())}</div> <div class="text-xs">{formatDate(event().createdAtAsDate())}</div>
@@ -76,7 +76,7 @@ const ReactionDisplay: Component<ReactionDisplayProps> = (props) => {
when={reactedEvent()} when={reactedEvent()}
fallback={ fallback={
<div class="truncate"> <div class="truncate">
{i18n()('general.loading')} {eventId()} {i18n.t('general.loading')} {eventId()}
</div> </div>
} }
keyed keyed

View File

@@ -27,7 +27,7 @@ const Repost: Component<RepostProps> = (props) => {
<div> <div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<div class="flex shrink-0 place-items-center pl-[2px]" aria-hidden="true"> <div class="flex shrink-0 place-items-center pl-[2px]" aria-hidden="true">
<span class="h-4 w-4 text-r-repost"> <span class="size-4 text-r-repost">
<ArrowPathRoundedSquare /> <ArrowPathRoundedSquare />
</span> </span>
</div> </div>
@@ -38,7 +38,7 @@ const Repost: Component<RepostProps> = (props) => {
> >
<UserDisplayName pubkey={props.event.pubkey} /> <UserDisplayName pubkey={props.event.pubkey} />
</button> </button>
<span class="shrink-0">{i18n()('notification.reposted')}</span> <span class="shrink-0">{i18n.t('notification.reposted')}</span>
</div> </div>
<div class="text-xs">{formatDate(event().createdAtAsDate())}</div> <div class="text-xs">{formatDate(event().createdAtAsDate())}</div>
</div> </div>

View File

@@ -83,7 +83,7 @@ const TextNote: Component<TextNoteProps> = (props) => {
</Show> </Show>
<Show when={event().taggedPubkeys().length > 0}> <Show when={event().taggedPubkeys().length > 0}>
<div class="text-xs"> <div class="text-xs">
{i18n()('post.replyToPre')} {i18n.t('post.replyToPre')}
<For each={event().taggedPubkeys()}> <For each={event().taggedPubkeys()}>
{(replyToPubkey: string) => ( {(replyToPubkey: string) => (
<button <button
@@ -97,7 +97,7 @@ const TextNote: Component<TextNoteProps> = (props) => {
</button> </button>
)} )}
</For> </For>
{i18n()('post.replyToPost')} {i18n.t('post.replyToPost')}
</div> </div>
</Show> </Show>
<ContentWarningDisplay contentWarning={event().contentWarning()}> <ContentWarningDisplay contentWarning={event().contentWarning()}>

View File

@@ -74,20 +74,20 @@ const ZapReceiptDisplay: Component<ZapReceiptProps> = (props) => {
<Show when={!shouldMuteEvent(props.event) && isZapReceiptVerified()}> <Show when={!shouldMuteEvent(props.event) && isZapReceiptVerified()}>
<div class="flex items-center gap-1 text-sm"> <div class="flex items-center gap-1 text-sm">
<div class="flex w-6 flex-col items-center"> <div class="flex w-6 flex-col items-center">
<div class="h-4 w-4 shrink-0 text-amber-500" aria-hidden="true"> <div class="size-4 shrink-0 text-amber-500" aria-hidden="true">
<Bolt /> <Bolt />
</div> </div>
<div class="mt-[-2px] shrink-0 text-xs">{amountSi()}</div> <div class="mt-[-2px] shrink-0 text-xs">{amountSi()}</div>
</div> </div>
<div class="notification-user flex gap-1 overflow-hidden"> <div class="notification-user flex gap-1 overflow-hidden">
<div class="author-icon h-5 w-5 shrink-0 overflow-hidden rounded"> <div class="author-icon size-5 shrink-0 overflow-hidden rounded">
<Show when={senderProfile()?.picture} keyed> <Show when={senderProfile()?.picture} keyed>
{(url) => ( {(url) => (
<img <img
src={thumbnailUrl(url, 'icon')} src={thumbnailUrl(url, 'icon')}
alt="icon" alt="icon"
// TODO autofit // TODO autofit
class="h-full w-full object-cover" class="size-full object-cover"
/> />
)} )}
</Show> </Show>
@@ -99,7 +99,7 @@ const ZapReceiptDisplay: Component<ZapReceiptProps> = (props) => {
> >
<UserDisplayName pubkey={event().senderPubkey()} /> <UserDisplayName pubkey={event().senderPubkey()} />
</button> </button>
<span class="shrink-0">{i18n()('notification.zapped')}</span> <span class="shrink-0">{i18n.t('notification.zapped')}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -25,10 +25,10 @@ const ContentWarningDisplay: Component<ContentWarningDisplayProps> = (props) =>
<span class="inline-block size-4"> <span class="inline-block size-4">
<ExclamationTriangle /> <ExclamationTriangle />
</span> </span>
<span>{i18n()('post.contentWarning.show')}</span> <span>{i18n.t('post.contentWarning.show')}</span>
<Show when={props.contentWarning.reason != null}> <Show when={props.contentWarning.reason != null}>
<span> <span>
{i18n()('post.contentWarning.reason')}: {props.contentWarning.reason} {i18n.t('post.contentWarning.reason')}: {props.contentWarning.reason}
</span> </span>
</Show> </Show>
</button> </button>
@@ -40,7 +40,7 @@ const ContentWarningDisplay: Component<ContentWarningDisplayProps> = (props) =>
class="text-xs text-fg-secondary hover:text-fg-secondary/70" class="text-xs text-fg-secondary hover:text-fg-secondary/70"
onClick={() => setShowContentWarning(false)} onClick={() => setShowContentWarning(false)}
> >
{i18n()('post.contentWarning.hide')} {i18n.t('post.contentWarning.hide')}
</button> </button>
</Show> </Show>
</Show> </Show>

View File

@@ -23,7 +23,7 @@ const ImageDisplay: Component<ImageDisplayProps> = (props) => {
class="rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow" class="rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow"
onClick={() => setHidden(false)} onClick={() => setHidden(false)}
> >
{i18n()('post.showImage')} {i18n.t('post.showImage')}
</button> </button>
} }
> >

View File

@@ -109,7 +109,7 @@ const ClickToShow: Component<ClickToShowProps> = (props) => {
class="flex flex-col items-center rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow" class="flex flex-col items-center rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow"
onClick={() => setHidden(false)} onClick={() => setHidden(false)}
> >
{i18n()('post.showPreview')} {i18n.t('post.showPreview')}
</button> </button>
<SafeLink class="text-link underline" href={props.url} /> <SafeLink class="text-link underline" href={props.url} />
</div> </div>

View File

@@ -22,7 +22,7 @@ const VideoDisplay: Component<VideoDisplayProps> = (props) => {
class="rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow" class="rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow"
onClick={() => setHidden(false)} onClick={() => setHidden(false)}
> >
{i18n()('post.showVideo')} {i18n.t('post.showVideo')}
</button> </button>
} }
> >
@@ -36,7 +36,7 @@ const VideoDisplay: Component<VideoDisplayProps> = (props) => {
src={props.url} src={props.url}
controls controls
> >
<a href={props.url}>{i18n()('post.download')}</a> <a href={props.url}>{i18n.t('post.download')}</a>
</video> </video>
</SafeLink> </SafeLink>
)} )}

View File

@@ -61,17 +61,17 @@ const About: Component<AboutProps> = (props) => {
class="rounded border-2 border-primary px-4 py-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover" class="rounded border-2 border-primary px-4 py-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover"
href="https://github.com/syusui-s/rabbit/issues/new/choose" href="https://github.com/syusui-s/rabbit/issues/new/choose"
> >
{i18n()('about.bugReport')} {i18n.t('about.bugReport')}
</SafeLink> </SafeLink>
<SafeLink <SafeLink
class="rounded border-2 border-primary px-4 py-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover" class="rounded border-2 border-primary px-4 py-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover"
href="https://github.com/syusui-s/rabbit" href="https://github.com/syusui-s/rabbit"
> >
{i18n()('about.sourceCode')} {i18n.t('about.sourceCode')}
</SafeLink> </SafeLink>
</div> </div>
<h2 class="my-4 text-xl font-bold">{i18n()('about.termOfService')}</h2> <h2 class="my-4 text-xl font-bold">{i18n.t('about.termOfService')}</h2>
<p class="my-4"> <p class="my-4">
Copyright (C) 2023 Shusui Moyatani and{' '} Copyright (C) 2023 Shusui Moyatani and{' '}
@@ -84,12 +84,12 @@ const About: Component<AboutProps> = (props) => {
</p> </p>
<pre class=" max-h-96 overflow-auto rounded bg-bg-tertiary p-2 text-sm"> <pre class=" max-h-96 overflow-auto rounded bg-bg-tertiary p-2 text-sm">
{i18n()('about.agplText')} {i18n.t('about.agplText')}
</pre> </pre>
<p> <p>
<SafeLink class="text-link underline" href="https://gpl.mhatta.org/agpl.ja.html"> <SafeLink class="text-link underline" href="https://gpl.mhatta.org/agpl.ja.html">
{i18n()('about.agplTranslationJa')} {i18n.t('about.agplTranslationJa')}
</SafeLink> </SafeLink>
</p> </p>
@@ -97,7 +97,7 @@ const About: Component<AboutProps> = (props) => {
{packageInfo()?.self.licenseText} {packageInfo()?.self.licenseText}
</pre> </pre>
<h2 class="my-4 text-xl font-bold">{i18n()('about.usingLibraries')}</h2> <h2 class="my-4 text-xl font-bold">{i18n.t('about.usingLibraries')}</h2>
<For each={packageInfo()?.packages ?? []}> <For each={packageInfo()?.packages ?? []}>
{(p) => ( {(p) => (

View File

@@ -84,28 +84,28 @@ const AddColumn: Component<AddColumnProps> = (props) => {
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addFollowingColumn()} onClick={() => addFollowingColumn()}
> >
<span class="inline-block h-8 w-8"> <span class="inline-block size-8">
<Home /> <Home />
</span> </span>
{i18n()('column.home')} {i18n.t('column.home')}
</button> </button>
<button <button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addNotificationColumn()} onClick={() => addNotificationColumn()}
> >
<span class="inline-block h-8 w-8"> <span class="inline-block size-8">
<Bell /> <Bell />
</span> </span>
{i18n()('column.notification')} {i18n.t('column.notification')}
</button> </button>
<button <button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addJapanRelaysColumn()} onClick={() => addJapanRelaysColumn()}
> >
<span class="inline-block h-8 w-8"> <span class="inline-block size-8">
<GlobeAlt /> <GlobeAlt />
</span> </span>
{i18n()('column.japanese')} {i18n.t('column.japanese')}
</button> </button>
{/* {/*
<button <button
@@ -133,28 +133,28 @@ const AddColumn: Component<AddColumnProps> = (props) => {
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addSearchColumn()} onClick={() => addSearchColumn()}
> >
<span class="inline-block h-8 w-8"> <span class="inline-block size-8">
<MagnifyingGlass /> <MagnifyingGlass />
</span> </span>
{i18n()('column.search')} {i18n.t('column.search')}
</button> </button>
<button <button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addMyPostsColumn()} onClick={() => addMyPostsColumn()}
> >
<span class="inline-block h-8 w-8"> <span class="inline-block size-8">
<User /> <User />
</span> </span>
{i18n()('column.myPosts')} {i18n.t('column.myPosts')}
</button> </button>
<button <button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4" class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addMyReactionsColumn()} onClick={() => addMyReactionsColumn()}
> >
<span class="inline-block h-8 w-8"> <span class="inline-block size-8">
<Heart /> <Heart />
</span> </span>
{i18n()('column.myReactions')} {i18n.t('column.myReactions')}
</button> </button>
</div> </div>
</BasicModal> </BasicModal>

View File

@@ -44,7 +44,7 @@ const Section = (props: { title: string; initialOpened?: boolean; children: JSX.
onClick={() => toggleOpened()} onClick={() => toggleOpened()}
> >
<span class="flex-1 hover:text-fg-secondary">{props.title}</span> <span class="flex-1 hover:text-fg-secondary">{props.title}</span>
<span class="inline-block h-4 w-4 shrink-0 text-fg"> <span class="inline-block size-4 shrink-0 text-fg">
<Show when={opened()} fallback={<ChevronDown />}> <Show when={opened()} fallback={<ChevronDown />}>
<ChevronUp /> <ChevronUp />
</Show> </Show>
@@ -73,7 +73,7 @@ const ToggleButton = (props: {
area-label={props.value} area-label={props.value}
onClick={(event) => props.onClick(event)} onClick={(event) => props.onClick(event)}
> >
<span class="m-[-3px] inline-block h-5 w-5 rounded-full border bg-primary-fg shadow" /> <span class="m-[-3px] inline-block size-5 rounded-full border bg-primary-fg shadow" />
</button> </button>
); );
@@ -83,7 +83,7 @@ const ProfileSection = () => {
const { showProfile, showProfileEdit } = useModalState(); const { showProfile, showProfileEdit } = useModalState();
return ( return (
<Section title={i18n()('config.profile.profile')}> <Section title={i18n.t('config.profile.profile')}>
<div class="flex gap-2 py-1"> <div class="flex gap-2 py-1">
<button <button
class="rounded border border-primary px-4 py-1 font-bold text-primary" class="rounded border border-primary px-4 py-1 font-bold text-primary"
@@ -93,13 +93,13 @@ const ProfileSection = () => {
}) })
} }
> >
{i18n()('config.profile.openProfile')} {i18n.t('config.profile.openProfile')}
</button> </button>
<button <button
class="rounded border border-primary px-4 py-1 font-bold text-primary" class="rounded border border-primary px-4 py-1 font-bold text-primary"
onClick={() => showProfileEdit()} onClick={() => showProfileEdit()}
> >
{i18n()('config.profile.editProfile')} {i18n.t('config.profile.editProfile')}
</button> </button>
</div> </div>
</Section> </Section>
@@ -126,11 +126,11 @@ const RelayConfig = () => {
const relayUrls = importedRelays.map(([relayUrl]) => relayUrl).join('\n'); const relayUrls = importedRelays.map(([relayUrl]) => relayUrl).join('\n');
if (importedRelays.length === 0) { if (importedRelays.length === 0) {
window.alert(i18n()('config.relays.notConfigured')); window.alert(i18n.t('config.relays.notConfigured'));
return; return;
} }
if (!window.confirm(`${i18n()('config.relays.askImport')}\n\n${relayUrls}`)) { if (!window.confirm(`${i18n.t('config.relays.askImport')}\n\n${relayUrls}`)) {
return; return;
} }
@@ -142,14 +142,14 @@ const RelayConfig = () => {
}); });
const currentCount = config().relayUrls.length; const currentCount = config().relayUrls.length;
const importedCount = currentCount - lastCount; const importedCount = currentCount - lastCount;
window.alert(i18n()('config.relays.imported', { count: importedCount })); window.alert(i18n.t('config.relays.imported', { count: importedCount }));
}; };
return ( return (
<> <>
<Section title={i18n()('config.relays.relays')}> <Section title={i18n.t('config.relays.relays')}>
<p class="py-1"> <p class="py-1">
{i18n()('config.relays.numOfRelays', { count: config().relayUrls.length })} {i18n.t('config.relays.numOfRelays', { count: config().relayUrls.length })}
</p> </p>
<form class="flex gap-2" onSubmit={handleClickAddRelay}> <form class="flex gap-2" onSubmit={handleClickAddRelay}>
<input <input
@@ -162,7 +162,7 @@ const RelayConfig = () => {
onChange={(ev) => setRelayUrlInput(ev.currentTarget.value)} onChange={(ev) => setRelayUrlInput(ev.currentTarget.value)}
/> />
<button type="submit" class="rounded bg-primary p-2 font-bold text-primary-fg"> <button type="submit" class="rounded bg-primary p-2 font-bold text-primary-fg">
{i18n()('config.relays.addRelay')} {i18n.t('config.relays.addRelay')}
</button> </button>
</form> </form>
<ul class="pt-2"> <ul class="pt-2">
@@ -170,7 +170,7 @@ const RelayConfig = () => {
{(relayUrl: string) => ( {(relayUrl: string) => (
<li class="flex items-center border-t border-border pr-4"> <li class="flex items-center border-t border-border pr-4">
<div class="flex-1 truncate">{relayUrl}</div> <div class="flex-1 truncate">{relayUrl}</div>
<button class="h-3 w-3 shrink-0" onClick={() => removeRelay(relayUrl)}> <button class="size-3 shrink-0" onClick={() => removeRelay(relayUrl)}>
<XMark /> <XMark />
</button> </button>
</li> </li>
@@ -178,18 +178,18 @@ const RelayConfig = () => {
</For> </For>
</ul> </ul>
</Section> </Section>
<Section title={i18n()('config.relays.importRelays')}> <Section title={i18n.t('config.relays.importRelays')}>
<button <button
type="button" type="button"
class="rounded bg-primary p-2 font-bold text-primary-fg" class="rounded bg-primary p-2 font-bold text-primary-fg"
onClick={() => { onClick={() => {
importFromNIP07().catch((err) => { importFromNIP07().catch((err) => {
console.error('failed to import relays', err); console.error('failed to import relays', err);
window.alert(i18n()('config.relays.failedToImport')); window.alert(i18n.t('config.relays.failedToImport'));
}); });
}} }}
> >
{i18n()('config.relays.importFromExtension')} {i18n.t('config.relays.importFromExtension')}
</button> </button>
</Section> </Section>
</> </>
@@ -213,7 +213,7 @@ const ColorThemeConfig = () => {
}; };
return ( return (
<Section title={i18n()('config.display.colorTheme')}> <Section title={i18n.t('config.display.colorTheme')}>
<div class="scrollbar flex flex-col overflow-y-auto rounded-md border border-border"> <div class="scrollbar flex flex-col overflow-y-auto rounded-md border border-border">
<For each={Object.values(colorThemes)}> <For each={Object.values(colorThemes)}>
{(colorTheme) => ( {(colorTheme) => (
@@ -247,18 +247,18 @@ const DateFormatConfig = () => {
}[] = [ }[] = [
{ {
id: 'relative', id: 'relative',
name: i18n()('config.display.relativeTimeNotation'), name: i18n.t('config.display.relativeTimeNotation'),
example: i18n()('config.display.relativeTimeNotationExample'), example: i18n.t('config.display.relativeTimeNotationExample'),
}, },
{ {
id: 'absolute-short', id: 'absolute-short',
name: i18n()('config.display.absoluteTimeNotationShort'), name: i18n.t('config.display.absoluteTimeNotationShort'),
example: i18n()('config.display.absoluteTimeNotationShortExample'), example: i18n.t('config.display.absoluteTimeNotationShortExample'),
}, },
{ {
id: 'absolute-long', id: 'absolute-long',
name: i18n()('config.display.absoluteTimeNotationLong'), name: i18n.t('config.display.absoluteTimeNotationLong'),
example: i18n()('config.display.absoluteTimeNotationLongExample'), example: i18n.t('config.display.absoluteTimeNotationLongExample'),
}, },
]; ];
@@ -267,7 +267,7 @@ const DateFormatConfig = () => {
}; };
return ( return (
<Section title={i18n()('config.display.timeNotation')}> <Section title={i18n.t('config.display.timeNotation')}>
<div class="flex flex-col justify-evenly gap-2 sm:flex-row"> <div class="flex flex-col justify-evenly gap-2 sm:flex-row">
<For each={dateFormats}> <For each={dateFormats}>
{({ id, name, example }) => ( {({ id, name, example }) => (
@@ -313,17 +313,17 @@ const ReactionConfig = () => {
}; };
return ( return (
<Section title={i18n()('config.display.reaction')}> <Section title={i18n.t('config.display.reaction')}>
<div class="flex flex-col justify-evenly gap-2"> <div class="flex flex-col justify-evenly gap-2">
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1">{i18n()('config.display.enableEmojiReaction')}</div> <div class="flex-1">{i18n.t('config.display.enableEmojiReaction')}</div>
<ToggleButton <ToggleButton
value={config().useEmojiReaction} value={config().useEmojiReaction}
onClick={() => toggleUseEmojiReaction()} onClick={() => toggleUseEmojiReaction()}
/> />
</div> </div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1">{i18n()('config.display.showEmojiReaction')}</div> <div class="flex-1">{i18n.t('config.display.showEmojiReaction')}</div>
<ToggleButton <ToggleButton
value={config().showEmojiReaction} value={config().showEmojiReaction}
onClick={() => toggleShowEmojiReaction()} onClick={() => toggleShowEmojiReaction()}
@@ -350,10 +350,10 @@ const EmojiConfig = () => {
}; };
return ( return (
<Section title={i18n()('config.customEmoji.customEmoji')}> <Section title={i18n.t('config.customEmoji.customEmoji')}>
<form class="flex flex-col gap-2" onSubmit={handleClickSaveEmoji}> <form class="flex flex-col gap-2" onSubmit={handleClickSaveEmoji}>
<label class="flex flex-1 items-center gap-1"> <label class="flex flex-1 items-center gap-1">
<div class="w-9">{i18n()('config.customEmoji.shortcode')}</div> <div class="w-9">{i18n.t('config.customEmoji.shortcode')}</div>
<input <input
class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary" class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary"
type="text" type="text"
@@ -366,7 +366,7 @@ const EmojiConfig = () => {
/> />
</label> </label>
<label class="flex flex-1 items-center gap-1"> <label class="flex flex-1 items-center gap-1">
<div class="w-9">{i18n()('config.customEmoji.url')}</div> <div class="w-9">{i18n.t('config.customEmoji.url')}</div>
<input <input
class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary" class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary"
type="text" type="text"
@@ -382,7 +382,7 @@ const EmojiConfig = () => {
type="submit" type="submit"
class="w-24 self-end rounded bg-primary p-2 font-bold text-primary-fg" class="w-24 self-end rounded bg-primary p-2 font-bold text-primary-fg"
> >
{i18n()('config.customEmoji.addEmoji')} {i18n.t('config.customEmoji.addEmoji')}
</button> </button>
</form> </form>
<ul class="mt-4 flex max-h-[40vh] min-h-64 flex-wrap overflow-y-auto border-t border-border"> <ul class="mt-4 flex max-h-[40vh] min-h-64 flex-wrap overflow-y-auto border-t border-border">
@@ -401,7 +401,7 @@ const EmojiConfig = () => {
class="w-full px-2 py-1 text-danger" class="w-full px-2 py-1 text-danger"
onClick={() => removeEmoji(shortcode)} onClick={() => removeEmoji(shortcode)}
> >
{i18n()('config.customEmoji.removeEmoji')} {i18n.t('config.customEmoji.removeEmoji')}
</button> </button>
</div> </div>
</div> </div>
@@ -415,7 +415,7 @@ const EmojiConfig = () => {
class="flex w-full flex-col items-center gap-1 rounded p-2 hover:bg-bg-tertiary/20 hover:shadow" class="flex w-full flex-col items-center gap-1 rounded p-2 hover:bg-bg-tertiary/20 hover:shadow"
onClick={() => popup.open()} onClick={() => popup.open()}
> >
<LazyLoad fallback={<div class="h-8 w-8" />}> <LazyLoad fallback={<div class="size-8" />}>
{() => ( {() => (
<div class="flex h-8 max-w-8 items-center"> <div class="flex h-8 max-w-8 items-center">
<img class="object-contain" src={url} alt={shortcode} /> <img class="object-contain" src={url} alt={shortcode} />
@@ -456,8 +456,8 @@ const EmojiImport = () => {
}; };
return ( return (
<Section title={i18n()('config.customEmoji.emojiImport')}> <Section title={i18n.t('config.customEmoji.emojiImport')}>
<p>{i18n()('config.customEmoji.emojiImportDescription')}</p> <p>{i18n.t('config.customEmoji.emojiImportDescription')}</p>
<form class="flex flex-col gap-2" onSubmit={handleClickSaveEmoji}> <form class="flex flex-col gap-2" onSubmit={handleClickSaveEmoji}>
<textarea <textarea
class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary" class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary"
@@ -470,7 +470,7 @@ const EmojiImport = () => {
type="submit" type="submit"
class="w-24 self-end rounded bg-primary p-2 font-bold text-primary-fg" class="w-24 self-end rounded bg-primary p-2 font-bold text-primary-fg"
> >
{i18n()('config.customEmoji.importEmoji')} {i18n.t('config.customEmoji.importEmoji')}
</button> </button>
</form> </form>
</Section> </Section>
@@ -492,7 +492,7 @@ const MuteConfig = () => {
return ( return (
<> <>
<Section title={i18n()('config.mute.mutedUsers')} initialOpened={false}> <Section title={i18n.t('config.mute.mutedUsers')} initialOpened={false}>
<ul class="flex max-h-[50vh] min-h-64 flex-col overflow-y-auto"> <ul class="flex max-h-[50vh] min-h-64 flex-col overflow-y-auto">
<For each={config().mutedPubkeys}> <For each={config().mutedPubkeys}>
{(pubkey) => ( {(pubkey) => (
@@ -500,7 +500,7 @@ const MuteConfig = () => {
<div class="flex-1 truncate"> <div class="flex-1 truncate">
<LazyLoad>{() => <UserNameDisplay pubkey={pubkey} />}</LazyLoad> <LazyLoad>{() => <UserNameDisplay pubkey={pubkey} />}</LazyLoad>
</div> </div>
<button class="h-3 w-3 shrink-0" onClick={() => removeMutedPubkey(pubkey)}> <button class="size-3 shrink-0" onClick={() => removeMutedPubkey(pubkey)}>
<XMark /> <XMark />
</button> </button>
</li> </li>
@@ -508,7 +508,7 @@ const MuteConfig = () => {
</For> </For>
</ul> </ul>
</Section> </Section>
<Section title={i18n()('config.mute.mutedKeywords')} initialOpened={false}> <Section title={i18n.t('config.mute.mutedKeywords')} initialOpened={false}>
<form class="flex gap-2" onSubmit={handleClickAddKeyword}> <form class="flex gap-2" onSubmit={handleClickAddKeyword}>
<input <input
class="flex-1 rounded-md border border-border bg-bg ring-border focus:border-border focus:ring-primary" class="flex-1 rounded-md border border-border bg-bg ring-border focus:border-border focus:ring-primary"
@@ -518,7 +518,7 @@ const MuteConfig = () => {
onChange={(ev) => setKeywordInput(ev.currentTarget.value)} onChange={(ev) => setKeywordInput(ev.currentTarget.value)}
/> />
<button type="submit" class="rounded bg-primary p-2 font-bold text-primary-fg"> <button type="submit" class="rounded bg-primary p-2 font-bold text-primary-fg">
{i18n()('config.mute.add')} {i18n.t('config.mute.add')}
</button> </button>
</form> </form>
<ul class="mt-2 flex max-h-[50vh] min-h-64 flex-col overflow-y-auto border-t border-border"> <ul class="mt-2 flex max-h-[50vh] min-h-64 flex-col overflow-y-auto border-t border-border">
@@ -526,7 +526,7 @@ const MuteConfig = () => {
{(keyword) => ( {(keyword) => (
<li class="flex items-center border-b border-border pr-4"> <li class="flex items-center border-b border-border pr-4">
<div class="flex-1 truncate">{keyword}</div> <div class="flex-1 truncate">{keyword}</div>
<button class="h-3 w-3 shrink-0" onClick={() => removeMutedKeyword(keyword)}> <button class="size-3 shrink-0" onClick={() => removeMutedKeyword(keyword)}>
<XMark /> <XMark />
</button> </button>
</li> </li>
@@ -553,8 +553,8 @@ const EmbeddingConfig = () => {
}; };
return ( return (
<Section title={i18n()('config.display.embedding')}> <Section title={i18n.t('config.display.embedding')}>
<p>{i18n()('config.display.embeddingDescription')}</p> <p>{i18n.t('config.display.embeddingDescription')}</p>
<div class="flex flex-col justify-evenly gap-2"> <div class="flex flex-col justify-evenly gap-2">
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1">YouTube</div> <div class="flex-1">YouTube</div>
@@ -599,21 +599,21 @@ const OtherConfig = () => {
}; };
return ( return (
<Section title={i18n()('config.display.others')}> <Section title={i18n.t('config.display.others')}>
<div class="flex flex-col justify-evenly gap-2"> <div class="flex flex-col justify-evenly gap-2">
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1">{i18n()('config.display.keepOpenPostForm')}</div> <div class="flex-1">{i18n.t('config.display.keepOpenPostForm')}</div>
<ToggleButton <ToggleButton
value={config().keepOpenPostForm} value={config().keepOpenPostForm}
onClick={() => toggleKeepOpenPostForm()} onClick={() => toggleKeepOpenPostForm()}
/> />
</div> </div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1">{i18n()('config.display.showMediaByDefault')}</div> <div class="flex-1">{i18n.t('config.display.showMediaByDefault')}</div>
<ToggleButton value={config().showMedia} onClick={() => toggleShowMedia()} /> <ToggleButton value={config().showMedia} onClick={() => toggleShowMedia()} />
</div> </div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1">{i18n()('config.display.hideNumbers')}</div> <div class="flex-1">{i18n.t('config.display.hideNumbers')}</div>
<ToggleButton value={config().hideCount} onClick={() => toggleHideCount()} /> <ToggleButton value={config().hideCount} onClick={() => toggleHideCount()} />
</div> </div>
{/* {/*
@@ -638,17 +638,17 @@ const ConfigUI = (props: ConfigProps) => {
const menu = [ const menu = [
{ {
name: () => i18n()('config.profile.profile'), name: () => i18n.t('config.profile.profile'),
icon: () => <User />, icon: () => <User />,
render: () => <ProfileSection />, render: () => <ProfileSection />,
}, },
{ {
name: () => i18n()('config.relays.relays'), name: () => i18n.t('config.relays.relays'),
icon: () => <ServerStack />, icon: () => <ServerStack />,
render: () => <RelayConfig />, render: () => <RelayConfig />,
}, },
{ {
name: () => i18n()('config.display.display'), name: () => i18n.t('config.display.display'),
icon: () => <PaintBrush />, icon: () => <PaintBrush />,
render: () => ( render: () => (
<> <>
@@ -661,7 +661,7 @@ const ConfigUI = (props: ConfigProps) => {
), ),
}, },
{ {
name: () => i18n()('config.customEmoji.customEmoji'), name: () => i18n.t('config.customEmoji.customEmoji'),
icon: () => <FaceSmile />, icon: () => <FaceSmile />,
render: () => ( render: () => (
<> <>
@@ -671,7 +671,7 @@ const ConfigUI = (props: ConfigProps) => {
), ),
}, },
{ {
name: () => i18n()('config.mute.mute'), name: () => i18n.t('config.mute.mute'),
icon: () => <EyeSlash />, icon: () => <EyeSlash />,
render: () => <MuteConfig />, render: () => <MuteConfig />,
}, },
@@ -690,7 +690,7 @@ const ConfigUI = (props: ConfigProps) => {
when={getMenuItem()} when={getMenuItem()}
fallback={ fallback={
<> <>
<h2 class="flex-1 text-center text-lg font-bold">{i18n()('config.config')}</h2> <h2 class="flex-1 text-center text-lg font-bold">{i18n.t('config.config')}</h2>
<div class="flex gap-1"> <div class="flex gap-1">
<Show when={window.location.host === 'syusui-s.github.io'}> <Show when={window.location.host === 'syusui-s.github.io'}>
<button <button
@@ -703,7 +703,7 @@ const ConfigUI = (props: ConfigProps) => {
.catch(() => window.alert('failed to copy')); .catch(() => window.alert('failed to copy'));
}} }}
> >
{i18n()('config.copyToClipboard')} {i18n.t('config.copyToClipboard')}
</button> </button>
</Show> </Show>
<Show when={window.location.host === 'rabbit.syusui.net'}> <Show when={window.location.host === 'rabbit.syusui.net'}>
@@ -722,7 +722,7 @@ const ConfigUI = (props: ConfigProps) => {
} }
}} }}
> >
{i18n()('config.importFromClipboard')} {i18n.t('config.importFromClipboard')}
</button> </button>
</Show> </Show>
</div> </div>
@@ -734,7 +734,7 @@ const ConfigUI = (props: ConfigProps) => {
class="flex w-full gap-2 py-3 hover:text-primary" class="flex w-full gap-2 py-3 hover:text-primary"
onClick={() => setMenuIndex(i)} onClick={() => setMenuIndex(i)}
> >
<span class="inline-block h-6 w-6">{menuItem.icon()}</span> <span class="inline-block size-6">{menuItem.icon()}</span>
{menuItem.name()} {menuItem.name()}
</button> </button>
</li> </li>
@@ -752,7 +752,7 @@ const ConfigUI = (props: ConfigProps) => {
class="pr-4 text-fg hover:text-fg-secondary" class="pr-4 text-fg hover:text-fg-secondary"
onClick={() => setMenuIndex(null)} onClick={() => setMenuIndex(null)}
> >
<span class="inline-block h-6 w-6"> <span class="inline-block size-6">
<ArrowLeft /> <ArrowLeft />
</span> </span>
</button> </button>

View File

@@ -151,12 +151,12 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
if ( if (
(latest.data() == null || latest.followingPubkeys().length === 0) && (latest.data() == null || latest.followingPubkeys().length === 0) &&
!window.confirm(i18n()('profile.confirmUpdateEvenIfEmpty')) !window.confirm(i18n.t('profile.confirmUpdateEvenIfEmpty'))
) )
return; return;
if ((latest?.data()?.created_at ?? 0) < (myFollowingQuery.data?.created_at ?? 0)) { if ((latest?.data()?.created_at ?? 0) < (myFollowingQuery.data?.created_at ?? 0)) {
window.alert(i18n()('profile.failedToFetchLatestFollowList')); window.alert(i18n.t('profile.failedToFetchLatestFollowList'));
return; return;
} }
@@ -182,7 +182,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
}); });
} catch (err) { } catch (err) {
console.error('failed to update contact list', err); console.error('failed to update contact list', err);
window.alert(i18n()('profile.failedToUpdateFollowList')); window.alert(i18n.t('profile.failedToUpdateFollowList'));
} finally { } finally {
setUpdatingContacts(false); setUpdatingContacts(false);
} }
@@ -195,7 +195,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
}; };
const unfollow = () => { const unfollow = () => {
if (!window.confirm(i18n()('profile.confirmUnfollow'))) return; if (!window.confirm(i18n.t('profile.confirmUnfollow'))) return;
updateContacts('unfollow', props.pubkey).catch((err) => { updateContacts('unfollow', props.pubkey).catch((err) => {
console.log('failed to unfollow', err); console.log('failed to unfollow', err);
@@ -213,13 +213,13 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
}, },
*/ */
{ {
content: i18n()('profile.copyPubkey'), content: i18n.t('profile.copyPubkey'),
onSelect: () => { onSelect: () => {
navigator.clipboard.writeText(npub()).catch((err) => window.alert(err)); navigator.clipboard.writeText(npub()).catch((err) => window.alert(err));
}, },
}, },
{ {
content: i18n()('profile.addUserColumn'), content: i18n.t('profile.addUserColumn'),
onSelect: () => { onSelect: () => {
const columnName = profile()?.name ?? npub(); const columnName = profile()?.name ?? npub();
saveColumn(createPostsColumn({ name: columnName, pubkey: props.pubkey })); saveColumn(createPostsColumn({ name: columnName, pubkey: props.pubkey }));
@@ -228,16 +228,16 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
}, },
}, },
{ {
content: i18n()('profile.addUserHomeColumn'), content: i18n.t('profile.addUserHomeColumn'),
onSelect: () => { onSelect: () => {
const columnName = `${i18n()('column.home')} / ${profile()?.name ?? npub()}`; const columnName = `${i18n.t('column.home')} / ${profile()?.name ?? npub()}`;
saveColumn(createFollowingColumn({ name: columnName, pubkey: props.pubkey })); saveColumn(createFollowingColumn({ name: columnName, pubkey: props.pubkey }));
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err)); request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
props.onClose?.(); props.onClose?.();
}, },
}, },
{ {
content: !isMuted() ? i18n()('profile.mute') : i18n()('profile.unmute'), content: !isMuted() ? i18n.t('profile.mute') : i18n.t('profile.unmute'),
onSelect: () => { onSelect: () => {
if (!isMuted()) { if (!isMuted()) {
addMutedPubkey(props.pubkey); addMutedPubkey(props.pubkey);
@@ -248,7 +248,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
}, },
{ {
when: () => props.pubkey === myPubkey(), when: () => props.pubkey === myPubkey(),
content: !following() ? i18n()('profile.followMyself') : i18n()('profile.unfollowMyself'), content: !following() ? i18n.t('profile.followMyself') : i18n.t('profile.unfollowMyself'),
onSelect: () => { onSelect: () => {
if (!following()) { if (!following()) {
follow(); follow();
@@ -282,16 +282,16 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
> >
{(bannerUrl) => ( {(bannerUrl) => (
<div class="h-40 w-full shrink-0 sm:h-52"> <div class="h-40 w-full shrink-0 sm:h-52">
<img src={bannerUrl} alt="header" class="h-full w-full object-cover" /> <img src={bannerUrl} alt="header" class="size-full object-cover" />
</div> </div>
)} )}
</Show> </Show>
<div class="mt-[-54px] flex items-end gap-4 px-4 pt-4"> <div class="mt-[-54px] flex items-end gap-4 px-4 pt-4">
<div class="flex-1 shrink-0"> <div class="flex-1 shrink-0">
<div class="h-28 w-28 overflow-hidden rounded-lg shadow-md"> <div class="size-28 overflow-hidden rounded-lg shadow-md">
<Show when={profileQuery.isFetched && profile()?.picture} keyed> <Show when={profileQuery.isFetched && profile()?.picture} keyed>
{(pictureUrl) => ( {(pictureUrl) => (
<img src={pictureUrl} alt="user icon" class="h-full w-full object-cover" /> <img src={pictureUrl} alt="user icon" class="size-full object-cover" />
)} )}
</Show> </Show>
</div> </div>
@@ -306,17 +306,17 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
text-center font-bold text-primary hover:bg-primary hover:text-primary-fg sm:w-20" text-center font-bold text-primary hover:bg-primary hover:text-primary-fg sm:w-20"
onClick={() => showProfileEdit()} onClick={() => showProfileEdit()}
> >
{i18n()('profile.editProfile')} {i18n.t('profile.editProfile')}
</button> </button>
</Match> </Match>
<Match when={updateContactsMutation.isPending || updatingContacts()}> <Match when={updateContactsMutation.isPending || updatingContacts()}>
<span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base"> <span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base">
{i18n()('general.updating')} {i18n.t('general.updating')}
</span> </span>
</Match> </Match>
<Match when={myFollowingQuery.isPending || myFollowingQuery.isFetching}> <Match when={myFollowingQuery.isPending || myFollowingQuery.isFetching}>
<span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base"> <span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base">
{i18n()('general.loading')} {i18n.t('general.loading')}
</span> </span>
</Match> </Match>
<Match when={following()}> <Match when={following()}>
@@ -327,8 +327,8 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
onClick={() => unfollow()} onClick={() => unfollow()}
disabled={updateContactsMutation.isPending} disabled={updateContactsMutation.isPending}
> >
<Show when={!hoverFollowButton()} fallback={i18n()('profile.unfollow')}> <Show when={!hoverFollowButton()} fallback={i18n.t('profile.unfollow')}>
{i18n()('profile.followingCurrently')} {i18n.t('profile.followingCurrently')}
</Show> </Show>
</button> </button>
</Match> </Match>
@@ -338,7 +338,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
onClick={() => follow()} onClick={() => follow()}
disabled={updateContactsMutation.isPending} disabled={updateContactsMutation.isPending}
> >
{i18n()('profile.follow')} {i18n.t('profile.follow')}
</button> </button>
</Match> </Match>
</Switch> </Switch>
@@ -354,10 +354,10 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</div> </div>
<Switch> <Switch>
<Match when={userFollowingQuery.isPending}> <Match when={userFollowingQuery.isPending}>
<div class="shrink-0 text-xs">{i18n()('general.loading')}</div> <div class="shrink-0 text-xs">{i18n.t('general.loading')}</div>
</Match> </Match>
<Match when={followed()}> <Match when={followed()}>
<div class="shrink-0 text-xs">{i18n()('profile.followsYou')}</div> <div class="shrink-0 text-xs">{i18n.t('profile.followsYou')}</div>
</Match> </Match>
</Switch> </Switch>
</div> </div>
@@ -365,7 +365,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</div> </div>
<div class="flex items-start px-4 pt-2"> <div class="flex items-start px-4 pt-2">
<div class="h-16 shrink overflow-hidden"> <div class="h-16 shrink overflow-hidden">
<Show when={profileQuery.isPending}>{i18n()('general.loading')}</Show> <Show when={profileQuery.isPending}>{i18n.t('general.loading')}</Show>
<Show when={(profile()?.display_name?.length ?? 0) > 0}> <Show when={(profile()?.display_name?.length ?? 0) > 0}>
<div class="truncate text-xl font-bold">{profile()?.display_name}</div> <div class="truncate text-xl font-bold">{profile()?.display_name}</div>
</Show> </Show>
@@ -378,18 +378,18 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
{nip05Identifier()?.ident} {nip05Identifier()?.ident}
<Switch <Switch
fallback={ fallback={
<span class="inline-block h-4 w-4 text-danger"> <span class="inline-block size-4 text-danger">
<ExclamationCircle /> <ExclamationCircle />
</span> </span>
} }
> >
<Match when={verificationQuery.isPending}> <Match when={verificationQuery.isPending}>
<span class="inline-block h-3 w-3"> <span class="inline-block size-3">
<ArrowPath /> <ArrowPath />
</span> </span>
</Match> </Match>
<Match when={isVerified()}> <Match when={isVerified()}>
<span class="inline-block h-4 w-4 text-link"> <span class="inline-block size-4 text-link">
<CheckCircle /> <CheckCircle />
</span> </span>
</Match> </Match>
@@ -411,11 +411,11 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</Show> </Show>
<div class="flex border-t border-border px-4 py-2"> <div class="flex border-t border-border px-4 py-2">
<button class="flex flex-1 flex-col items-start" onClick={() => setModal('Following')}> <button class="flex flex-1 flex-col items-start" onClick={() => setModal('Following')}>
<div class="text-sm">{i18n()('profile.following')}</div> <div class="text-sm">{i18n.t('profile.following')}</div>
<div class="text-xl"> <div class="text-xl">
<Show <Show
when={userFollowingQuery.isFetched} when={userFollowingQuery.isFetched}
fallback={<span class="text-sm">{i18n()('general.loading')}</span>} fallback={<span class="text-sm">{i18n.t('general.loading')}</span>}
> >
{userFollowingPubkeys().length} {userFollowingPubkeys().length}
</Show> </Show>
@@ -423,7 +423,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</button> </button>
<Show when={!config().hideCount}> <Show when={!config().hideCount}>
<div class="flex flex-1 flex-col items-start"> <div class="flex flex-1 flex-col items-start">
<div class="text-sm">{i18n()('profile.followers')}</div> <div class="text-sm">{i18n.t('profile.followers')}</div>
<div class="text-xl"> <div class="text-xl">
<Show <Show
when={showFollowers()} when={showFollowers()}
@@ -432,7 +432,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
class="text-sm hover:text-fg-secondary" class="text-sm hover:text-fg-secondary"
onClick={() => setShowFollowers(true)} onClick={() => setShowFollowers(true)}
> >
{i18n()('profile.loadFollowers')} {i18n.t('profile.loadFollowers')}
</button> </button>
} }
keyed keyed
@@ -448,7 +448,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
<Show when={profile()?.website} keyed> <Show when={profile()?.website} keyed>
{(website) => ( {(website) => (
<li class="flex items-center gap-1"> <li class="flex items-center gap-1">
<span class="inline-block h-4 w-4" area-label="website" title="website"> <span class="inline-block size-4" area-label="website" title="website">
<GlobeAlt /> <GlobeAlt />
</span> </span>
<SafeLink class="text-link underline" href={website} /> <SafeLink class="text-link underline" href={website} />

View File

@@ -58,11 +58,11 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
const succeeded = results.filter((res) => res.status === 'fulfilled').length; const succeeded = results.filter((res) => res.status === 'fulfilled').length;
const failed = results.length - succeeded; const failed = results.length - succeeded;
if (succeeded === results.length) { if (succeeded === results.length) {
window.alert(i18n()('profile.edit.updateSucceeded')); window.alert(i18n.t('profile.edit.updateSucceeded'));
} else if (succeeded > 0) { } else if (succeeded > 0) {
window.alert(i18n()('profile.edit.failedToUpdatePartially', { count: failed })); window.alert(i18n.t('profile.edit.failedToUpdatePartially', { count: failed }));
} else { } else {
window.alert(i18n()('profile.edit.failedToUpdate')); window.alert(i18n.t('profile.edit.failedToUpdate'));
} }
invalidateProfile() invalidateProfile()
.then(() => query.refetch()) .then(() => query.refetch())
@@ -149,23 +149,23 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
<div> <div>
<Show when={banner().length > 0} fallback={<div class="h-24 shrink-0" />} keyed> <Show when={banner().length > 0} fallback={<div class="h-24 shrink-0" />} keyed>
<div class="h-40 w-full shrink-0 sm:h-52"> <div class="h-40 w-full shrink-0 sm:h-52">
<img src={banner()} alt="header" class="h-full w-full object-cover" /> <img src={banner()} alt="header" class="size-full object-cover" />
</div> </div>
</Show> </Show>
<div class="ml-4 mt-[-64px] h-28 w-28 rounded-lg shadow-md"> <div class="ml-4 mt-[-64px] size-28 rounded-lg shadow-md">
<Show when={picture().length > 0}> <Show when={picture().length > 0}>
<img src={picture()} alt="user icon" class="h-full w-full rounded-lg object-cover" /> <img src={picture()} alt="user icon" class="size-full rounded-lg object-cover" />
</Show> </Show>
</div> </div>
</div> </div>
<Show when={loading()}> <Show when={loading()}>
<div class="px-4 pt-4">{i18n()('general.loading')}</div> <div class="px-4 pt-4">{i18n.t('general.loading')}</div>
</Show> </Show>
<div> <div>
<form class="flex flex-col gap-4 p-4" onSubmit={handleSubmit}> <form class="flex flex-col gap-4 p-4" onSubmit={handleSubmit}>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="picture"> <label class="font-bold" for="picture">
{i18n()('profile.edit.icon')} {i18n.t('profile.edit.icon')}
</label> </label>
<input <input
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
@@ -181,7 +181,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="picture"> <label class="font-bold" for="picture">
{i18n()('profile.edit.banner')} {i18n.t('profile.edit.banner')}
</label> </label>
<input <input
class="w-full rounded-md border-border bg-bg focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg focus:border-border focus:ring-primary"
@@ -197,7 +197,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name"> <label class="font-bold" for="name">
{i18n()('profile.edit.name')} {i18n.t('profile.edit.name')}
</label> </label>
<div class="flex w-full items-center gap-2"> <div class="flex w-full items-center gap-2">
<span>@</span> <span>@</span>
@@ -217,7 +217,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name"> <label class="font-bold" for="name">
{i18n()('profile.edit.displayName')} {i18n.t('profile.edit.displayName')}
</label> </label>
<input <input
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
@@ -232,7 +232,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name"> <label class="font-bold" for="name">
{i18n()('profile.edit.about')} {i18n.t('profile.edit.about')}
</label> </label>
<textarea <textarea
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
@@ -245,7 +245,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name"> <label class="font-bold" for="name">
{i18n()('profile.edit.website')} {i18n.t('profile.edit.website')}
</label> </label>
<input <input
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
@@ -260,7 +260,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name"> <label class="font-bold" for="name">
{i18n()('profile.edit.nip05')} {i18n.t('profile.edit.nip05')}
</label> </label>
<input <input
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
@@ -276,9 +276,9 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name"> <label class="font-bold" for="name">
{i18n()('profile.edit.lightningAddress')} {i18n.t('profile.edit.lightningAddress')}
</label> </label>
<span class="text-xs">{i18n()('profile.edit.lightningAddressDescription')}</span> <span class="text-xs">{i18n.t('profile.edit.lightningAddressDescription')}</span>
<input <input
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary" class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
type="text" type="text"
@@ -293,7 +293,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div> </div>
<Show when={Object.entries(otherProperties()).length > 0}> <Show when={Object.entries(otherProperties()).length > 0}>
<div> <div>
<span class="font-bold">{i18n()('profile.edit.otherProperties')}</span> <span class="font-bold">{i18n.t('profile.edit.otherProperties')}</span>
<div> <div>
<For each={Object.entries(otherProperties())}> <For each={Object.entries(otherProperties())}>
{([key, value]) => ( {([key, value]) => (
@@ -316,17 +316,17 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
}} }}
disabled={mutation.isPending} disabled={mutation.isPending}
> >
{i18n()('profile.edit.save')} {i18n.t('profile.edit.save')}
</button> </button>
<button <button
type="button" type="button"
class="rounded border border-primary p-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover" class="rounded border border-primary p-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover"
onClick={() => props.onClose()} onClick={() => props.onClose()}
> >
{i18n()('profile.edit.cancel')} {i18n.t('profile.edit.cancel')}
</button> </button>
</div> </div>
<Show when={mutation.isPending}>{i18n()('profile.edit.updating')}</Show> <Show when={mutation.isPending}>{i18n.t('profile.edit.updating')}</Show>
</form> </form>
</div> </div>
</BasicModal> </BasicModal>

View File

@@ -143,10 +143,10 @@ const InvoiceDisplay: Component<{ invoice: string; event: NostrEvent; nostrPubke
when={!zapped()} when={!zapped()}
fallback={ fallback={
<div class="flex flex-col items-center gap-4 py-8"> <div class="flex flex-col items-center gap-4 py-8">
<span class="inline-block h-28 w-28 rounded-full border-4 border-primary p-4 text-primary"> <span class="inline-block size-28 rounded-full border-4 border-primary p-4 text-primary">
<Check /> <Check />
</span> </span>
<div class="text-secondary text-xl">{i18n()('zap.completed')}</div> <div class="text-secondary text-xl">{i18n.t('zap.completed')}</div>
</div> </div>
} }
> >
@@ -158,7 +158,7 @@ const InvoiceDisplay: Component<{ invoice: string; event: NostrEvent; nostrPubke
class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover" class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover"
href={lightingInvoice()} href={lightingInvoice()}
> >
{i18n()('zap.sendViaWallet')} {i18n.t('zap.sendViaWallet')}
</a> </a>
<Show when={webln.status() === 'available'}> <Show when={webln.status() === 'available'}>
<button <button
@@ -166,7 +166,7 @@ const InvoiceDisplay: Component<{ invoice: string; event: NostrEvent; nostrPubke
class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover" class="inline-block rounded bg-primary p-4 font-bold text-primary-fg hover:bg-primary-hover"
onClick={handleClickWebLN} onClick={handleClickWebLN}
> >
{i18n()('zap.sendViaWebLN')} {i18n.t('zap.sendViaWebLN')}
</button> </button>
</Show> </Show>
</div> </div>
@@ -274,19 +274,19 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
return ( return (
<Switch> <Switch>
<Match when={query.isError}> <Match when={query.isError}>
{i18n()('zap.fetchingLnUrlEndpointError')}: {query?.error?.message} {i18n.t('zap.fetchingLnUrlEndpointError')}: {query?.error?.message}
</Match> </Match>
<Match when={error()} keyed> <Match when={error()} keyed>
{(err) => ( {(err) => (
<> <>
{i18n()('zap.lnUrlEndpointError')}: {err.reason} {i18n.t('zap.lnUrlEndpointError')}: {err.reason}
</> </>
)} )}
</Match> </Match>
<Match when={query.isFetching}>{i18n()('zap.fetchingLnUrlEndpoint')}</Match> <Match when={query.isFetching}>{i18n.t('zap.fetchingLnUrlEndpoint')}</Match>
<Match when={getInvoiceMutation.isPending}>{i18n()('zap.fetchingLnUrlInvoice')}</Match> <Match when={getInvoiceMutation.isPending}>{i18n.t('zap.fetchingLnUrlInvoice')}</Match>
<Match when={getInvoiceMutation.isError}> <Match when={getInvoiceMutation.isError}>
{i18n()('zap.fetchingLnUrlInvoiceError')}: {getInvoiceMutation?.error?.message} {i18n.t('zap.fetchingLnUrlInvoiceError')}: {getInvoiceMutation?.error?.message}
</Match> </Match>
<Match when={getInvoiceMutation.isSuccess && getInvoiceMutation.data} keyed> <Match when={getInvoiceMutation.isSuccess && getInvoiceMutation.data} keyed>
{(invoice) => ( {(invoice) => (
@@ -300,10 +300,10 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
<Match when={query.isSuccess}> <Match when={query.isSuccess}>
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">
<Show when={!allowsNostr()}> <Show when={!allowsNostr()}>
<div class="pb-8 text-center">{i18n()('zap.lnurlServiceDoesNotAllowNostr')}</div> <div class="pb-8 text-center">{i18n.t('zap.lnurlServiceDoesNotAllowNostr')}</div>
</Show> </Show>
<Show when={hasZapTag()}> <Show when={hasZapTag()}>
<div class="pb-8 text-center">{i18n()('zap.zapSplitIsNotSupported')}</div> <div class="pb-8 text-center">{i18n.t('zap.zapSplitIsNotSupported')}</div>
</Show> </Show>
<div class="flex flex-col items-center overflow-hidden rounded px-8 py-2 text-fg-secondary"> <div class="flex flex-col items-center overflow-hidden rounded px-8 py-2 text-fg-secondary">
<Show when={lnurlServiceIcon()} keyed> <Show when={lnurlServiceIcon()} keyed>
@@ -342,7 +342,7 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
name="comment" name="comment"
class="w-full rounded-md border border-border bg-bg ring-border placeholder:text-fg-secondary focus:border-border focus:ring-primary" class="w-full rounded-md border border-border bg-bg ring-border placeholder:text-fg-secondary focus:border-border focus:ring-primary"
maxLength={commentAllowed() > 0 ? commentAllowed() : 70} maxLength={commentAllowed() > 0 ? commentAllowed() : 70}
placeholder={i18n()('zap.comment')} placeholder={i18n.t('zap.comment')}
disabled={query.isPending} disabled={query.isPending}
value={comment()} value={comment()}
onChange={(ev) => setComment(ev.target.value)} onChange={(ev) => setComment(ev.target.value)}
@@ -352,7 +352,7 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
class="flex w-full items-center justify-center rounded bg-primary py-4 text-primary-fg hover:bg-primary-hover" class="flex w-full items-center justify-center rounded bg-primary py-4 text-primary-fg hover:bg-primary-hover"
disabled={getInvoiceMutation.isPending} disabled={getInvoiceMutation.isPending}
> >
<span class="inline-block h-6 w-6"> <span class="inline-block size-6">
<Bolt /> <Bolt />
</span> </span>
</button> </button>
@@ -379,7 +379,7 @@ const ZapRequestModal: Component<ZapRequestModalProps> = (props) => {
return ( return (
<BasicModal onClose={props.onClose}> <BasicModal onClose={props.onClose}>
<div class="p-8"> <div class="p-8">
<Show when={isZapConfigured()} fallback={i18n()('zap.userDidNotConfigureZap')}> <Show when={isZapConfigured()} fallback={i18n.t('zap.userDidNotConfigureZap')}>
<Show when={lud06() != null && lud16() != null}> <Show when={lud06() != null && lud16() != null}>
<div class="flex justify-center gap-3 pb-2"> <div class="flex justify-center gap-3 pb-2">
<button <button
@@ -393,7 +393,7 @@ const ZapRequestModal: Component<ZapRequestModalProps> = (props) => {
}} }}
onClick={() => setLnurlSource('lud06')} onClick={() => setLnurlSource('lud06')}
> >
{i18n()('zap.lud06')} {i18n.t('zap.lud06')}
</button> </button>
<button <button
type="button" type="button"
@@ -406,7 +406,7 @@ const ZapRequestModal: Component<ZapRequestModalProps> = (props) => {
}} }}
onClick={() => setLnurlSource('lud16')} onClick={() => setLnurlSource('lud16')}
> >
{i18n()('zap.lud16')} {i18n.t('zap.lud16')}
</button> </button>
</div> </div>
</Show> </Show>

View File

@@ -237,8 +237,8 @@ const useConfig = (): UseConfig => {
const columns: ColumnType[] = [ const columns: ColumnType[] = [
createFollowingColumn({ width: 'widest', pubkey }), createFollowingColumn({ width: 'widest', pubkey }),
createNotificationColumn({ pubkey }), createNotificationColumn({ pubkey }),
createPostsColumn({ name: i18n()('column.myPosts'), pubkey }), createPostsColumn({ name: i18n.t('column.myPosts'), pubkey }),
createReactionsColumn({ name: i18n()('column.myReactions'), pubkey }), createReactionsColumn({ name: i18n.t('column.myReactions'), pubkey }),
]; ];
if (navigator.language.includes('ja')) { if (navigator.language.includes('ja')) {

View File

@@ -4,7 +4,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import en from '@/locales/en'; import en from '@/locales/en';
import ja from '@/locales/ja'; import ja from '@/locales/ja';
const i18nextInstance = (): Promise<void | typeof i18next.t> => const i18nextInstance = () =>
i18next i18next
.use(LanguageDetector) .use(LanguageDetector)
.init({ .init({

View File

@@ -2,38 +2,37 @@ import { Component, JSX, createContext, createEffect, createSignal, useContext }
import i18next from 'i18next'; import i18next from 'i18next';
type I18Next = typeof i18next.t; type I18Next = typeof i18next;
export type I18NextProviderProps = { export type I18NextProviderProps = {
i18next: I18Next | Promise<I18Next | void> | void; i18next: I18Next | Promise<I18Next | void> | void;
children?: JSX.Element; children?: JSX.Element;
}; };
const I18NextContext = createContext<I18Next | Promise<I18Next | void> | void>(); const I18NextContext = createContext<I18Next>(i18next);
export const useTranslation = () => { export const useTranslation = () => useContext<I18Next>(I18NextContext);
const [i18nextFn, setI18nextFn] = createSignal<I18Next>(i18next.t);
const maybePromise = useContext(I18NextContext); export const I18NextProvider: Component<I18NextProviderProps> = (props) => {
const [i18nextInstance, setI18nextInstance] = createSignal<I18Next>(i18next);
createEffect(() => { createEffect(() => {
if (maybePromise instanceof Promise) { if (props.i18next instanceof Promise) {
maybePromise props.i18next
.then((instance) => { .then((instance) => {
if (instance != null) { if (instance != null) {
setI18nextFn(() => instance); setI18nextInstance(() => instance);
} }
}) })
.catch((err) => { .catch((err) => {
console.error('failed to initialize i18next', err); console.error('failed to initialize i18n.text', err);
}); });
} else if (maybePromise != null) { } else if (props.i18next != null) {
setI18nextFn(() => maybePromise); setI18nextInstance(props.i18next);
} }
}); });
return i18nextFn; return (
}; <I18NextContext.Provider value={i18nextInstance()}>{props.children}</I18NextContext.Provider>
export const I18NextProvider: Component<I18NextProviderProps> = (props) => (
<I18NextContext.Provider value={props.i18next}>{props.children}</I18NextContext.Provider>
); );
};

View File

@@ -59,19 +59,19 @@ const Hello: Component = () => {
<div class="rounded-md p-8 shadow-md"> <div class="rounded-md p-8 shadow-md">
<Switch> <Switch>
<Match when={signerStatus() === 'checking'}> <Match when={signerStatus() === 'checking'}>
<p>{i18n()('hello.signerChecking')}</p> <p>{i18n.t('hello.signerChecking')}</p>
</Match> </Match>
<Match when={signerStatus() === 'unavailable'}> <Match when={signerStatus() === 'unavailable'}>
<h2 class="font-bold">{i18n()('hello.signerUnavailable')}</h2> <h2 class="font-bold">{i18n.t('hello.signerUnavailable')}</h2>
<p>{i18n()('hello.signerUnavailableMessage')}</p> <p>{i18n.t('hello.signerUnavailableMessage')}</p>
<SignerExtensions /> <SignerExtensions />
<div class="flex flex-col items-center gap-2"> <div class="flex flex-col items-center gap-2">
<p class="text-sm">{i18n()('hello.reloadAfterInstall')}</p> <p class="text-sm">{i18n.t('hello.reloadAfterInstall')}</p>
<button <button
class="rounded bg-primary px-4 py-2 text-sm font-bold text-primary-fg hover:bg-primary-hover" class="rounded bg-primary px-4 py-2 text-sm font-bold text-primary-fg hover:bg-primary-hover"
onClick={() => window.location.reload()} onClick={() => window.location.reload()}
> >
{i18n()('hello.reload')} {i18n.t('hello.reload')}
</button> </button>
</div> </div>
</Match> </Match>
@@ -80,7 +80,7 @@ const Hello: Component = () => {
class="rounded bg-primary p-4 text-lg font-bold text-primary-fg hover:shadow-md" class="rounded bg-primary p-4 text-lg font-bold text-primary-fg hover:shadow-md"
onClick={handleLogin} onClick={handleLogin}
> >
{i18n()('hello.loginWithSigner')} {i18n.t('hello.loginWithSigner')}
</button> </button>
</Match> </Match>
</Switch> </Switch>

View File

@@ -7,10 +7,10 @@ const NotFound: Component = () => {
return ( return (
<div class="container mx-auto max-w-[640px] py-10"> <div class="container mx-auto max-w-[640px] py-10">
<h1 class="text-4xl font-bold text-fg">{i18n()('notFound.title')}</h1> <h1 class="text-4xl font-bold text-fg">{i18n.t('notFound.title')}</h1>
<p class="pt-4"> <p class="pt-4">
<a class="text-link underline" href="/"> <a class="text-link underline" href="/">
{i18n()('notFound.back')} {i18n.t('notFound.back')}
</a> </a>
</p> </p>
</div> </div>