feat: i18n for dashboard

This commit is contained in:
DIYgod 2023-02-24 22:48:43 +00:00
parent 4976f3b974
commit 90a7401cf0
No known key found for this signature in database
22 changed files with 325 additions and 184 deletions

View File

@ -1,7 +1,5 @@
{
"intlRelativeTime": "{{val, relativetime}}",
"intlDateTime": "{{val, datetime}}",
"ago": "{{time}} ago",
"joined ago": "Joined {{time}} ago",
"like stored": "Your like has been stored on the blockchain, view it on <2>Crossbell Scan</2>",
"mint stored": "This post has been minted to NFT by you, view it on <2>xChar</2> or <6>Crossbell Scan</6>"
"joined ago": "Joined {{time}} ago"
}

View File

@ -1 +1,3 @@
{}
{
"link post-vs-page" : "https://wordpress.com/zh-cn/support/post-vs-page/"
}

View File

@ -1,5 +1 @@
{
"description": "xLog, <1>on-chain</1> and <3>open-source</3> blogging platform for everyone.",
"discover awesome": "Discover these awesome teams and geeks on xLog (sorted by update time)",
"Integration description": "xLog's open design allows it to integrate with many other open protocols and applications without friction."
}
{}

View File

@ -5,7 +5,7 @@
"Followings": "正在关注",
"Follow": "关注",
"Unfollow": "取消关注",
"intlRelativeTime": "{{val, relativetime}}",
"intlDateTime": "{{val, datetime}}",
"post": "文章",
"posts": "文章",
"page": "页面",

View File

@ -3,6 +3,7 @@
"Posts": "文章",
"Pages": "页面",
"Notifications": "通知",
"Unread notifications": "未读通知",
"Settings": "设置",
"Site Stats": "站点统计",
"Total Posts": "文章总数",
@ -17,7 +18,104 @@
"All Posts": "所有文章",
"All Pages": "所有页面",
"Published": "已发布",
"published": "已发布",
"Draft": "草稿",
"draft": "草稿",
"Scheduled": "定时发布",
"Others on Crossbell": "Crossbell上的其他内容"
"scheduled": "定时发布",
"published and local modified": "已发布并本地修改",
"Others on Crossbell": "Crossbell上的其他内容",
"link post-vs-page" : "https://wordpress.com/zh-cn/support/post-vs-page/",
"posts description": "文章是按时间倒序在你的网站上列出的条目。可以将它们看作是更新,以提供新内容给读者。<2>文章与页面</2>。",
"pages description": "页面是静态的,不受日期影响。它们更像是你网站上的永久元素,比如“关于我们”、“联系我们”等。<2>文章与页面</2>。",
"pages add": "创建页面后,你可以将其<2>添加到你网站的导航菜单中</2>,以便访问者可以找到它。",
"New Post": "新建文章",
"New Page": "新建页面",
"Import": "导入",
"Import markdown file with front matter supported": "导入 Markdown 文件(支持 Front Matter)",
"View Site": "查看站点",
"hello" : {
"welcome" : "<p>你好,</p><p>欢迎使用xLog</p><p>以下是一些有用的链接,帮助你开始使用 xLog</p>",
"community": "<p>加入我们的社区认识新朋友或共同建设xLog</p>"
},
"Edit": "编辑",
"Convert to Page": "转换为页面",
"Convert to Post": "转换为文章",
"Delete": "删除",
"Heading": "标题",
"Bold": "粗体",
"Italic": "斜体",
"Strikethrough": "删除线",
"Underline": "下划线",
"Quote": "引用",
"Inline Code": "行内代码",
"Code Block": "代码块",
"Unordered List": "无序列表",
"Ordered List": "有序列表",
"Link": "链接",
"Image": "图片",
"Upload Image": "上传图片",
"Tip: xLog Flavored Markdown": "提示xLog 风格的 Markdown",
"Preview": "预览",
"Publish": "发布",
"Update": "更新",
"Unpublish": "取消发布",
"Publish at": "发布时间",
"This post will be accessible from this time": "此文章将从此时间开始可访问",
"This page will be accessible from this time": "此页面将从此时间开始可访问",
"Post slug": "文章短链接",
"Page slug": "页面短链接",
"This post will be accessible at": "此文章将可通过以下链接访问",
"This page will be accessible at": "此页面将可通过以下链接访问",
"Tags": "标签",
"Separate multiple tags with English commas": "多个标签请用英文逗号分隔",
"Excerpt": "摘要",
"Leave it blank to use auto-generated excerpt": "留空则使用自动生成的摘要",
"General": "常规",
"Social Platforms": "社交平台",
"Navigation": "导航",
"Domains": "域名",
"Custom CSS": "自定义 CSS",
"Operators": "影子授权",
"Export data": "导出数据",
"Site Settings": "站点设置",
"Icon": "图标",
"Banner": "横幅",
"Supports both pictures and videos.": "支持图片和视频。",
"Name": "名称",
"Description": "描述",
"Integrate Google Analytics": "将 Google Analytics 集成到你的站点中。你可以按照<2>这里</2>的说明查找你的 Measurement ID。",
"Save": "保存",
"Tips": "提示",
"social tips": {
"p1": "这些社交平台将显示在你的 xLog 的右下角。",
"p2": "我们支持<2>这些平台</2>自动显示其标志和链接。对于其他平台将显示默认标志。如果你有其他需求请随时向我们提交问题或PR以支持更多的平台。",
"p3": "你还可以在 <2>xSync</2> 上连接到 Twitter、Telegram 频道、Medium、Substack 等平台,并自动同步内容。当你在那里设置同步后,它也会显示在这里。"
},
"Platform": "平台",
"Identity": "身份",
"Remove": "移除",
"New Item": "新建项目",
"xLog provides some out-of-the-box built-in pages": "xLog 提供了一些开箱即用的内置页面",
"Home page": "首页",
"Archives page": "归档页面",
"Tag page": "标签页面",
"NFT Showcase page": "NFT 展示页面",
"Label": "标签",
"URL": "链接",
"subdomain": "子域名",
"Custom Domain": "自定义域名",
"Set the following record on your DNS provider to active your custom domain": "在你的 DNS 提供商上设置以下记录以激活自定义域名",
"Scope: These styles will be applied to your entire blog, including this dashboard.": "范围:这些样式将应用于你的整个博客,包括这个仪表盘。",
"Support": "支持",
"CSS variables: xLog provides some built-in CSS variables": "CSS 变量xLog 提供了一些内置的 CSS 变量",
"Address": "地址",
"Character": "角色",
"New operator": "新建授权",
"Operator Address": "授权地址",
"Operator Character Check": "授权角色检查",
"Warning": "警告",
"Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site.":
"影子授权可以进入你的仪表盘,更改你的设置(不包括 xLog 子域名)并在你的站点上发布、修改、删除内容。",
"Add": "添加"
}

