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

View File

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

View File

@@ -112,10 +112,10 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
const placeholder = (mode: NotePostFormProps['mode']) => {
switch (mode) {
case 'reply':
return i18n()('posting.placeholderReply');
return i18n.t('posting.placeholderReply');
case 'normal':
default:
return i18n()('posting.placeholder');
return i18n.t('posting.placeholder');
}
};
@@ -163,7 +163,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
if (failed.length > 0) {
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 (/nsec1[0-9a-zA-Z]+/.test(text())) {
window.alert(i18n()('posting.forbiddenToIncludeNsec'));
window.alert(i18n.t('posting.forbiddenToIncludeNsec'));
return;
}
@@ -353,7 +353,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
<div class="p-1">
<Show when={props.replyTo != null}>
<div>
{i18n()('posting.replyToPre')}
{i18n.t('posting.replyToPre')}
<For each={notifyPubkeys()}>
{(pubkey, index) => (
<>
@@ -362,7 +362,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
</>
)}
</For>
{i18n()('posting.replyToPost')}
{i18n.t('posting.replyToPost')}
</div>
</Show>
<form class="flex flex-col gap-1" onSubmit={handleSubmit}>
@@ -371,7 +371,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
ref={contentWarningReasonRef}
type="text"
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}
onInput={(ev) => setContentWarningReason(ev.currentTarget.value)}
value={contentWarningReason()}
@@ -406,7 +406,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
}}
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 />
</span>
</button>
@@ -442,8 +442,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
'p-[6px]': mode() === 'reply',
}}
type="button"
aria-label={i18n()('posting.contentWarning')}
title={i18n()('posting.contentWarning')}
aria-label={i18n.t('posting.contentWarning')}
title={i18n.t('posting.contentWarning')}
onClick={() => {
setContentWarning((e) => !e);
contentWarningReasonRef?.focus();
@@ -464,8 +464,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
'p-[6px]': mode() === 'reply',
}}
type="button"
title={i18n()('posting.uploadImage')}
aria-label={i18n()('posting.uploadImage')}
title={i18n.t('posting.uploadImage')}
aria-label={i18n.t('posting.uploadImage')}
disabled={fileUploadDisabled()}
onClick={() => fileInputRef?.click()}
>
@@ -482,8 +482,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
'w-7': mode() === 'reply',
}}
type="submit"
aria-label={i18n()('posting.submit')}
title={i18n()('posting.submit')}
aria-label={i18n.t('posting.submit')}
title={i18n.t('posting.submit')}
disabled={submitDisabled()}
>
<PaperAirplane />

View File

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

View File

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

View File

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

View File

@@ -73,10 +73,10 @@ const Column: Component<ColumnProps> = (props) => {
class="flex w-full items-center gap-1"
onClick={() => timelineState?.clearTimeline()}
>
<div class="inline-block h-4 w-4">
<div class="inline-block size-4">
<ArrowLeft />
</div>
<div>{i18n()('column.back')}</div>
<div>{i18n.t('column.back')}</div>
</button>
</div>
<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 (
<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">
<For each={['widest', 'wide', 'medium', 'narrow'] as const}>
{(width) => (
@@ -56,7 +56,7 @@ const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
}}
onClick={() => setColumnWidth(width)}
>
{i18n()(`column.config.${width}`)}
{i18n.t(`column.config.${width}`)}
</button>
)}
</For>
@@ -65,29 +65,29 @@ const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
<div class="flex h-10 items-center gap-2">
<button
class="py-4 pl-2"
title={i18n()('column.config.moveLeft')}
title={i18n.t('column.config.moveLeft')}
onClick={() => move(props.columnIndex - 1)}
>
<span class="inline-block h-4 w-4">
<span class="inline-block size-4">
<ChevronLeft />
</span>
</button>
<button
class="py-4 pr-2"
title={i18n()('column.config.moveRight')}
title={i18n.t('column.config.moveRight')}
onClick={() => move(props.columnIndex + 1)}
>
<span class="inline-block h-4 w-4">
<span class="inline-block size-4">
<ChevronRight />
</span>
</button>
<div class="flex-1" />
<button
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)}
>
<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 />
</span>
</button>

View File

