chore: i18n

This commit is contained in:
codytseng
2024-11-19 23:02:56 +08:00
parent b36cbeb158
commit 5aefd7dbde
11 changed files with 76 additions and 27 deletions

View File

@@ -1,14 +1,16 @@
import { Button } from '@renderer/components/ui/button' import { Button } from '@renderer/components/ui/button'
import { useSecondaryPage } from '@renderer/PageManager' import { useSecondaryPage } from '@renderer/PageManager'
import { ChevronLeft } from 'lucide-react' import { ChevronLeft } from 'lucide-react'
import { useTranslation } from 'react-i18next'
export default function BackButton({ hide = false }: { hide?: boolean }) { export default function BackButton({ hide = false }: { hide?: boolean }) {
const { t } = useTranslation()
const { pop } = useSecondaryPage() const { pop } = useSecondaryPage()
return ( return (
<> <>
{!hide && ( {!hide && (
<Button variant="titlebar" size="titlebar" title="back" onClick={() => pop()}> <Button variant="titlebar" size="titlebar" title={t('back')} onClick={() => pop()}>
<ChevronLeft /> <ChevronLeft />
</Button> </Button>
)} )}

View File

@@ -39,7 +39,7 @@ export default function Mentions({
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-48"> <PopoverContent className="w-48">
<div className="space-y-2"> <div className="space-y-2">
<div className="text-sm font-semibold">Mentions:</div> <div className="text-sm font-semibold">{t('Mentions')}:</div>
{pubkeys.map((pubkey, index) => ( {pubkeys.map((pubkey, index) => (
<div key={`${pubkey}-${index}`} className="flex gap-1 items-center"> <div key={`${pubkey}-${index}`} className="flex gap-1 items-center">
<UserAvatar userId={pubkey} size="small" /> <UserAvatar userId={pubkey} size="small" />

View File

@@ -99,7 +99,7 @@ export default function PostDialog({
<DialogTitle> <DialogTitle>
{parentEvent ? ( {parentEvent ? (
<div className="flex gap-2 items-center max-w-full"> <div className="flex gap-2 items-center max-w-full">
<div className="shrink-0">Reply to</div> <div className="shrink-0">{t('Reply to')}</div>
<UserAvatar userId={parentEvent.pubkey} size="tiny" /> <UserAvatar userId={parentEvent.pubkey} size="tiny" />
<div className="truncate">{parentEvent.content}</div> <div className="truncate">{parentEvent.content}</div>
</div> </div>
@@ -131,7 +131,7 @@ export default function PostDialog({
</Button> </Button>
<Button type="submit" disabled={!canPost} onClick={post}> <Button type="submit" disabled={!canPost} onClick={post}>
{posting && <LoaderCircle className="animate-spin" />} {posting && <LoaderCircle className="animate-spin" />}
{t('Post')} {parentEvent ? t('Reply') : t('Post')}
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -12,8 +12,10 @@ import { useState } from 'react'
import RelayUrls from './RelayUrl' import RelayUrls from './RelayUrl'
import { useRelaySettingsComponent } from './provider' import { useRelaySettingsComponent } from './provider'
import { TRelayGroup } from './types' import { TRelayGroup } from './types'
import { useTranslation } from 'react-i18next'
export default function RelayGroup({ group }: { group: TRelayGroup }) { export default function RelayGroup({ group }: { group: TRelayGroup }) {
const { t } = useTranslation()
const { expandedRelayGroup } = useRelaySettingsComponent() const { expandedRelayGroup } = useRelaySettingsComponent()
const { temporaryRelayUrls } = useRelaySettings() const { temporaryRelayUrls } = useRelaySettings()
const { groupName, relayUrls } = group const { groupName, relayUrls } = group
@@ -34,7 +36,7 @@ export default function RelayGroup({ group }: { group: TRelayGroup }) {
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1">
<RelayUrlsExpandToggle groupName={groupName}> <RelayUrlsExpandToggle groupName={groupName}>
{relayUrls.length} relays {t('n relays', { n: relayUrls.length })}
</RelayUrlsExpandToggle> </RelayUrlsExpandToggle>
<RelayGroupOptions group={group} /> <RelayGroupOptions group={group} />
</div> </div>
@@ -71,6 +73,7 @@ function RelayGroupActiveToggle({
} }
function RelayGroupName({ groupName }: { groupName: string }) { function RelayGroupName({ groupName }: { groupName: string }) {
const { t } = useTranslation()
const [newGroupName, setNewGroupName] = useState(groupName) const [newGroupName, setNewGroupName] = useState(groupName)
const [newNameError, setNewNameError] = useState<string | null>(null) const [newNameError, setNewNameError] = useState<string | null>(null)
const { relayGroups, switchRelayGroup, renameRelayGroup } = useRelaySettings() const { relayGroups, switchRelayGroup, renameRelayGroup } = useRelaySettings()
@@ -80,7 +83,7 @@ function RelayGroupName({ groupName }: { groupName: string }) {
const saveNewGroupName = () => { const saveNewGroupName = () => {
if (relayGroups.find((group) => group.groupName === newGroupName)) { if (relayGroups.find((group) => group.groupName === newGroupName)) {
return setNewNameError('already exists') return setNewNameError(t('relay collection name already exists'))
} }
const errMsg = renameRelayGroup(groupName, newGroupName) const errMsg = renameRelayGroup(groupName, newGroupName)
if (errMsg) { if (errMsg) {
@@ -153,6 +156,7 @@ function RelayUrlsExpandToggle({
} }
function RelayGroupOptions({ group }: { group: TRelayGroup }) { function RelayGroupOptions({ group }: { group: TRelayGroup }) {
const { t } = useTranslation()
const { deleteRelayGroup } = useRelaySettings() const { deleteRelayGroup } = useRelaySettings()
const { setRenamingGroup } = useRelaySettingsComponent() const { setRenamingGroup } = useRelaySettingsComponent()
@@ -165,7 +169,7 @@ function RelayGroupOptions({ group }: { group: TRelayGroup }) {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuItem onClick={() => setRenamingGroup(group.groupName)}> <DropdownMenuItem onClick={() => setRenamingGroup(group.groupName)}>
Rename {t('Rename')}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
@@ -174,13 +178,13 @@ function RelayGroupOptions({ group }: { group: TRelayGroup }) {
) )
}} }}
> >
Copy share link {t('Copy share link')}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
className="text-destructive focus:text-destructive" className="text-destructive focus:text-destructive"
onClick={() => deleteRelayGroup(group.groupName)} onClick={() => deleteRelayGroup(group.groupName)}
> >
Delete {t('Delete')}
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@@ -5,8 +5,10 @@ import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
import client from '@renderer/services/client.service' import client from '@renderer/services/client.service'
import { CircleX } from 'lucide-react' import { CircleX } from 'lucide-react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function RelayUrls({ groupName }: { groupName: string }) { export default function RelayUrls({ groupName }: { groupName: string }) {
const { t } = useTranslation()
const { relayGroups, updateRelayGroupRelayUrls } = useRelaySettings() const { relayGroups, updateRelayGroupRelayUrls } = useRelaySettings()
const rawRelayUrls = relayGroups.find((group) => group.groupName === groupName)?.relayUrls ?? [] const rawRelayUrls = relayGroups.find((group) => group.groupName === groupName)?.relayUrls ?? []
const isActive = relayGroups.find((group) => group.groupName === groupName)?.isActive ?? false const isActive = relayGroups.find((group) => group.groupName === groupName)?.isActive ?? false
@@ -46,10 +48,10 @@ export default function RelayUrls({ groupName }: { groupName: string }) {
if (newRelayUrl === '') return if (newRelayUrl === '') return
const normalizedUrl = normalizeUrl(newRelayUrl) const normalizedUrl = normalizeUrl(newRelayUrl)
if (relays.some(({ url }) => url === normalizedUrl)) { if (relays.some(({ url }) => url === normalizedUrl)) {
return setNewRelayUrlError('already exists') return setNewRelayUrlError(t('Relay already exists'))
} }
if (!isWebsocketUrl(normalizedUrl)) { if (!isWebsocketUrl(normalizedUrl)) {
return setNewRelayUrlError('invalid URL') return setNewRelayUrlError(t('invalid relay URL'))
} }
setRelays((pre) => [...pre, { url: normalizedUrl, isConnected: false }]) setRelays((pre) => [...pre, { url: normalizedUrl, isConnected: false }])
const newRelayUrls = [...relays.map(({ url }) => url), normalizedUrl] const newRelayUrls = [...relays.map(({ url }) => url), normalizedUrl]
@@ -85,13 +87,13 @@ export default function RelayUrls({ groupName }: { groupName: string }) {
<div className="mt-2 flex gap-2"> <div className="mt-2 flex gap-2">
<Input <Input
className={newRelayUrlError ? 'border-destructive' : ''} className={newRelayUrlError ? 'border-destructive' : ''}
placeholder="Add new relay URL" placeholder={t('Add a new relay')}
value={newRelayUrl} value={newRelayUrl}
onKeyDown={handleRelayUrlInputKeyDown} onKeyDown={handleRelayUrlInputKeyDown}
onChange={handleRelayUrlInputChange} onChange={handleRelayUrlInputChange}
onBlur={saveNewRelayUrl} onBlur={saveNewRelayUrl}
/> />
<Button onClick={saveNewRelayUrl}>Add</Button> <Button onClick={saveNewRelayUrl}>{t('Add')}</Button>
</div> </div>
{newRelayUrlError && <div className="text-xs text-destructive mt-1">{newRelayUrlError}</div>} {newRelayUrlError && <div className="text-xs text-destructive mt-1">{newRelayUrlError}</div>}
</> </>

View File

@@ -6,8 +6,10 @@ import { useEffect, useRef, useState } from 'react'
import { RelaySettingsComponentProvider } from './provider' import { RelaySettingsComponentProvider } from './provider'
import RelayGroup from './RelayGroup' import RelayGroup from './RelayGroup'
import TemporaryRelayGroup from './TemporaryRelayGroup' import TemporaryRelayGroup from './TemporaryRelayGroup'
import { useTranslation } from 'react-i18next'
export default function RelaySettings() { export default function RelaySettings() {
const { t } = useTranslation()
const { relayGroups, addRelayGroup } = useRelaySettings() const { relayGroups, addRelayGroup } = useRelaySettings()
const [newGroupName, setNewGroupName] = useState('') const [newGroupName, setNewGroupName] = useState('')
const [newNameError, setNewNameError] = useState<string | null>(null) const [newNameError, setNewNameError] = useState<string | null>(null)
@@ -21,7 +23,7 @@ export default function RelaySettings() {
const saveRelayGroup = () => { const saveRelayGroup = () => {
if (relayGroups.find((group) => group.groupName === newGroupName)) { if (relayGroups.find((group) => group.groupName === newGroupName)) {
return setNewNameError('already exists') return setNewNameError(t('relay collection name already exists'))
} }
const errMsg = addRelayGroup(newGroupName) const errMsg = addRelayGroup(newGroupName)
if (errMsg) { if (errMsg) {
@@ -45,7 +47,7 @@ export default function RelaySettings() {
return ( return (
<RelaySettingsComponentProvider> <RelaySettingsComponentProvider>
<div ref={dummyRef} tabIndex={-1} style={{ position: 'absolute', opacity: 0 }}></div> <div ref={dummyRef} tabIndex={-1} style={{ position: 'absolute', opacity: 0 }}></div>
<div className="text-lg font-semibold mb-4">Relay Settings</div> <div className="text-lg font-semibold mb-4">{t('Relay Settings')}</div>
<div className="space-y-2"> <div className="space-y-2">
<TemporaryRelayGroup /> <TemporaryRelayGroup />
{relayGroups.map((group, index) => ( {relayGroups.map((group, index) => (
@@ -57,18 +59,18 @@ export default function RelaySettings() {
<Separator className="my-4" /> <Separator className="my-4" />
<div className="w-full border rounded-lg p-4"> <div className="w-full border rounded-lg p-4">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="font-semibold">Add a new relay group</div> <div className="font-semibold">{t('Add a new relay collection')}</div>
</div> </div>
<div className="mt-2 flex gap-2"> <div className="mt-2 flex gap-2">
<Input <Input
className={newNameError ? 'border-destructive' : ''} className={newNameError ? 'border-destructive' : ''}
placeholder="Group name" placeholder={t('Relay collection name')}
value={newGroupName} value={newGroupName}
onChange={handleNewGroupNameChange} onChange={handleNewGroupNameChange}
onKeyDown={handleNewGroupNameKeyDown} onKeyDown={handleNewGroupNameKeyDown}
onBlur={saveRelayGroup} onBlur={saveRelayGroup}
/> />
<Button onClick={saveRelayGroup}>Add</Button> <Button onClick={saveRelayGroup}>{t('Add')}</Button>
</div> </div>
{newNameError && <div className="text-xs text-destructive mt-1">{newNameError}</div>} {newNameError && <div className="text-xs text-destructive mt-1">{newNameError}</div>}
</div> </div>

View File

@@ -119,7 +119,7 @@ export default function ReplyNoteList({ event, className }: { event: Event; clas
})} })}
</div> </div>
{replies.length === 0 && !loading && !hasMore && ( {replies.length === 0 && !loading && !hasMore && (
<div className="text-sm text-center text-muted-foreground">no replies</div> <div className="text-sm text-center text-muted-foreground">{t('no replies')}</div>
)} )}
</> </>
) )

View File

@@ -44,6 +44,24 @@ export default {
note: 'note', note: 'note',
"username's following": "{{username}}'s following", "username's following": "{{username}}'s following",
following: 'following', following: 'following',
Login: 'Login' Login: 'Login',
'Follows you': 'Follows you',
'relay collection name already exists': 'relay collection name already exists',
'Relay Settings': 'Relay Settings',
'Relay collection name': 'Relay collection name',
'Add a new relay collection': 'Add a new relay collection',
Add: 'Add',
'n relays': '{{n}} relays',
Rename: 'Rename',
'Copy share link': 'Copy share link',
Delete: 'Delete',
'Relay already exists': 'Relay already exists',
'invalid relay URL': 'invalid relay URL',
'Add a new relay': 'Add a new relay',
back: 'back',
'Lost in the void': 'Lost in the void',
'Carry me home': 'Carry me home',
'no replies': 'no replies',
'Reply to': 'Reply to'
} }
} }

View File

@@ -1,11 +1,11 @@
export default { export default {
translation: { translation: {
'Welcome! 🥳': '欢迎!🥳', 'Welcome! 🥳': '来都来了',
About: '关于', About: '关于',
'New post': '发布新笔记', 'New post': '发布新笔记',
Post: '发布笔记', Post: '发布笔记',
'Relay settings': '中继设置', 'Relay settings': '服务器设置',
SidebarRelays: '中继设置', SidebarRelays: '服务器',
Refresh: '刷新列表', Refresh: '刷新列表',
Profile: '个人资料', Profile: '个人资料',
Logout: '退出登录', Logout: '退出登录',
@@ -44,6 +44,24 @@ export default {
note: '笔记', note: '笔记',
"username's following": '{{username}} 的关注', "username's following": '{{username}} 的关注',
following: '关注', following: '关注',
Login: '登录' Login: '登录',
'Follows you': '关注了你',
'relay collection name already exists': '服务器组名已存在',
'Relay Settings': '服务器设置',
'Relay collection name': '服务器组名',
'Add a new relay collection': '添加新的服务器组',
Add: '添加',
'n relays': '{{n}} 个服务器',
Rename: '重命名',
'Copy share link': '复制分享链接',
Delete: '删除',
'Relay already exists': '服务器已存在',
'invalid relay URL': '无效的服务器地址',
'Add a new relay': '添加新的服务器',
back: '返回',
'Lost in the void': '迷失在虚空中',
'Carry me home': '带我回家',
'no replies': '暂无回复',
'Reply to': '回复'
} }
} }

View File

@@ -2,16 +2,19 @@ import { Button } from '@renderer/components/ui/button'
import SecondaryPageLayout from '@renderer/layouts/SecondaryPageLayout' import SecondaryPageLayout from '@renderer/layouts/SecondaryPageLayout'
import { toHome } from '@renderer/lib/link' import { toHome } from '@renderer/lib/link'
import { useSecondaryPage } from '@renderer/PageManager' import { useSecondaryPage } from '@renderer/PageManager'
import { useTranslation } from 'react-i18next'
export default function NotFoundPage() { export default function NotFoundPage() {
const { t } = useTranslation()
const { push } = useSecondaryPage() const { push } = useSecondaryPage()
return ( return (
<SecondaryPageLayout hideBackButton> <SecondaryPageLayout hideBackButton>
<div className="text-muted-foreground w-full h-full flex flex-col items-center justify-center gap-2"> <div className="text-muted-foreground w-full h-full flex flex-col items-center justify-center gap-2">
<div>Lost in the void 🌌</div> <div>{t('Lost in the void')} 🌌</div>
<div>(404)</div> <div>(404)</div>
<Button variant="secondary" onClick={() => push(toHome())}> <Button variant="secondary" onClick={() => push(toHome())}>
Carry me home {t('Carry me home')}
</Button> </Button>
</div> </div>
</SecondaryPageLayout> </SecondaryPageLayout>

View File

@@ -71,7 +71,7 @@ export default function ProfilePage({ id }: { id?: string }) {
<div className="flex justify-end h-8 gap-2 items-center"> <div className="flex justify-end h-8 gap-2 items-center">
{isFollowingYou && ( {isFollowingYou && (
<div className="text-muted-foreground rounded-full bg-muted text-xs h-fit px-2"> <div className="text-muted-foreground rounded-full bg-muted text-xs h-fit px-2">
Follows you {t('Follows you')}
</div> </div>
)} )}
<FollowButton pubkey={pubkey} /> <FollowButton pubkey={pubkey} />