View File

@ -17,10 +17,10 @@
"Customizable description": "使用自己的<strong>域名</strong>,自定义网站<strong>风格</strong>,完全自由地打造你的网站。这是你的网站,没有任何限制。",
"Open": "开放",
"Open description": "采用标准的 <strong>Markdown</strong> 语法并提供了导入和导出工具以及丰富的API让迁入和迁出变得轻而易举。所有的代码都<strong>开源</strong>在 GitHub 上,所有的<strong>数据都透明</strong>地存储在区块链上。",
"discover awesome": "在 xLog 上发现这些超棒的团队和极客们(按更新时间排序)",
"Discover these awesome teams and geeks on xLog (sorted by update time)": "在 xLog 上发现这些超棒的团队和极客们(按更新时间排序)",
"Follow All!": "关注所有!",
"Show more": "显示更多",
"Submit yours": "提交你的",
"Integration description": "xLog 的开放式设计使其能够与许多其他开放协议和应用程序无缝集成,没有任何限制。",
"xLog's open design allows it to integrate with many other open protocols and applications without friction.": "xLog 的开放式设计使其能够与许多其他开放协议和应用程序无缝集成,没有任何限制。",
"Dashboard": "仪表盘"
}

View File

@ -40,7 +40,7 @@ export function DashboardLayout({
const connectModal = useConnectModal()
const [ready, setReady] = React.useState(false)
const [hasPermission, setHasPermission] = React.useState(false)
const { t } = useTranslation(["common", "dashboard"])
const { t } = useTranslation("dashboard")
useEffect(() => {
if (ssrReady) {
@ -169,9 +169,7 @@ export function DashboardLayout({
onClick={link.onClick}
>
<span className={clsx(link.icon, "text-lg")}></span>
{isOpen && (
<span>{t(link.text, { ns: "dashboard" })}</span>
)}
{isOpen && <span>{t(link.text)}</span>}
</UniLink>
)
})}
@ -185,7 +183,7 @@ export function DashboardLayout({
className="space-x-2 border rounded-lg bg-slate-100 border-slate-200 text-slate-500 hover:text-accent flex w-full h-12 items-center justify-center transition-colors"
>
<span className="i-bi:box-arrow-up-right"></span>
{isOpen && <span>View Site</span>}
{isOpen && <span>{t("View Site")}</span>}
</UniLink>
</div>
</>

View File

@ -25,7 +25,7 @@ import { nanoid } from "nanoid"
import { renderPageContent } from "~/markdown"
import { Tooltip } from "../ui/Tooltip"
import { APP_NAME } from "~/lib/env"
import { useTranslation } from "next-i18next"
import { Trans, useTranslation } from "next-i18next"
export const PagesManager: React.FC<{
isPost: boolean
@ -45,12 +45,12 @@ export const PagesManager: React.FC<{
const createOrUpdatePage = useCreateOrUpdatePage()
const [convertToastId, setConvertToastId] = useState("")
const [deleteToastId, setDeleteToastId] = useState("")
const { t } = useTranslation(["common", "site", "dashboard"])
const { t } = useTranslation(["dashboard", "site"])
const date = useDate()
useEffect(() => {
if (deletePage.isSuccess) {
toast.success(t("Deleted!", { ns: "dashboard" }), {
toast.success(t("Deleted!"), {
id: deleteToastId,
})
}
@ -58,11 +58,11 @@ export const PagesManager: React.FC<{
useEffect(() => {
if (createOrUpdatePage.isSuccess) {
toast.success(t("Converted!", { ns: "dashboard" }), {
toast.success(t("Converted!"), {
id: convertToastId,
})
} else if (createOrUpdatePage.isError) {
toast.error(t("Failed to convert.", { ns: "dashboard" }), {
toast.error(t("Failed to convert."), {
id: convertToastId,
})
}
@ -290,39 +290,42 @@ export const PagesManager: React.FC<{
const description = isPost ? (
<>
<p>
<UniLink
className="underline"
href="https://wordpress.com/support/post-vs-page/"
>
Post vs. Page
</UniLink>
. Posts are entries listed in reverse chronological order on your site.
Think of them as articles or updates that you share to offer up new
content to your readers.
<Trans i18nKey="posts description" ns="dashboard">
Posts are entries listed in reverse chronological order on your site.
Think of them as articles or updates that you share to offer up new
content to your readers.{" "}
<UniLink className="underline" href={t("link post-vs-page") || ""}>
Post vs. Page
</UniLink>
</Trans>
</p>
</>
) : (
<>
<p>
<UniLink
className="underline"
href="https://wordpress.com/support/post-vs-page/"
>
Post vs. Page
</UniLink>
. Pages are static and are not affected by date. Think of them as more
permanent fixtures of your site an About page, and a Contact page are
great examples of this.
<Trans i18nKey="pages description" ns="dashboard">
Pages are static and are not affected by date. Think of them as more
permanent fixtures of your site an About page, and a Contact page
are great examples of this.{" "}
<UniLink
className="underline"
href="https://wordpress.com/support/post-vs-page/"
>
Post vs. Page
</UniLink>
</Trans>
</p>
<p>
After you create a page, you can{" "}
<UniLink
className="underline"
href={`/dashboard/${subdomain}/settings/navigation`}
>
add it to your site&apos;s navigation menu
</UniLink>{" "}
so your visitors can find it.
<Trans i18nKey="pages add" ns="dashboard">
After you create a page, you can{" "}
<UniLink
className="underline"
href={`/dashboard/${subdomain}/settings/navigation`}
>
add it to your site&apos;s navigation menu
</UniLink>{" "}
so your visitors can find it.
</Trans>
</p>
</>
)
@ -333,9 +336,7 @@ export const PagesManager: React.FC<{
<DashboardMain>
<header className="mb-8">
<div className="flex justify-between items-center mb-4">
<h2 className="text-2xl font-bold">
{t(title, { ns: "dashboard" })}
</h2>
<h2 className="text-2xl font-bold">{t(title)}</h2>
<div className="flex justify-center items-center space-x-4">
<Button
className={clsx(`space-x-2 inline-flex`)}
@ -359,16 +360,16 @@ export const PagesManager: React.FC<{
<path d="M12 20h9"></path>
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
</svg>
<span>New {isPost ? "Post" : "Page"}</span>
<span>{t(`New ${isPost ? "Post" : "Page"}`)}</span>
</Button>
<span className="hidden sm:inline-flex">
<Tooltip
label="Import markdown file with front matter supported"
label={t("Import markdown file with front matter supported")}
placement="bottom"
>
<Button className={clsx(`space-x-2`)} onClick={importFile}>
<span className="i-bxs:duplicate inline-block"></span>
<span>Import</span>
<span>{t("Import")}</span>
</Button>
</Tooltip>
</span>
@ -406,7 +407,7 @@ export const PagesManager: React.FC<{
)}
<div className="text-zinc-400 text-xs mt-1">
<span className="capitalize">
{getPageVisibility(page).toLowerCase()}
{t(getPageVisibility(page))}
</span>
<span className="mx-2">·</span>
<span>
@ -448,7 +449,7 @@ export const PagesManager: React.FC<{
<span className="inline-flex">
{item.icon}
</span>
<span>{item.text}</span>
<span>{t(item.text)}</span>
</button>
</Menu.Item>
)

View File

@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from "react"
import useOnClickOutside from "use-onclickoutside"
import { Button, ButtonGroup } from "../ui/Button"
import { useTranslation } from "next-i18next"
export const PublishButton: React.FC<{
save: (published: boolean) => void
@ -12,6 +13,7 @@ export const PublishButton: React.FC<{
const [showDropdown, setShowDropdown] = useState(false)
const dropdownRef = useRef<HTMLDivElement | null>(null)
const triggerRef = useRef<HTMLButtonElement | null>(null)
const { t } = useTranslation("dashboard")
useOnClickOutside(dropdownRef, (e) => {
if (triggerRef.current?.contains(e.target as any)) return
@ -34,7 +36,7 @@ export const PublishButton: React.FC<{
isLoading={isSaving}
isDisabled={isDisabled}
>
{published ? "Update" : "Publish"}
{t(published ? "Update" : "Publish")}
</Button>
{published && (
<Button
@ -60,7 +62,7 @@ export const PublishButton: React.FC<{
className="flex w-full h-8 hover:bg-zinc-100 items-center px-5"
onClick={() => save(false)}
>
Unpublish
{t("Unpublish")}
</button>
)}
</div>

View File

@ -3,6 +3,7 @@ import React from "react"
import { type TabItem, Tabs } from "../ui/Tabs"
import { DashboardMain } from "./DashboardMain"
import { useTranslation } from "next-i18next"
export const SettingsLayout: React.FC<{
title: string
@ -10,6 +11,7 @@ export const SettingsLayout: React.FC<{
type: "site" | "account"
}> = ({ title, children, type }) => {
const router = useRouter()
const { t } = useTranslation("dashboard")
const subdomain = router.query.subdomain as string
const tabItems: TabItem[] = (
@ -42,7 +44,7 @@ export const SettingsLayout: React.FC<{
<DashboardMain>
<div className="">
<header className="mb-8">
<h2 className="text-2xl font-bold">{title}</h2>
<h2 className="text-2xl font-bold">{t(title)}</h2>
</header>
<div>
<Tabs items={tabItems} />

View File

@ -3,6 +3,7 @@ import clsx from "clsx"
import { Dispatch, FC, SetStateAction } from "react"
import { ICommand } from "~/editor"
import { Tooltip } from "./Tooltip"
import { useTranslation } from "next-i18next"
export interface EditorToolbarProps {
view?: EditorView
@ -15,12 +16,13 @@ enum ToolbarMode {
}
export const EditorToolbar: FC<EditorToolbarProps> = ({ view, toolbars }) => {
const { t } = useTranslation("dashboard")
const renderToolbar =
(mode: ToolbarMode) =>
// eslint-disable-next-line react/display-name
({ name, icon, label, execute }: ICommand) => {
return (
<Tooltip key={name} label={label} placement="bottom">
<Tooltip key={name} label={t(label)} placement="bottom">
<button
key={name}
type="button"

View File

@ -26,7 +26,7 @@ export enum PageVisibilityEnum {
Scheduled = "scheduled",
Draft = "draft",
Crossbell = "crossbell",
Modified = "published and modified",
Modified = "published and local modified",
}
export type PostOnSiteHome = {

View File

@ -1,13 +0,0 @@
import { Html, Head, Main, NextScript, DocumentProps } from "next/document"
export default function Document(props: DocumentProps) {
return (
<Html lang="en">
<Head></Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

View File

@ -64,6 +64,7 @@ export default function SubdomainEditor() {
const router = useRouter()
const queryClient = useQueryClient()
const date = useDate()
const { t } = useTranslation("dashboard")
let pageId = router.query.id as string | undefined
const subdomain = router.query.subdomain as string
@ -394,7 +395,7 @@ export default function SubdomainEditor() {
<DashboardMain fullWidth>
{page.isLoading ? (
<div className="flex justify-center items-center min-h-[300px]">
Loading...
{t("Loading")}...
</div>
) : (
<>
@ -407,7 +408,7 @@ export default function SubdomainEditor() {
subdomain: "xlog",
})}/xfm`}
>
Tip: xLog Flavored Markdown
{t("Tip: xLog Flavored Markdown")}
</UniLink>
</div>
<div className="flex items-center space-x-3">
@ -421,7 +422,7 @@ export default function SubdomainEditor() {
: "text-green-600",
)}
>
{visibility?.toLowerCase()}
{t(visibility as string)}
</span>
<Button
isAutoWidth
@ -434,7 +435,7 @@ export default function SubdomainEditor() {
)
}}
>
Preview
{t("Preview")}
</Button>
<PublishButton
save={savePage}
@ -493,7 +494,7 @@ export default function SubdomainEditor() {
<div>
<Input
type="datetime-local"
label="Publish at"
label={t("Publish at") || ""}
isBlock
name="publishAt"
id="publishAt"
@ -507,9 +508,11 @@ export default function SubdomainEditor() {
.toISOString()
updateValue("publishedAt", value)
}}
help={`This ${
isPost ? "post" : "page"
} will be accessible from this time`}
help={t(
`This ${
isPost ? "post" : "page"
} will be accessible from this time`,
)}
/>
</div>
<div>
@ -517,7 +520,7 @@ export default function SubdomainEditor() {
name="slug"
value={values.slug}
placeholder={defaultSlug}
label={`${isPost ? "Post" : "Page"} slug`}
label={t(`${isPost ? "Post" : "Page"} slug`) || ""}
id="slug"
isBlock
onChange={(e: ChangeEvent<HTMLInputElement>) =>
@ -527,8 +530,11 @@ export default function SubdomainEditor() {
<>
{(values.slug || defaultSlug) && (
<>
This {isPost ? "post" : "page"} will be accessible
at{" "}
{t(
`This ${
isPost ? "post" : "page"
} will be accessible at`,
)}{" "}
<UniLink
href={`${getSiteLink({
subdomain,
@ -553,18 +559,20 @@ export default function SubdomainEditor() {
<Input
name="tags"
value={values.tags}
label="Tags"
label={t("Tags") || ""}
id="tags"
isBlock
onChange={(e: ChangeEvent<HTMLInputElement>) =>
updateValue("tags", e.target.value)
}
help='Separate multiple tags with English commas ","'
help={
t("Separate multiple tags with English commas") + ` ","`
}
/>
</div>
<div>
<Input
label="Excerpt"
label={t("Excerpt") || ""}
isBlock
name="excerpt"
id="excerpt"
@ -574,7 +582,7 @@ export default function SubdomainEditor() {
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => {
updateValue("excerpt", e.target.value)
}}
help="Leave it blank to use auto-generated excerpt"
help={t("Leave it blank to use auto-generated excerpt")}
/>
</div>
</div>

View File

@ -8,7 +8,7 @@ import { getSiteLink } from "~/lib/helpers"
import type { ReactElement } from "react"
import { useGetSite, useGetStat } from "~/queries/site"
import { useDate } from "~/hooks/useDate"
import { useTranslation } from "next-i18next"
import { useTranslation, Trans } from "next-i18next"
import { getServerSideProps as getLayoutServerSideProps } from "~/components/dashboard/DashboardLayout.server"
import { GetServerSideProps } from "next"
import { serverSidePropsHandler } from "~/lib/server-side-props"
@ -81,9 +81,14 @@ export default function SubdomainIndex() {
</div>
))}
</div>
<p>Hello there,</p>
<p>Welcome to use {APP_NAME}!</p>
<p>Here{`'`}re some useful links to get started:</p>
<Trans
i18nKey="hello.welcome"
defaults="<p>Hello there,</p><p>Welcome to use xLog!</p><p>Here're some useful links to get started:</p>"
components={{
p: <p />,
}}
ns="dashboard"
/>
<ul>
<li>
<UniLink href={`/dashboard/${subdomain}/editor?type=post`}>
@ -115,7 +120,14 @@ export default function SubdomainIndex() {
via RSS Reader
</li>
</ul>
<p>Join the community to meet friends or build {APP_NAME} together:</p>
<Trans
i18nKey="hello.community"
defaults="<p>Join the community to meet friends or build xLog together:</p>"
components={{
p: <p />,
}}
ns="dashboard"
/>
<ul>
<li>
<UniLink href={DISCORD_LINK}>

View File

@ -31,6 +31,7 @@ export default function SettingsCSSPage() {
const updateSite = useUpdateSite()
const site = useGetSite(subdomain)
const { t } = useTranslation("dashboard")
const [css, setCss] = useState("")
const handleSubmit = (e: any) => {
@ -66,38 +67,39 @@ export default function SettingsCSSPage() {
<form onSubmit={handleSubmit}>
<div className="">
<div className="p-5 text-zinc-500 bg-zinc-50 mb-5 rounded-lg text-xs space-y-2">
<p className="text-zinc-800 text-sm font-bold">Scope</p>
<p className="text-zinc-800 text-sm font-bold">{t("Tips")}:</p>
<p>
These styles will be applied to your entire blog, including this
dashboard.
{t(
"Scope: These styles will be applied to your entire blog, including this dashboard.",
)}
</p>
<p>
Support <code>ipfs://</code>
</p>
<p className="text-zinc-800 text-sm font-bold">
Tips: you can override the built-in CSS variables
{t("Support")} <code>ipfs://</code>
</p>
<p>
{t("CSS variables: xLog provides some built-in CSS variables")}
</p>
<p className="pl-2">
<span className="bg-zinc-200 rounded-lg px-2">
--theme-color: #4f46e5;
</span>
</p>
<p>
<p className="pl-2">
<span className="bg-zinc-200 rounded-lg px-2">
--theme-color-emphasis: #4338ca;
</span>
</p>
<p>
<p className="pl-2">
<span className="bg-zinc-200 rounded-lg px-2">
--header-height: auto;
</span>
</p>
<p>
<p className="pl-2">
<span className="bg-zinc-200 rounded-lg px-2">
--banner-bg-color: #000;
</span>
</p>
<p>
<p className="pl-2">
<span className="bg-zinc-200 rounded-lg px-2">
--font-fans: ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto,
@ -108,7 +110,7 @@ export default function SettingsCSSPage() {
</span>
</p>
</div>
<FieldLabel label="Custom CSS" />
<FieldLabel label={t("Custom CSS")} />
<Editor
className="w-full h-96 border outline-none py-3 rounded-lg inline-flex items-center overflow-hidden"
defaultLanguage="css"
@ -121,7 +123,7 @@ export default function SettingsCSSPage() {
</div>
<div className="mt-5">
<Button type="submit" isLoading={updateSite.isLoading}>
Save
{t("Save")}
</Button>
</div>
</form>

View File

@ -37,6 +37,7 @@ export default function SettingsDomainsPage() {
const updateSite = useUpdateSite()
const site = useGetSite(subdomain)
const userRole = useUserRole(subdomain)
const { t } = useTranslation("dashboard")
const isEmailAccount = useAccountState(
(s) => s.computed.account?.type === "email",
@ -98,7 +99,7 @@ export default function SettingsDomainsPage() {
<div>
<Input
id="subdomain"
label={`${APP_NAME} subdomain`}
label={`xLog ${t("subdomain")}`}
addon={`.${OUR_DOMAIN}`}
className="w-28"
{...form.register("subdomain")}
@ -137,15 +138,17 @@ export default function SettingsDomainsPage() {
<div className="mt-5">
<Input
id="custom_domain"
label="Custom Domain"
label={t("Custom Domain") || ""}
className="w-64"
{...form.register("custom_domain")}
/>
{customDomain && (
<div className="mt-2 text-xs">
<p className="mb-2">
Set the following record on your DNS provider to active your
custom domain:
{t(
"Set the following record on your DNS provider to active your custom domain",
)}
:
</p>
<table className="">
<tbody>
@ -175,7 +178,7 @@ export default function SettingsDomainsPage() {
</div>
<div className="mt-5">
<Button type="submit" isLoading={updateSite.isLoading}>
Save
{t("Save")}
</Button>
</div>
</form>

View File

@ -11,7 +11,7 @@ import { UniLink } from "~/components/ui/UniLink"
import { ImageUploader } from "~/components/ui/ImageUploader"
import { toIPFS } from "~/lib/ipfs-parser"
import type { ReactElement } from "react"
import { useTranslation } from "next-i18next"
import { useTranslation, Trans } from "next-i18next"
import { getServerSideProps as getLayoutServerSideProps } from "~/components/dashboard/DashboardLayout.server"
import { GetServerSideProps } from "next"
import { serverSidePropsHandler } from "~/lib/server-side-props"
@ -35,6 +35,7 @@ export default function SiteSettingsGeneralPage() {
const updateSite = useUpdateSite()
const site = useGetSite(subdomain)
const { t } = useTranslation("dashboard")
const form = useForm({
defaultValues: {
@ -107,7 +108,7 @@ export default function SiteSettingsGeneralPage() {
<form onSubmit={handleSubmit}>
<div className="mt-5">
<label htmlFor="icon" className="form-label">
Icon
{t("Icon")}
</label>
<Controller
name="icon"
@ -130,7 +131,7 @@ export default function SiteSettingsGeneralPage() {
</div>
<div className="mt-5">
<label htmlFor="icon" className="form-label">
Banner
{t("Banner")}
</label>
<Controller
name="banner"
@ -156,15 +157,20 @@ export default function SiteSettingsGeneralPage() {
)}
/>
<div className="text-xs text-gray-400 mt-1">
Supports both pictures and videos.
{t("Supports both pictures and videos.")}
</div>
</div>
<div className="mt-5">
<Input required label="Name" id="name" {...form.register("name")} />
<Input
required
label={t("Name") || ""}
id="name"
{...form.register("name")}
/>
</div>
<div className="mt-5">
<label htmlFor="description" className="form-label">
Description
{t("Description")}
</label>
<Input
multiline
@ -182,15 +188,17 @@ export default function SiteSettingsGeneralPage() {
label="Google Analytics"
help={
<p>
Integrate Google Analytics into your site. You can follow the
instructions{" "}
<UniLink
className="underline"
href="https://support.google.com/analytics/answer/9539598"
>
here
</UniLink>{" "}
to find your Measurement ID.
<Trans i18nKey="Integrate Google Analytics" ns="dashboard">
Integrate Google Analytics into your site. You can follow the
instructions{" "}
<UniLink
className="underline"
href="https://support.google.com/analytics/answer/9539598"
>
here
</UniLink>{" "}
to find your Measurement ID.
</Trans>
</p>
}
/>
@ -201,7 +209,7 @@ export default function SiteSettingsGeneralPage() {
isLoading={updateSite.isLoading}
isDisabled={iconUploading || bannerUploading}
>
Save
{t("Save")}
</Button>
</div>
</form>

View File

@ -37,6 +37,7 @@ const SortableNavigationItem: React.FC<{
updateItem: UpdateItem
removeItem: RemoveItem
}> = ({ item, updateItem, removeItem }) => {
const { t } = useTranslation("dashboard")
return (
<div className="flex space-x-5 border-b p-5 bg-zinc-50 last:border-0">
<div>
@ -53,7 +54,7 @@ const SortableNavigationItem: React.FC<{
</button>
</div>
<Input
label="Label"
label={t("Label") || ""}
required
id={`${item.id}-label`}
value={item.label}
@ -62,7 +63,7 @@ const SortableNavigationItem: React.FC<{
}
/>
<Input
label="URL"
label={t("URL") || ""}
required
id={`${item.id}-url`}
type="text"
@ -75,7 +76,7 @@ const SortableNavigationItem: React.FC<{
/>
<div className="flex items-end relative -top-[5px]">
<Button onClick={() => removeItem(item.id)} variantColor="red">
Remove
{t("Remove")}
</Button>
</div>
</div>
@ -89,6 +90,7 @@ export default function SiteSettingsNavigationPage() {
const updateSite = useUpdateSite()
const site = useGetSite(subdomain)
const { t } = useTranslation("dashboard")
const [items, setItems] = useState<SiteNavigationItem[]>([])
@ -147,23 +149,26 @@ export default function SiteSettingsNavigationPage() {
return (
<SettingsLayout title="Site Settings" type="site">
<div className="p-5 text-zinc-500 bg-zinc-50 mb-5 rounded-lg text-xs space-y-2">
<p className="text-zinc-800 text-sm font-bold">Tips: built-in pages</p>
<p className="text-zinc-800 text-sm font-bold">{t("Tips")}:</p>
<p>
<span className="text-zinc-800">
Home page (use your own Label to replace the default Label):
</span>{" "}
<span className="text-zinc-800 font-medium">
{t("xLog provides some out-of-the-box built-in pages")}:
</span>
</p>
<p>
<span className="text-zinc-800">- {t("Home page")}:</span>{" "}
<span className="bg-zinc-200 rounded-lg px-2">/</span>
</p>
<p>
<span className="text-zinc-800">Archives page:</span>{" "}
<span className="text-zinc-800">- {t("Archives page")}:</span>{" "}
<span className="bg-zinc-200 rounded-lg px-2">/archives</span>
</p>
<p>
<span className="text-zinc-900">Tag page:</span>{" "}
<span className="text-zinc-900">- {t("Tag page")}:</span>{" "}
<span className="bg-zinc-200 rounded-lg px-2">/tag/[tag]</span>
</p>
<p>
<span className="text-zinc-900">NFT Showcase page:</span>{" "}
<span className="text-zinc-900">- {t("NFT Showcase page")}:</span>{" "}
<span className="bg-zinc-200 rounded-lg px-2">/nft</span>
</p>
</div>
@ -198,10 +203,10 @@ export default function SiteSettingsNavigationPage() {
isLoading={updateSite.isLoading}
isDisabled={!itemsModified}
>
Save
{t("Save")}
</Button>
<Button variant="secondary" onClick={newEmptyItem}>
New Item
{t("New Item")}
</Button>
</div>
</form>

View File

@ -43,11 +43,14 @@ const SortableNavigationItem: React.FC<{
isLoading: boolean
disabled?: boolean
}> = ({ item, removeItem, isLoading, disabled }) => {
const { t } = useTranslation("dashboard")
return (
<div className="flex space-x-5 border-b p-5 bg-zinc-50 last:border-0 items-center">
<div className="text-sm space-y-4">
<div>Address: {item}</div>
<div>Character:</div>
<div>
{t("Address")}: {item}
</div>
<div>{t("Character")}:</div>
<CharacterCard
address={item}
open={true}
@ -62,7 +65,7 @@ const SortableNavigationItem: React.FC<{
isLoading={isLoading}
isDisabled={disabled}
>
Remove
{t("Remove")}
</Button>
</div>
</div>
@ -85,6 +88,7 @@ export default function SettingsOperatorPage() {
)
const upgradeAccountModal = useUpgradeAccountModal()
const userRole = useUserRole(subdomain)
const { t } = useTranslation("dashboard")
const [items, setItems] = useState<string[]>([])
@ -144,19 +148,21 @@ export default function SettingsOperatorPage() {
<div className="relative bg-white rounded-lg max-w-md w-full mx-auto">
<Dialog.Title className="px-5 h-12 flex items-center border-b">
New operator
{t("New operator")}
</Dialog.Title>
<div className="p-5">
<Input
className="w-full"
label="Operator Address"
label={t("Operator Address") || ""}
required
onChange={(e) => {
setAddress(e.target.value)
}}
/>
<div className="form-label mt-5">Operator Character Check</div>
<div className="form-label mt-5">
{t("Operator Character Check")}
</div>
<div>
<CharacterCard
address={address}
@ -173,14 +179,14 @@ export default function SettingsOperatorPage() {
isDisabled={!address}
onClick={addItem}
>
Save
{t("Save")}
</Button>
</div>
</div>
</div>
</Dialog>
<div className="p-5 text-zinc-500 bg-orange-50 mb-5 rounded-lg text-sm space-y-2">
<p className="text-zinc-800 text-sm font-bold"> Warning:</p>
<p className="text-zinc-800 text-sm font-bold"> {t("Warning")}:</p>
<p>
<span className="text-zinc-800">
{isEmailAccount && (
@ -214,9 +220,9 @@ export default function SettingsOperatorPage() {
)}
{!isEmailAccount && userRole.data !== "operator" && (
<span>
Operators have permissions to enter your dashboard, change your
settings(excluding xLog subdomain) and post contents on your
site.
{t(
"Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site.",
)}
</span>
)}
</span>
@ -257,7 +263,7 @@ export default function SettingsOperatorPage() {
onClick={newEmptyItem}
isDisabled={isEmailAccount || userRole.data === "operator"}
>
Add
{t("Add")}
</Button>
</div>
</div>

View File

@ -13,7 +13,7 @@ import { useGetSite, useUpdateSite } from "~/queries/site"
import type { ReactElement } from "react"
import { UniLink } from "~/components/ui/UniLink"
import { Platform } from "~/components/site/Platform"
import { useTranslation } from "next-i18next"
import { useTranslation, Trans } from "next-i18next"
import { getServerSideProps as getLayoutServerSideProps } from "~/components/dashboard/DashboardLayout.server"
import { GetServerSideProps } from "next"
import { serverSidePropsHandler } from "~/lib/server-side-props"
@ -43,6 +43,7 @@ const SortableNavigationItem: React.FC<{
updateItem: UpdateItem
removeItem: RemoveItem
}> = ({ item, updateItem, removeItem }) => {
const { t } = useTranslation("dashboard")
return (
<div className="flex space-x-5 border-b p-5 bg-zinc-50 last:border-0">
<div>
@ -59,7 +60,7 @@ const SortableNavigationItem: React.FC<{
</button>
</div>
<Input
label="Platform"
label={t("Platform") || ""}
required
id={`${item.id}-platform`}
value={item.platform}
@ -82,7 +83,7 @@ const SortableNavigationItem: React.FC<{
</div>
<div className="flex items-end relative -top-[5px]">
<Button onClick={() => removeItem(item.id)} variantColor="red">
Remove
{t("Remove")}
</Button>
</div>
</div>
@ -96,6 +97,7 @@ export default function SiteSettingsNavigationPage() {
const updateSite = useUpdateSite()
const site = useGetSite(subdomain)
const { t } = useTranslation("dashboard")
const [items, setItems] = useState<Item[]>([])
@ -162,35 +164,36 @@ export default function SiteSettingsNavigationPage() {
return (
<SettingsLayout title="Site Settings" type="site">
<div className="p-5 text-zinc-500 bg-zinc-50 mb-5 rounded-lg text-xs space-y-2">
<p className="text-zinc-800 text-sm font-bold">Tips:</p>
<p className="text-zinc-800 text-sm font-bold">{t("Tips")}:</p>
<p>
<span className="text-zinc-800">{t("social tips.p1")}</span>
</p>
<p>
<span className="text-zinc-800">
These social platforms will be displayed in the bottom right hand
corner of your xLog.
<Trans ns="dashboard" i18nKey="social tips.p2">
We support{" "}
<UniLink
href="https://github.com/Crossbell-Box/xLog/blob/dev/src/components/site/Platform.tsx#L7"
className="underline"
>
these platforms
</UniLink>{" "}
with automatic display of logos and links, other platforms will
display a default logo. Please feel free to submit an issue or pr
to us to support more platforms.
</Trans>
</span>
</p>
<p>
<span className="text-zinc-800">
We support{" "}
<UniLink
href="https://github.com/Crossbell-Box/xLog/blob/dev/src/components/site/Platform.tsx#L7"
className="underline"
>
these platforms
</UniLink>{" "}
with automatic display of logos and links, other platforms will
display a default logo. Please feel free to submit an issue or pr to
us to support more platforms.
</span>
</p>
<p>
<span className="text-zinc-800">
You can also connect to Twitter, Telegram Channel, Medium, Substack
and more and automatically sync content on{" "}
<UniLink href="https://xsync.app/" className="underline">
xSync
</UniLink>
. When you have set up the sync there, it will also show here.
<Trans ns="dashboard" i18nKey="social tips.p3">
You can also connect to Twitter, Telegram Channel, Medium,
Substack and more and automatically sync content on{" "}
<UniLink href="https://xsync.app/" className="underline">
xSync
</UniLink>
. When you have set up the sync there, it will also show here.
</Trans>
</span>
</p>
</div>
@ -225,10 +228,10 @@ export default function SiteSettingsNavigationPage() {
isLoading={updateSite.isLoading}
isDisabled={!itemsModified}
>
Save
{t("Save")}
</Button>
<Button variant="secondary" onClick={newEmptyItem}>
New Item
{t("New Item")}
</Button>
</div>
</form>

View File

@ -315,7 +315,11 @@ export default function Home() {
<Element name="Showcase">
<div className="pt-28 text-4xl font-bold">{t("Showcase")}</div>
<div className="my-10 text-zinc-700">
<p className="text-lg">{t("discover awesome")}</p>
<p className="text-lg">
{t(
"Discover these awesome teams and geeks on xLog (sorted by update time)",
)}
</p>
<Button
size="xl"
className="mt-5"
@ -394,7 +398,11 @@ export default function Home() {
<Element name="Integration">
<div className="pt-28 text-4xl font-bold">{t("Integration")}</div>
<div className="my-10 text-zinc-700">
<p className="text-xl">{t("Integration description")}</p>
<p className="text-xl">
{t(
"xLog's open design allows it to integrate with many other open protocols and applications without friction.",
)}
</p>
<ul className="mt-14 grid grid-cols-3 sm:grid-cols-5 gap-y-14 gap-x-2">
{integrations.map((item, index) => (
<li