chore: i18n
This commit is contained in:
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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': '回复'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
Reference in New Issue
Block a user