@@ -70,7 +70,7 @@ const FollowingColumn: Component<FollowingColumnDisplayProps> = (props) => {
<Column
header={
<BasicColumnHeader
name={props.column.name ?? i18n()('column.home')}
name={props.column.name ?? i18n.t('column.home')}
icon={<Home />}
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
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"
onClick={() => props.loadMore.loadLatest()}
>
<span>{i18n()('column.loadLatest')}</span>
<span>{i18n.t('column.loadLatest')}</span>
</button>
</ColumnItem>
</Show>
@@ -102,7 +102,7 @@ const LoadMore: Component<LoadMoreProps> = (props) => {
disabled={!props.eose}
onClick={() => props.loadMore.loadOld()}
>
<span>{i18n()('column.loadOld')}</span>
<span>{i18n.t('column.loadOld')}</span>
</button>
</ColumnItem>
</>

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,14 +30,14 @@ const EventDisplay: Component<EventDisplayProps> = (props) => {
<Switch
fallback={
<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} />
</div>
}
>
<Match when={!isAllowedKind()} keyed>
<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} />
</div>
</Match>

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ const Repost: Component<RepostProps> = (props) => {
<div>
<div class="flex items-center gap-1">
<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 />
</span>
</div>
@@ -38,7 +38,7 @@ const Repost: Component<RepostProps> = (props) => {
>
<UserDisplayName pubkey={props.event.pubkey} />
</button>
<span class="shrink-0">{i18n()('notification.reposted')}</span>
<span class="shrink-0">{i18n.t('notification.reposted')}</span>
</div>
<div class="text-xs">{formatDate(event().createdAtAsDate())}</div>
</div>

View File

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

View File

@@ -74,20 +74,20 @@ const ZapReceiptDisplay: Component<ZapReceiptProps> = (props) => {
<Show when={!shouldMuteEvent(props.event) && isZapReceiptVerified()}>
<div class="flex items-center gap-1 text-sm">
<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 />
</div>
<div class="mt-[-2px] shrink-0 text-xs">{amountSi()}</div>
</div>
<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>
{(url) => (
<img
src={thumbnailUrl(url, 'icon')}
alt="icon"
// TODO autofit
class="h-full w-full object-cover"
class="size-full object-cover"
/>
)}
</Show>
@@ -99,7 +99,7 @@ const ZapReceiptDisplay: Component<ZapReceiptProps> = (props) => {
>
<UserDisplayName pubkey={event().senderPubkey()} />
</button>
<span class="shrink-0">{i18n()('notification.zapped')}</span>
<span class="shrink-0">{i18n.t('notification.zapped')}</span>
</div>
</div>
</div>

View File

@@ -25,10 +25,10 @@ const ContentWarningDisplay: Component<ContentWarningDisplayProps> = (props) =>
<span class="inline-block size-4">
<ExclamationTriangle />
</span>
<span>{i18n()('post.contentWarning.show')}</span>
<span>{i18n.t('post.contentWarning.show')}</span>
<Show when={props.contentWarning.reason != null}>
<span>
{i18n()('post.contentWarning.reason')}: {props.contentWarning.reason}
{i18n.t('post.contentWarning.reason')}: {props.contentWarning.reason}
</span>
</Show>
</button>
@@ -40,7 +40,7 @@ const ContentWarningDisplay: Component<ContentWarningDisplayProps> = (props) =>
class="text-xs text-fg-secondary hover:text-fg-secondary/70"
onClick={() => setShowContentWarning(false)}
>
{i18n()('post.contentWarning.hide')}
{i18n.t('post.contentWarning.hide')}
</button>
</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"
onClick={() => setHidden(false)}
>
{i18n()('post.showImage')}
{i18n.t('post.showImage')}
</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"
onClick={() => setHidden(false)}
>
{i18n()('post.showPreview')}
{i18n.t('post.showPreview')}
</button>
<SafeLink class="text-link underline" href={props.url} />
</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"
onClick={() => setHidden(false)}
>
{i18n()('post.showVideo')}
{i18n.t('post.showVideo')}
</button>
}
>
@@ -36,7 +36,7 @@ const VideoDisplay: Component<VideoDisplayProps> = (props) => {
src={props.url}
controls
>
<a href={props.url}>{i18n()('post.download')}</a>
<a href={props.url}>{i18n.t('post.download')}</a>
</video>
</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"
href="https://github.com/syusui-s/rabbit/issues/new/choose"
>
{i18n()('about.bugReport')}
{i18n.t('about.bugReport')}
</SafeLink>
<SafeLink
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"
>
{i18n()('about.sourceCode')}
{i18n.t('about.sourceCode')}
</SafeLink>
</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">
Copyright (C) 2023 Shusui Moyatani and{' '}
@@ -84,12 +84,12 @@ const About: Component<AboutProps> = (props) => {
</p>
<pre class=" max-h-96 overflow-auto rounded bg-bg-tertiary p-2 text-sm">
{i18n()('about.agplText')}
{i18n.t('about.agplText')}
</pre>
<p>
<SafeLink class="text-link underline" href="https://gpl.mhatta.org/agpl.ja.html">
{i18n()('about.agplTranslationJa')}
{i18n.t('about.agplTranslationJa')}
</SafeLink>
</p>
@@ -97,7 +97,7 @@ const About: Component<AboutProps> = (props) => {
{packageInfo()?.self.licenseText}
</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 ?? []}>
{(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"
onClick={() => addFollowingColumn()}
>
<span class="inline-block h-8 w-8">
<span class="inline-block size-8">
<Home />
</span>
{i18n()('column.home')}
{i18n.t('column.home')}
</button>
<button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addNotificationColumn()}
>
<span class="inline-block h-8 w-8">
<span class="inline-block size-8">
<Bell />
</span>
{i18n()('column.notification')}
{i18n.t('column.notification')}
</button>
<button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addJapanRelaysColumn()}
>
<span class="inline-block h-8 w-8">
<span class="inline-block size-8">
<GlobeAlt />
</span>
{i18n()('column.japanese')}
{i18n.t('column.japanese')}
</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"
onClick={() => addSearchColumn()}
>
<span class="inline-block h-8 w-8">
<span class="inline-block size-8">
<MagnifyingGlass />
</span>
{i18n()('column.search')}
{i18n.t('column.search')}
</button>
<button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addMyPostsColumn()}
>
<span class="inline-block h-8 w-8">
<span class="inline-block size-8">
<User />
</span>
{i18n()('column.myPosts')}
{i18n.t('column.myPosts')}
</button>
<button
class="flex basis-1/2 flex-col items-center gap-2 py-8 hover:text-primary sm:basis-1/4"
onClick={() => addMyReactionsColumn()}
>
<span class="inline-block h-8 w-8">
<span class="inline-block size-8">
<Heart />
</span>
{i18n()('column.myReactions')}
{i18n.t('column.myReactions')}
</button>
</div>
</BasicModal>

View File

@@ -44,7 +44,7 @@ const Section = (props: { title: string; initialOpened?: boolean; children: JSX.
onClick={() => toggleOpened()}
>
<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 />}>
<ChevronUp />
</Show>
@@ -73,7 +73,7 @@ const ToggleButton = (props: {
area-label={props.value}
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>
);
@@ -83,7 +83,7 @@ const ProfileSection = () => {
const { showProfile, showProfileEdit } = useModalState();
return (
<Section title={i18n()('config.profile.profile')}>
<Section title={i18n.t('config.profile.profile')}>
<div class="flex gap-2 py-1">
<button
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
class="rounded border border-primary px-4 py-1 font-bold text-primary"
onClick={() => showProfileEdit()}
>
{i18n()('config.profile.editProfile')}
{i18n.t('config.profile.editProfile')}
</button>
</div>
</Section>
@@ -126,11 +126,11 @@ const RelayConfig = () => {
const relayUrls = importedRelays.map(([relayUrl]) => relayUrl).join('\n');
if (importedRelays.length === 0) {
window.alert(i18n()('config.relays.notConfigured'));
window.alert(i18n.t('config.relays.notConfigured'));
return;
}
if (!window.confirm(`${i18n()('config.relays.askImport')}\n\n${relayUrls}`)) {
if (!window.confirm(`${i18n.t('config.relays.askImport')}\n\n${relayUrls}`)) {
return;
}
@@ -142,14 +142,14 @@ const RelayConfig = () => {
});
const currentCount = config().relayUrls.length;
const importedCount = currentCount - lastCount;
window.alert(i18n()('config.relays.imported', { count: importedCount }));
window.alert(i18n.t('config.relays.imported', { count: importedCount }));
};
return (
<>
<Section title={i18n()('config.relays.relays')}>
<Section title={i18n.t('config.relays.relays')}>
<p class="py-1">
{i18n()('config.relays.numOfRelays', { count: config().relayUrls.length })}
{i18n.t('config.relays.numOfRelays', { count: config().relayUrls.length })}
</p>
<form class="flex gap-2" onSubmit={handleClickAddRelay}>
<input
@@ -162,7 +162,7 @@ const RelayConfig = () => {
onChange={(ev) => setRelayUrlInput(ev.currentTarget.value)}
/>
<button type="submit" class="rounded bg-primary p-2 font-bold text-primary-fg">
{i18n()('config.relays.addRelay')}
{i18n.t('config.relays.addRelay')}
</button>
</form>
<ul class="pt-2">
@@ -170,7 +170,7 @@ const RelayConfig = () => {
{(relayUrl: string) => (
<li class="flex items-center border-t border-border pr-4">
<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 />
</button>
</li>
@@ -178,18 +178,18 @@ const RelayConfig = () => {
</For>
</ul>
</Section>
<Section title={i18n()('config.relays.importRelays')}>
<Section title={i18n.t('config.relays.importRelays')}>
<button
type="button"
class="rounded bg-primary p-2 font-bold text-primary-fg"
onClick={() => {
importFromNIP07().catch((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>
</Section>
</>
@@ -213,7 +213,7 @@ const ColorThemeConfig = () => {
};
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">
<For each={Object.values(colorThemes)}>
{(colorTheme) => (
@@ -247,18 +247,18 @@ const DateFormatConfig = () => {
}[] = [
{
id: 'relative',
name: i18n()('config.display.relativeTimeNotation'),
example: i18n()('config.display.relativeTimeNotationExample'),
name: i18n.t('config.display.relativeTimeNotation'),
example: i18n.t('config.display.relativeTimeNotationExample'),
},
{
id: 'absolute-short',
name: i18n()('config.display.absoluteTimeNotationShort'),
example: i18n()('config.display.absoluteTimeNotationShortExample'),
name: i18n.t('config.display.absoluteTimeNotationShort'),
example: i18n.t('config.display.absoluteTimeNotationShortExample'),
},
{
id: 'absolute-long',
name: i18n()('config.display.absoluteTimeNotationLong'),
example: i18n()('config.display.absoluteTimeNotationLongExample'),
name: i18n.t('config.display.absoluteTimeNotationLong'),
example: i18n.t('config.display.absoluteTimeNotationLongExample'),
},
];
@@ -267,7 +267,7 @@ const DateFormatConfig = () => {
};
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">
<For each={dateFormats}>
{({ id, name, example }) => (
@@ -313,17 +313,17 @@ const ReactionConfig = () => {
};
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 w-full">
<div class="flex-1">{i18n()('config.display.enableEmojiReaction')}</div>
<div class="flex-1">{i18n.t('config.display.enableEmojiReaction')}</div>
<ToggleButton
value={config().useEmojiReaction}
onClick={() => toggleUseEmojiReaction()}
/>
</div>
<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
value={config().showEmojiReaction}
onClick={() => toggleShowEmojiReaction()}
@@ -350,10 +350,10 @@ const EmojiConfig = () => {
};
return (
<Section title={i18n()('config.customEmoji.customEmoji')}>
<Section title={i18n.t('config.customEmoji.customEmoji')}>
<form class="flex flex-col gap-2" onSubmit={handleClickSaveEmoji}>
<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
class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary"
type="text"
@@ -366,7 +366,7 @@ const EmojiConfig = () => {
/>
</label>
<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
class="flex-1 rounded-md border-border bg-bg placeholder:text-fg-secondary focus:border-border focus:ring-primary"
type="text"
@@ -382,7 +382,7 @@ const EmojiConfig = () => {
type="submit"
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>
</form>
<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"
onClick={() => removeEmoji(shortcode)}
>
{i18n()('config.customEmoji.removeEmoji')}
{i18n.t('config.customEmoji.removeEmoji')}
</button>
</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"
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">
<img class="object-contain" src={url} alt={shortcode} />
@@ -456,8 +456,8 @@ const EmojiImport = () => {
};
return (
<Section title={i18n()('config.customEmoji.emojiImport')}>
<p>{i18n()('config.customEmoji.emojiImportDescription')}</p>
<Section title={i18n.t('config.customEmoji.emojiImport')}>
<p>{i18n.t('config.customEmoji.emojiImportDescription')}</p>
<form class="flex flex-col gap-2" onSubmit={handleClickSaveEmoji}>
<textarea
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"
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>
</form>
</Section>
@@ -492,7 +492,7 @@ const MuteConfig = () => {
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">
<For each={config().mutedPubkeys}>
{(pubkey) => (
@@ -500,7 +500,7 @@ const MuteConfig = () => {
<div class="flex-1 truncate">
<LazyLoad>{() => <UserNameDisplay pubkey={pubkey} />}</LazyLoad>
</div>
<button class="h-3 w-3 shrink-0" onClick={() => removeMutedPubkey(pubkey)}>
<button class="size-3 shrink-0" onClick={() => removeMutedPubkey(pubkey)}>
<XMark />
</button>
</li>
@@ -508,7 +508,7 @@ const MuteConfig = () => {
</For>
</ul>
</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}>
<input
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)}
/>
<button type="submit" class="rounded bg-primary p-2 font-bold text-primary-fg">
{i18n()('config.mute.add')}
{i18n.t('config.mute.add')}
</button>
</form>
<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) => (
<li class="flex items-center border-b border-border pr-4">
<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 />
</button>
</li>
@@ -553,8 +553,8 @@ const EmbeddingConfig = () => {
};
return (
<Section title={i18n()('config.display.embedding')}>
<p>{i18n()('config.display.embeddingDescription')}</p>
<Section title={i18n.t('config.display.embedding')}>
<p>{i18n.t('config.display.embeddingDescription')}</p>
<div class="flex flex-col justify-evenly gap-2">
<div class="flex w-full">
<div class="flex-1">YouTube</div>
@@ -599,21 +599,21 @@ const OtherConfig = () => {
};
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 w-full">
<div class="flex-1">{i18n()('config.display.keepOpenPostForm')}</div>
<div class="flex-1">{i18n.t('config.display.keepOpenPostForm')}</div>
<ToggleButton
value={config().keepOpenPostForm}
onClick={() => toggleKeepOpenPostForm()}
/>
</div>
<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()} />
</div>
<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()} />
</div>
{/*
@@ -638,17 +638,17 @@ const ConfigUI = (props: ConfigProps) => {
const menu = [
{
name: () => i18n()('config.profile.profile'),
name: () => i18n.t('config.profile.profile'),
icon: () => <User />,
render: () => <ProfileSection />,
},
{
name: () => i18n()('config.relays.relays'),
name: () => i18n.t('config.relays.relays'),
icon: () => <ServerStack />,
render: () => <RelayConfig />,
},
{
name: () => i18n()('config.display.display'),
name: () => i18n.t('config.display.display'),
icon: () => <PaintBrush />,
render: () => (
<>
@@ -661,7 +661,7 @@ const ConfigUI = (props: ConfigProps) => {
),
},
{
name: () => i18n()('config.customEmoji.customEmoji'),
name: () => i18n.t('config.customEmoji.customEmoji'),
icon: () => <FaceSmile />,
render: () => (
<>
@@ -671,7 +671,7 @@ const ConfigUI = (props: ConfigProps) => {
),
},
{
name: () => i18n()('config.mute.mute'),
name: () => i18n.t('config.mute.mute'),
icon: () => <EyeSlash />,
render: () => <MuteConfig />,
},
@@ -690,7 +690,7 @@ const ConfigUI = (props: ConfigProps) => {
when={getMenuItem()}
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">
<Show when={window.location.host === 'syusui-s.github.io'}>
<button
@@ -703,7 +703,7 @@ const ConfigUI = (props: ConfigProps) => {
.catch(() => window.alert('failed to copy'));
}}
>
{i18n()('config.copyToClipboard')}
{i18n.t('config.copyToClipboard')}
</button>
</Show>
<Show when={window.location.host === 'rabbit.syusui.net'}>
@@ -722,7 +722,7 @@ const ConfigUI = (props: ConfigProps) => {
}
}}
>
{i18n()('config.importFromClipboard')}
{i18n.t('config.importFromClipboard')}
</button>
</Show>
</div>
@@ -734,7 +734,7 @@ const ConfigUI = (props: ConfigProps) => {
class="flex w-full gap-2 py-3 hover:text-primary"
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()}
</button>
</li>
@@ -752,7 +752,7 @@ const ConfigUI = (props: ConfigProps) => {
class="pr-4 text-fg hover:text-fg-secondary"
onClick={() => setMenuIndex(null)}
>
<span class="inline-block h-6 w-6">
<span class="inline-block size-6">
<ArrowLeft />
</span>
</button>

View File

@@ -151,12 +151,12 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
if (
(latest.data() == null || latest.followingPubkeys().length === 0) &&
!window.confirm(i18n()('profile.confirmUpdateEvenIfEmpty'))
!window.confirm(i18n.t('profile.confirmUpdateEvenIfEmpty'))
)
return;
if ((latest?.data()?.created_at ?? 0) < (myFollowingQuery.data?.created_at ?? 0)) {
window.alert(i18n()('profile.failedToFetchLatestFollowList'));
window.alert(i18n.t('profile.failedToFetchLatestFollowList'));
return;
}
@@ -182,7 +182,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
});
} catch (err) {
console.error('failed to update contact list', err);
window.alert(i18n()('profile.failedToUpdateFollowList'));
window.alert(i18n.t('profile.failedToUpdateFollowList'));
} finally {
setUpdatingContacts(false);
}
@@ -195,7 +195,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
};
const unfollow = () => {
if (!window.confirm(i18n()('profile.confirmUnfollow'))) return;
if (!window.confirm(i18n.t('profile.confirmUnfollow'))) return;
updateContacts('unfollow', props.pubkey).catch((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: () => {
navigator.clipboard.writeText(npub()).catch((err) => window.alert(err));
},
},
{
content: i18n()('profile.addUserColumn'),
content: i18n.t('profile.addUserColumn'),
onSelect: () => {
const columnName = profile()?.name ?? npub();
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: () => {
const columnName = `${i18n()('column.home')} / ${profile()?.name ?? npub()}`;
const columnName = `${i18n.t('column.home')} / ${profile()?.name ?? npub()}`;
saveColumn(createFollowingColumn({ name: columnName, pubkey: props.pubkey }));
request({ command: 'moveToLastColumn' }).catch((err) => console.error(err));
props.onClose?.();
},
},
{
content: !isMuted() ? i18n()('profile.mute') : i18n()('profile.unmute'),
content: !isMuted() ? i18n.t('profile.mute') : i18n.t('profile.unmute'),
onSelect: () => {
if (!isMuted()) {
addMutedPubkey(props.pubkey);
@@ -248,7 +248,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
},
{
when: () => props.pubkey === myPubkey(),
content: !following() ? i18n()('profile.followMyself') : i18n()('profile.unfollowMyself'),
content: !following() ? i18n.t('profile.followMyself') : i18n.t('profile.unfollowMyself'),
onSelect: () => {
if (!following()) {
follow();
@@ -282,16 +282,16 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
>
{(bannerUrl) => (
<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>
)}
</Show>
<div class="mt-[-54px] flex items-end gap-4 px-4 pt-4">
<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>
{(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>
</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"
onClick={() => showProfileEdit()}
>
{i18n()('profile.editProfile')}
{i18n.t('profile.editProfile')}
</button>
</Match>
<Match when={updateContactsMutation.isPending || updatingContacts()}>
<span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base">
{i18n()('general.updating')}
{i18n.t('general.updating')}
</span>
</Match>
<Match when={myFollowingQuery.isPending || myFollowingQuery.isFetching}>
<span class="rounded-full border border-primary px-4 py-2 text-primary sm:text-base">
{i18n()('general.loading')}
{i18n.t('general.loading')}
</span>
</Match>
<Match when={following()}>
@@ -327,8 +327,8 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
onClick={() => unfollow()}
disabled={updateContactsMutation.isPending}
>
<Show when={!hoverFollowButton()} fallback={i18n()('profile.unfollow')}>
{i18n()('profile.followingCurrently')}
<Show when={!hoverFollowButton()} fallback={i18n.t('profile.unfollow')}>
{i18n.t('profile.followingCurrently')}
</Show>
</button>
</Match>
@@ -338,7 +338,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
onClick={() => follow()}
disabled={updateContactsMutation.isPending}
>
{i18n()('profile.follow')}
{i18n.t('profile.follow')}
</button>
</Match>
</Switch>
@@ -354,10 +354,10 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</div>
<Switch>
<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 when={followed()}>
<div class="shrink-0 text-xs">{i18n()('profile.followsYou')}</div>
<div class="shrink-0 text-xs">{i18n.t('profile.followsYou')}</div>
</Match>
</Switch>
</div>
@@ -365,7 +365,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</div>
<div class="flex items-start px-4 pt-2">
<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}>
<div class="truncate text-xl font-bold">{profile()?.display_name}</div>
</Show>
@@ -378,18 +378,18 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
{nip05Identifier()?.ident}
<Switch
fallback={
<span class="inline-block h-4 w-4 text-danger">
<span class="inline-block size-4 text-danger">
<ExclamationCircle />
</span>
}
>
<Match when={verificationQuery.isPending}>
<span class="inline-block h-3 w-3">
<span class="inline-block size-3">
<ArrowPath />
</span>
</Match>
<Match when={isVerified()}>
<span class="inline-block h-4 w-4 text-link">
<span class="inline-block size-4 text-link">
<CheckCircle />
</span>
</Match>
@@ -411,11 +411,11 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</Show>
<div class="flex border-t border-border px-4 py-2">
<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">
<Show
when={userFollowingQuery.isFetched}
fallback={<span class="text-sm">{i18n()('general.loading')}</span>}
fallback={<span class="text-sm">{i18n.t('general.loading')}</span>}
>
{userFollowingPubkeys().length}
</Show>
@@ -423,7 +423,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
</button>
<Show when={!config().hideCount}>
<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">
<Show
when={showFollowers()}
@@ -432,7 +432,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
class="text-sm hover:text-fg-secondary"
onClick={() => setShowFollowers(true)}
>
{i18n()('profile.loadFollowers')}
{i18n.t('profile.loadFollowers')}
</button>
}
keyed
@@ -448,7 +448,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
<Show when={profile()?.website} keyed>
{(website) => (
<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 />
</span>
<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 failed = results.length - succeeded;
if (succeeded === results.length) {
window.alert(i18n()('profile.edit.updateSucceeded'));
window.alert(i18n.t('profile.edit.updateSucceeded'));
} else if (succeeded > 0) {
window.alert(i18n()('profile.edit.failedToUpdatePartially', { count: failed }));
window.alert(i18n.t('profile.edit.failedToUpdatePartially', { count: failed }));
} else {
window.alert(i18n()('profile.edit.failedToUpdate'));
window.alert(i18n.t('profile.edit.failedToUpdate'));
}
invalidateProfile()
.then(() => query.refetch())
@@ -149,23 +149,23 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
<div>
<Show when={banner().length > 0} fallback={<div class="h-24 shrink-0" />} keyed>
<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>
</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}>
<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>
</div>
</div>
<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>
<div>
<form class="flex flex-col gap-4 p-4" onSubmit={handleSubmit}>
<div class="flex flex-col items-start gap-1">
<label class="font-bold" for="picture">
{i18n()('profile.edit.icon')}
{i18n.t('profile.edit.icon')}
</label>
<input
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 class="flex flex-col items-start gap-1">
<label class="font-bold" for="picture">
{i18n()('profile.edit.banner')}
{i18n.t('profile.edit.banner')}
</label>
<input
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 class="flex flex-col items-start gap-1">
<label class="font-bold" for="name">
{i18n()('profile.edit.name')}
{i18n.t('profile.edit.name')}
</label>
<div class="flex w-full items-center gap-2">
<span>@</span>
@@ -217,7 +217,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div>
<div class="flex flex-col items-start gap-1">
<label class="font-bold" for="name">
{i18n()('profile.edit.displayName')}
{i18n.t('profile.edit.displayName')}
</label>
<input
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 class="flex flex-col items-start gap-1">
<label class="font-bold" for="name">
{i18n()('profile.edit.about')}
{i18n.t('profile.edit.about')}
</label>
<textarea
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 class="flex flex-col items-start gap-1">
<label class="font-bold" for="name">
{i18n()('profile.edit.website')}
{i18n.t('profile.edit.website')}
</label>
<input
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 class="flex flex-col items-start gap-1">
<label class="font-bold" for="name">
{i18n()('profile.edit.nip05')}
{i18n.t('profile.edit.nip05')}
</label>
<input
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 class="flex flex-col items-start gap-1">
<label class="font-bold" for="name">
{i18n()('profile.edit.lightningAddress')}
{i18n.t('profile.edit.lightningAddress')}
</label>
<span class="text-xs">{i18n()('profile.edit.lightningAddressDescription')}</span>
<span class="text-xs">{i18n.t('profile.edit.lightningAddressDescription')}</span>
<input
class="w-full rounded-md border-border bg-bg ring-border focus:border-border focus:ring-primary"
type="text"
@@ -293,7 +293,7 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
</div>
<Show when={Object.entries(otherProperties()).length > 0}>
<div>
<span class="font-bold">{i18n()('profile.edit.otherProperties')}</span>
<span class="font-bold">{i18n.t('profile.edit.otherProperties')}</span>
<div>
<For each={Object.entries(otherProperties())}>
{([key, value]) => (
@@ -316,17 +316,17 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
}}
disabled={mutation.isPending}
>
{i18n()('profile.edit.save')}
{i18n.t('profile.edit.save')}
</button>
<button
type="button"
class="rounded border border-primary p-2 font-bold text-primary hover:border-primary-hover hover:text-primary-hover"
onClick={() => props.onClose()}
>
{i18n()('profile.edit.cancel')}
{i18n.t('profile.edit.cancel')}
</button>
</div>
<Show when={mutation.isPending}>{i18n()('profile.edit.updating')}</Show>
<Show when={mutation.isPending}>{i18n.t('profile.edit.updating')}</Show>
</form>
</div>
</BasicModal>

View File

@@ -143,10 +143,10 @@ const InvoiceDisplay: Component<{ invoice: string; event: NostrEvent; nostrPubke
when={!zapped()}
fallback={
<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 />
</span>
<div class="text-secondary text-xl">{i18n()('zap.completed')}</div>
<div class="text-secondary text-xl">{i18n.t('zap.completed')}</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"
href={lightingInvoice()}
>
{i18n()('zap.sendViaWallet')}
{i18n.t('zap.sendViaWallet')}
</a>
<Show when={webln.status() === 'available'}>
<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"
onClick={handleClickWebLN}
>
{i18n()('zap.sendViaWebLN')}
{i18n.t('zap.sendViaWebLN')}
</button>
</Show>
</div>
@@ -274,19 +274,19 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
return (
<Switch>
<Match when={query.isError}>
{i18n()('zap.fetchingLnUrlEndpointError')}: {query?.error?.message}
{i18n.t('zap.fetchingLnUrlEndpointError')}: {query?.error?.message}
</Match>
<Match when={error()} keyed>
{(err) => (
<>
{i18n()('zap.lnUrlEndpointError')}: {err.reason}
{i18n.t('zap.lnUrlEndpointError')}: {err.reason}
</>
)}
</Match>
<Match when={query.isFetching}>{i18n()('zap.fetchingLnUrlEndpoint')}</Match>
<Match when={getInvoiceMutation.isPending}>{i18n()('zap.fetchingLnUrlInvoice')}</Match>
<Match when={query.isFetching}>{i18n.t('zap.fetchingLnUrlEndpoint')}</Match>
<Match when={getInvoiceMutation.isPending}>{i18n.t('zap.fetchingLnUrlInvoice')}</Match>
<Match when={getInvoiceMutation.isError}>
{i18n()('zap.fetchingLnUrlInvoiceError')}: {getInvoiceMutation?.error?.message}
{i18n.t('zap.fetchingLnUrlInvoiceError')}: {getInvoiceMutation?.error?.message}
</Match>
<Match when={getInvoiceMutation.isSuccess && getInvoiceMutation.data} keyed>
{(invoice) => (
@@ -300,10 +300,10 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
<Match when={query.isSuccess}>
<div class="flex flex-col items-center">
<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 when={hasZapTag()}>
<div class="pb-8 text-center">{i18n()('zap.zapSplitIsNotSupported')}</div>
<div class="pb-8 text-center">{i18n.t('zap.zapSplitIsNotSupported')}</div>
</Show>
<div class="flex flex-col items-center overflow-hidden rounded px-8 py-2 text-fg-secondary">
<Show when={lnurlServiceIcon()} keyed>
@@ -342,7 +342,7 @@ const ZapDialog: Component<ZapDialogProps> = (props) => {
name="comment"
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}
placeholder={i18n()('zap.comment')}
placeholder={i18n.t('zap.comment')}
disabled={query.isPending}
value={comment()}
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"
disabled={getInvoiceMutation.isPending}
>
<span class="inline-block h-6 w-6">
<span class="inline-block size-6">
<Bolt />
</span>
</button>
@@ -379,7 +379,7 @@ const ZapRequestModal: Component<ZapRequestModalProps> = (props) => {
return (
<BasicModal onClose={props.onClose}>
<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}>
<div class="flex justify-center gap-3 pb-2">
<button
@@ -393,7 +393,7 @@ const ZapRequestModal: Component<ZapRequestModalProps> = (props) => {
}}
onClick={() => setLnurlSource('lud06')}
>
{i18n()('zap.lud06')}
{i18n.t('zap.lud06')}
</button>
<button
type="button"
@@ -406,7 +406,7 @@ const ZapRequestModal: Component<ZapRequestModalProps> = (props) => {
}}
onClick={() => setLnurlSource('lud16')}
>
{i18n()('zap.lud16')}
{i18n.t('zap.lud16')}
</button>
</div>
</Show>

View File

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

View File

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

View File

@@ -2,38 +2,37 @@ import { Component, JSX, createContext, createEffect, createSignal, useContext }
import i18next from 'i18next';
type I18Next = typeof i18next.t;
type I18Next = typeof i18next;
export type I18NextProviderProps = {
i18next: I18Next | Promise<I18Next | void> | void;
children?: JSX.Element;
};
const I18NextContext = createContext<I18Next | Promise<I18Next | void> | void>();
const I18NextContext = createContext<I18Next>(i18next);
export const useTranslation = () => {
const [i18nextFn, setI18nextFn] = createSignal<I18Next>(i18next.t);
const maybePromise = useContext(I18NextContext);
export const useTranslation = () => useContext<I18Next>(I18NextContext);
export const I18NextProvider: Component<I18NextProviderProps> = (props) => {
const [i18nextInstance, setI18nextInstance] = createSignal<I18Next>(i18next);
createEffect(() => {
if (maybePromise instanceof Promise) {
maybePromise
if (props.i18next instanceof Promise) {
props.i18next
.then((instance) => {
if (instance != null) {
setI18nextFn(() => instance);
setI18nextInstance(() => instance);
}
})
.catch((err) => {
console.error('failed to initialize i18next', err);
console.error('failed to initialize i18n.text', err);
});
} else if (maybePromise != null) {
setI18nextFn(() => maybePromise);
} else if (props.i18next != null) {
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">
<Switch>
<Match when={signerStatus() === 'checking'}>
<p>{i18n()('hello.signerChecking')}</p>
<p>{i18n.t('hello.signerChecking')}</p>
</Match>
<Match when={signerStatus() === 'unavailable'}>
<h2 class="font-bold">{i18n()('hello.signerUnavailable')}</h2>
<p>{i18n()('hello.signerUnavailableMessage')}</p>
<h2 class="font-bold">{i18n.t('hello.signerUnavailable')}</h2>
<p>{i18n.t('hello.signerUnavailableMessage')}</p>
<SignerExtensions />
<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
class="rounded bg-primary px-4 py-2 text-sm font-bold text-primary-fg hover:bg-primary-hover"
onClick={() => window.location.reload()}
>
{i18n()('hello.reload')}
{i18n.t('hello.reload')}
</button>
</div>
</Match>
@@ -80,7 +80,7 @@ const Hello: Component = () => {
class="rounded bg-primary p-4 text-lg font-bold text-primary-fg hover:shadow-md"
onClick={handleLogin}
>
{i18n()('hello.loginWithSigner')}
{i18n.t('hello.loginWithSigner')}
</button>
</Match>
</Switch>

View File

@@ -7,10 +7,10 @@ const NotFound: Component = () => {
return (
<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">
<a class="text-link underline" href="/">
{i18n()('notFound.back')}
{i18n.t('notFound.back')}
</a>
</p>
</div>