Improved user interface translations and clarity in Simplified

This commit is contained in:
oiov
2025-06-06 15:10:11 +08:00
parent dc60a00103
commit ca35d96925
13 changed files with 254 additions and 100 deletions
@@ -4,6 +4,7 @@ import { useState } from "react";
import Link from "next/link";
import { User } from "@prisma/client";
import { PenLine, RefreshCwIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import useSWR, { useSWRConfig } from "swr";
@@ -70,6 +71,7 @@ function TableColumnSekleton() {
export default function DomainList({ user, action }: DomainListProps) {
const { isMobile } = useMediaQuery();
const t = useTranslations("List");
const [isShowForm, setShowForm] = useState(false);
const [formType, setFormType] = useState<FormType>("add");
const [currentEditDomain, setCurrentEditDomain] =
-2
View File
@@ -1,10 +1,8 @@
import { Skeleton } from "@/components/ui/skeleton";
import { DashboardHeader } from "@/components/dashboard/header";
export default function DashboardLoading() {
return (
<>
<DashboardHeader heading="Dashboard" text="" />
<div className="flex flex-col gap-5">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3 lg:grid-cols-3">
<Skeleton className="h-32 w-full rounded-lg" />
+1 -7
View File
@@ -13,11 +13,9 @@ import {
DashboardInfoCard,
HeroCard,
} from "@/components/dashboard/dashboard-info-card";
import { DashboardHeader } from "@/components/dashboard/header";
import { ErrorBoundary } from "@/components/shared/error-boundary";
import UserRecordsList from "./records/record-list";
import LiveLog from "./urls/live-logs";
import UserUrlsList from "./urls/url-list";
export const metadata = constructMetadata({
@@ -87,10 +85,6 @@ async function DnsRecordsCardSection({
);
}
async function LiveLogSection() {
return <LiveLog admin={false} />;
}
async function UserUrlsListSection({
user,
}: {
@@ -148,7 +142,7 @@ export default async function DashboardPage() {
return (
<>
<DashboardHeader heading="Dashboard" text="" />
{/* <DashboardHeader heading="Dashboard" text="" /> */}
<div className="flex flex-col gap-5">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3 xl:grid-cols-3">
<ErrorBoundary
@@ -4,6 +4,7 @@ import { useState } from "react";
import Link from "next/link";
import { User } from "@prisma/client";
import { PenLine, RefreshCwIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import useSWR, { useSWRConfig } from "swr";
@@ -87,9 +88,10 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
useState<UserRecordFormData | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [tab, setTab] = useState("app");
const isAdmin = action.includes("/admin");
const t = useTranslations("List");
const { mutate } = useSWRConfig();
const { data, isLoading } = useSWR<{
@@ -144,30 +146,30 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
<CardHeader className="flex flex-row items-center">
{isAdmin ? (
<CardDescription className="text-balance text-lg font-bold">
<span>Total Subdomains:</span>{" "}
<span>{t("Total Subdomains")}:</span>{" "}
<span className="font-bold">{data && data.total}</span>
</CardDescription>
) : (
<div className="grid gap-2">
<CardTitle>Subdomains</CardTitle>
<CardTitle>{t("Subdomain List")}</CardTitle>
<CardDescription className="hidden text-balance sm:block">
Please read the{" "}
{t("Please read the")}{" "}
<Link
target="_blank"
className="font-semibold text-yellow-600 after:content-['↗'] hover:underline"
href="/docs/dns-records#legitimacy-review"
>
Legitimacy review
{t("legitimacy review")}
</Link>{" "}
before using. See{" "}
{t("before using")}. {t("See")}{" "}
<Link
target="_blank"
className="text-blue-500 hover:underline"
href="/docs/examples/vercel"
>
examples
{t("examples")}
</Link>{" "}
for more usage.
{t("for more usage")}.
</CardDescription>
</div>
)}
@@ -194,7 +196,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
}}
>
<Icons.add className="size-4" />
<span className="hidden sm:inline">Add Record</span>
<span className="hidden sm:inline">{t("Add Record")}</span>
</Button>
</div>
</CardHeader>
@@ -203,28 +205,28 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
<TableHeader className="bg-gray-100/50 dark:bg-primary-foreground">
<TableRow className="grid grid-cols-3 items-center sm:grid-cols-9">
<TableHead className="col-span-1 flex items-center font-bold">
Type
{t("Type")}
</TableHead>
<TableHead className="col-span-1 flex items-center font-bold">
Name
{t("Name")}
</TableHead>
<TableHead className="col-span-2 hidden items-center font-bold sm:flex">
Content
{t("Content")}
</TableHead>
<TableHead className="col-span-1 hidden items-center font-bold sm:flex">
TTL
{t("TTL")}
</TableHead>
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
Status
{t("Status")}
</TableHead>
<TableHead className="col-span-1 hidden items-center font-bold sm:flex">
User
{t("User")}
</TableHead>
<TableHead className="col-span-1 hidden items-center justify-center font-bold sm:flex">
Updated
{t("Updated")}
</TableHead>
<TableHead className="col-span-1 flex items-center justify-center font-bold">
Actions
{t("Actions")}
</TableHead>
</TableRow>
</TableHeader>
@@ -279,7 +281,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
/>
) : (
<Badge className="rounded-md" variant={"yellow"}>
Pending
{t("Pending")}
</Badge>
)}
{record.active !== 1 && (
@@ -291,16 +293,21 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
<TooltipContent>
{record.active === 0 && (
<ul className="list-disc px-3">
<li>The target is currently inaccessible.</li>
<li>
Please check the target and try again.
{t("The target is currently inaccessible")}.
</li>
<li>
If the target is not activated within 3
days, <br />
the administrator will{" "}
{t("Please check the target and try again")}
.
</li>
<li>
{t(
"If the target is not activated within 3 days",
)}
, <br />
{t("the administrator will")}{" "}
<strong className="text-red-500">
delete this record
{t("delete this record")}
</strong>
.
</li>
@@ -309,8 +316,10 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
{record.active === 2 && (
<ul className="list-disc px-3">
<li>
The record is currently pending for admin
approval.
{t(
"The record is currently pending for admin approval",
)}
.
</li>
</ul>
)}
@@ -347,7 +356,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
setShowForm(!isShowForm);
}}
>
<p>Edit</p>
<p>{t("Edit")}</p>
<PenLine className="ml-1 size-4" />
</Button>
) : record.active === 2 &&
@@ -364,7 +373,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
setShowForm(!isShowForm);
}}
>
<p>Review</p>
<p>{t("Review")}</p>
</Button>
) : (
"--"
@@ -375,7 +384,9 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
) : (
<EmptyPlaceholder className="shadow-none">
<EmptyPlaceholder.Icon name="globe" />
<EmptyPlaceholder.Title>No Subdomain</EmptyPlaceholder.Title>
<EmptyPlaceholder.Title>
{t("No Subdomains")}
</EmptyPlaceholder.Title>
<EmptyPlaceholder.Description>
You don&apos;t have any subdomain yet. Start creating
record.
@@ -398,7 +409,7 @@ export default function UserRecordsList({ user, action }: RecordListProps) {
</Card>
<Modal
className="max-h-[90vh] overflow-y-auto md:max-w-2xl"
className="max-h-[99vh] overflow-y-auto md:max-w-2xl"
showModal={isShowForm}
setShowModal={setShowForm}
>
+5 -5
View File
@@ -28,19 +28,19 @@ export default async function DashboardPage() {
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
<StaticInfoCard
title="Url to Screenshot"
desc="Take a screenshot of the webpage."
desc="Take a screenshot of the webpage"
link="/dashboard/scrape/screenshot"
icon="camera"
/>
<StaticInfoCard
title="Url to Meta Info"
desc="Extract website metadata."
desc="Extract website metadata"
link="/dashboard/scrape/meta-info"
icon="globe"
/>
<StaticInfoCard
title="Url to QR Code"
desc="Generate QR Code from URL."
desc="Generate QR Code from URL"
link="/dashboard/scrape/qrcode"
icon="qrcode"
/>
@@ -48,13 +48,13 @@ export default async function DashboardPage() {
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<StaticInfoCard
title="Url to Markdown"
desc="Convert website content to Markdown format."
desc="Convert website content to Markdown format"
link="/dashboard/scrape/markdown"
icon="heading1"
/>
<StaticInfoCard
title="Url to Text"
desc="Extract website text."
desc="Convert website content to text"
link="/dashboard/scrape/markdown"
icon="fileText"
/>
+1 -1
View File
@@ -187,7 +187,7 @@ export default function UserUrlsList({ user, action }: UrlListProps) {
const rendeEmpty = () => (
<EmptyPlaceholder className="col-span-full shadow-none">
<EmptyPlaceholder.Icon name="link" />
<EmptyPlaceholder.Title>No urls</EmptyPlaceholder.Title>
<EmptyPlaceholder.Title>{t("No urls")}</EmptyPlaceholder.Title>
<EmptyPlaceholder.Description>
You don&apos;t have any url yet. Start creating url.
</EmptyPlaceholder.Description>
+18 -9
View File
@@ -1,5 +1,5 @@
import Link from "next/link";
import { Link as LinkIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { nFormatter } from "@/lib/utils";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
@@ -72,6 +72,7 @@ export async function DashboardInfoCard({
link: string;
icon?: keyof typeof Icons;
}) {
const t = useTranslations("Components");
const Icon = Icons[icon || "arrowRight"];
return (
<Card className="grids group animate-fade-in bg-gray-50/70 backdrop-blur-lg dark:bg-primary-foreground">
@@ -81,7 +82,7 @@ export async function DashboardInfoCard({
className="font-semibold text-slate-500 duration-500 group-hover:text-blue-500 group-hover:underline"
href={link}
>
{title}
{t(title)}
</Link>
</CardTitle>
<Icon className="size-4 text-muted-foreground" />
@@ -94,12 +95,15 @@ export async function DashboardInfoCard({
<CountUp count={monthTotal} />
{total !== undefined && (
<p className="align-top text-base text-slate-500">
/ {nFormatter(limit)} <span className="text-xs">(monthly)</span>
/ {nFormatter(limit)}{" "}
<span className="text-xs">({t("monthly")})</span>
</p>
)}
</div>
)}
<p className="text-xs text-muted-foreground">total: {total}</p>
<p className="text-xs text-muted-foreground">
{t("total")}: {total}
</p>
</CardContent>
</Card>
);
@@ -114,6 +118,7 @@ export function HeroCard({
monthTotal: number;
limit: number;
}) {
const t = useTranslations("Components");
return (
<div className="grids group relative mb-4 h-full w-full shrink-0 origin-left overflow-hidden rounded-lg border bg-gray-50/70 px-5 pt-5 text-left duration-500 before:absolute before:right-1 before:top-1 before:z-[2] before:h-12 before:w-12 before:rounded-full before:bg-violet-500 before:blur-lg before:duration-500 after:absolute after:right-8 after:top-3 after:z-[2] after:h-20 after:w-20 after:rounded-full after:bg-rose-300 after:blur-lg after:duration-500 hover:border-cyan-600 hover:decoration-2 hover:duration-500 hover:before:-bottom-8 hover:before:right-12 hover:before:blur hover:before:[box-shadow:_20px_20px_20px_30px_#a21caf] hover:after:-right-8 group-hover:before:duration-500 group-hover:after:duration-500 dark:bg-primary-foreground md:max-w-[350px]">
<div className="flex flex-row items-center justify-between">
@@ -121,7 +126,7 @@ export function HeroCard({
href="/emails"
className="text-lg font-bold duration-500 group-hover:text-blue-500 group-hover:underline"
>
Email box
{t("Email box")}
</Link>
<Icons.mail className="size-4 text-muted-foreground" />
</div>
@@ -134,12 +139,15 @@ export function HeroCard({
<CountUp count={monthTotal} />
{total !== undefined && (
<p className="align-top text-base text-slate-500">
/ {nFormatter(limit)} <span className="text-xs">(monthly)</span>
/ {nFormatter(limit)}{" "}
<span className="text-xs">({t("monthly")})</span>
</p>
)}
</div>
)}
<p className="text-xs text-muted-foreground">total: {total}</p>
<p className="text-xs text-muted-foreground">
{t("total")}: {total}
</p>
</div>
</div>
);
@@ -157,6 +165,7 @@ export async function StaticInfoCard({
icon?: keyof typeof Icons;
}) {
const Icon = Icons[icon || "arrowRight"];
const t = useTranslations("Components");
return (
<Card className="grids group bg-gray-50/70 backdrop-blur-lg dark:bg-primary-foreground">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@@ -165,13 +174,13 @@ export async function StaticInfoCard({
className="font-semibold text-slate-500 duration-500 group-hover:text-blue-500 group-hover:underline"
href={link}
>
{title}
{t(title)}
</Link>
</CardTitle>
<Icon className="size-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<p className="text-xs text-muted-foreground">{desc}</p>
{desc && <p className="text-xs text-muted-foreground">{t(desc)}</p>}
</CardContent>
</Card>
);
+40 -32
View File
@@ -3,6 +3,7 @@
import { Dispatch, SetStateAction, useState, useTransition } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { User } from "@prisma/client";
import { useTranslations } from "next-intl";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import useSWR from "swr";
@@ -61,9 +62,11 @@ export function RecordForm({
const [currentZoneName, setCurrentZoneName] = useState(
initData?.zone_name || "wr.do",
);
const [email, setEmail] = useState(user.email);
const [email, setEmail] = useState(initData?.user.email || user.email);
const isAdmin = action.indexOf("admin") > -1;
const t = useTranslations("List");
const {
handleSubmit,
register,
@@ -204,24 +207,26 @@ export function RecordForm({
return (
<div>
<div className="rounded-t-lg bg-muted px-4 py-2 text-lg font-semibold">
{type === "add" ? "Create" : "Edit"} record
{type === "add" ? t("Create record") : t("Edit record")}
</div>
{siteConfig.enableSubdomainApply && (
<ul className="m-2 list-disc gap-1 rounded-md bg-yellow-600/10 p-2 px-5 pr-2 text-xs font-medium text-yellow-600 dark:bg-yellow-500/10 dark:text-yellow-500">
<li>The administrator has enabled application mode.</li>
<li>{t("The administrator has enabled application mode")}.</li>
<li>
After submission, you need to wait for administrator approval before
the record takes effect.
{t(
"After submission, you need to wait for administrator approval before the record takes effect",
)}
.
</li>
</ul>
)}
<form className="p-4" onSubmit={onSubmit}>
{isAdmin && (
<div className="items-center justify-start gap-4 md:flex">
<FormSectionColumns required title="User email">
<FormSectionColumns required title={t("User email")}>
<div className="flex w-full items-center gap-2">
<Label className="sr-only" htmlFor="content">
User email
{t("User email")}
</Label>
<Input
id="email"
@@ -229,6 +234,7 @@ export function RecordForm({
size={32}
defaultValue={email || ""}
onChange={(e) => setEmail(e.target.value)}
disabled={type === "edit"}
/>
</div>
<div className="flex flex-col justify-between p-1">
@@ -248,12 +254,12 @@ export function RecordForm({
{siteConfig.enableSubdomainApply && (
<FormSectionColumns
title="What are you planning to use the subdomain for?"
title={t("What are you planning to use the subdomain for?")}
required
>
<div className="flex items-center gap-2">
<Label className="sr-only" htmlFor="comment">
What are you planning to use the subdomain for?
{t("What are you planning to use the subdomain for?")}
</Label>
<Textarea
id="comment"
@@ -263,12 +269,12 @@ export function RecordForm({
/>
</div>
<p className="p-1 text-[13px] text-muted-foreground">
At least 20 characters
{t("At least 20 characters")}
</p>
</FormSectionColumns>
)}
<div className="items-center justify-start gap-4 md:flex">
<FormSectionColumns title="Domain" required>
<FormSectionColumns title={t("Domain")} required>
{isLoading ? (
<Skeleton className="h-9 w-full" />
) : (
@@ -293,17 +299,17 @@ export function RecordForm({
))
) : (
<Button className="w-full" variant="ghost">
No domains configured
{t("No domains configured")}
</Button>
)}
</SelectContent>
</Select>
)}
<p className="p-1 text-[13px] text-muted-foreground">
Required. Select a domain.
{t("Required")}. {t("Select a domain")}.
</p>
</FormSectionColumns>
<FormSectionColumns title="Type" required>
<FormSectionColumns title={t("Type")} required>
<Select
onValueChange={(value: RecordType) => {
setValue("type", value);
@@ -323,14 +329,16 @@ export function RecordForm({
))}
</SelectContent>
</Select>
<p className="p-1 text-[13px] text-muted-foreground">Required.</p>
<p className="p-1 text-[13px] text-muted-foreground">
{t("Required")}.
</p>
</FormSectionColumns>
</div>
<div className="items-center justify-start gap-4 md:flex">
<FormSectionColumns title="Name" required>
<FormSectionColumns title={t("Name")} required>
<div className="flex w-full items-center gap-2">
<Label className="sr-only" htmlFor="name">
Name (required)
{t("Name")}
</Label>
<div className="relative w-full">
<Input
@@ -354,7 +362,7 @@ export function RecordForm({
</p>
) : (
<p className="pb-0.5 text-[13px] text-muted-foreground">
Required. E.g. www.
{t("Required")}. {t("Example")} www
</p>
)}
</div>
@@ -363,15 +371,15 @@ export function RecordForm({
required
title={
currentRecordType === "CNAME"
? "Content"
? t("Content")
: currentRecordType === "A"
? "IPv4 address"
: "Content"
? t("IPv4 address")
: t("Content")
}
>
<div className="flex w-full items-center gap-2">
<Label className="sr-only" htmlFor="content">
Content
t("Content")
</Label>
<Input
id="content"
@@ -388,10 +396,10 @@ export function RecordForm({
) : (
<p className="pb-0.5 text-[13px] text-muted-foreground">
{currentRecordType === "CNAME"
? "Required. E.g. www.example.com"
? `${t("Required")}. ${t("Example")} www.example.com`
: currentRecordType === "A"
? "Required. E.g. 8.8.8.8"
: "Required."}
? `${t("Required")}. ${t("Example")} 8.8.8.8`
: t("Required")}
</p>
)}
</div>
@@ -418,14 +426,14 @@ export function RecordForm({
</SelectContent>
</Select>
<p className="p-1 text-[13px] text-muted-foreground">
Optional. Time To Live.
{t("Optional")}. {t("Time To Live")}.
</p>
</FormSectionColumns>
{["A", "CNAME"].includes(currentRecordType) && (
<FormSectionColumns title="Proxy">
<FormSectionColumns title={t("Proxy")}>
<div className="flex w-full items-center gap-2">
<Label className="sr-only" htmlFor="proxy">
Proxy
{t("Proxy")}
</Label>
<Switch
id="proxied"
@@ -434,7 +442,7 @@ export function RecordForm({
/>
</div>
<p className="p-1 text-[13px] text-muted-foreground">
Proxy status.
{t("Proxy status")}.
</p>
</FormSectionColumns>
)}
@@ -452,7 +460,7 @@ export function RecordForm({
{isDeleting ? (
<Icons.spinner className="size-4 animate-spin" />
) : (
<p>Delete</p>
<p>{t("Delete")}</p>
)}
</Button>
)}
@@ -462,7 +470,7 @@ export function RecordForm({
className="w-[80px] px-0"
onClick={() => setShowForm(false)}
>
Cancle
{t("Cancel")}
</Button>
<Button
type="submit"
@@ -473,7 +481,7 @@ export function RecordForm({
{isPending ? (
<Icons.spinner className="size-4 animate-spin" />
) : (
<p>{type === "edit" ? "Update" : "Save"}</p>
<p>{type === "edit" ? t("Update") : t("Save")}</p>
)}
</Button>
</div>
+4
View File
@@ -5,6 +5,10 @@ description: 选择你的部署方式
<DocsLang en="/docs/developer/deploy" zh="/docs/developer/deploy-zh" />
<Callout type="warning" twClass="mt-4">
在阅读此文档之前,建议首先阅读 [快速开始](/docs/developer/quick-start-zh),以确认准备好依赖的环境、变量。
</Callout>
## 使用 Vercel 部署(推荐)
<Callout type="warning" twClass="mt-4">
+5
View File
@@ -5,6 +5,11 @@ description: Choose your deployment method
<DocsLang en="/docs/developer/deploy" zh="/docs/developer/deploy-zh" />
<Callout type="warning" twClass="mt-4">
Before reading this document, it is recommended to first read [Quick Start](/docs/developer/quick-start),
to confirm that the necessary environment variables are ready.
</Callout>
## Deploy with Vercel (Recommended)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/oiov/wr.do.git&project-name=wrdo&env=DATABASE_URL&env=AUTH_SECRET&env=RESEND_API_KEY&env=NEXT_PUBLIC_EMAIL_R2_DOMAIN&env=NEXT_PUBLIC_OPEN_SIGNUP&env=GITHUB_TOKEN)
+76 -2
View File
@@ -49,7 +49,46 @@
"Learn more about": "Learn more about",
"Create Api Key": "Create Api Key",
"Show": "Show",
"per page": "per page"
"per page": "per page",
"Total Subdomains": "Total Subdomains",
"Subdomain List": "Subdomains",
"Please read the": "Please read the",
"Legitimacy review": "Legitimacy review",
"before using": "before using",
"See": "See",
"examples": "examples",
"for more usage": "for more usage",
"Add Record": "Add Record",
"Type": "Type",
"Name": "Name",
"Content": "Content",
"TTL": "TTL",
"Status": "Status",
"Pending": "Pending",
"The record is currently pending for admin approval": "The record is currently pending for admin approval",
"The target is currently inaccessible": "The target is currently inaccessible",
"Please check the target and try again": "Please check the target and try again",
"If the target is not activated within 3 days": "If the target is not activated within 3 days",
"the administrator will": "the administrator will",
"delete this record": "delete this record",
"Review": "Review",
"No Subdomains": "No Subdomains",
"No urls": "No urls",
"Create record": "Create record",
"Edit record": "Edit record",
"The administrator has enabled application mode": "The administrator has enabled application mode",
"After submission, you need to wait for administrator approval before the record takes effect": "After submission, you need to wait for administrator approval before the record takes effect",
"What are you planning to use the subdomain for?": "What are you planning to use the subdomain for?",
"At least 20 characters": "At least 20 characters",
"User email": "User email",
"Domain": "Domain",
"No domains configured": "No domains configured",
"Select a domain": "Select a domain",
"IPv4 address": "IPv4 address",
"Example": "E.g.",
"Time To Live": "Time To Live",
"Proxy": "Proxy",
"Proxy status": "DNS response is replaced by Cloudflare Anycast IP"
},
"Components": {
"Dashboard": "Dashboard",
@@ -72,7 +111,42 @@
"Realtime Visits": "Realtime Visits",
"See documentation": "See documentation",
"Manage Short URLs": "Manage Short URLs",
"List and manage short urls": "List and manage short urls"
"List and manage short urls": "List and manage short urls",
"Manage DNS Records": "Manage DNS Records",
"List and manage records": "List and manage records",
"Scraping API Overview": "Scraping API Overview",
"Quickly extract valuable structured website data": "Quickly extract valuable structured website data",
"Url to Screenshot": "Url to Screenshot",
"Quickly extract website screenshots": "Quickly extract website screenshots",
"extracting url as screenshot": "extracting url as screenshot",
"Url to QR Code": "Url to QR Code",
"Quickly extract website QR codes": "Quickly extract website QR codes",
"extracting url as qrcode": "extracting url as qrcode",
"Url to Meta Info": "Url to Meta Info",
"extracting url as meta info": "extracting url as meta info",
"Url to Markdown": "Url to Markdown",
"Quickly extract website content and convert it to Markdown format": "Quickly extract website content and convert it to Markdown format",
"extracting url as markdown": "extracting url as markdown",
"extracting url as text": "extracting url as text",
"Account Settings": "Account Settings",
"Manage account and website settings": "Manage account and website settings",
"Setup Guide": "Setup Guide",
"Admin Panel": "Admin Panel",
"Access only for users with ADMIN role": "Access only for users with ADMIN role",
"Domains Management": "Domains Management",
"List and manage domains": "List and manage domains",
"User Management": "User Management",
"List and manage all users": "List and manage all users",
"Email box": "Email box",
"monthly": "monthly",
"total": "total",
"Short URLs": "Short URLs",
"DNS Records": "DNS Records",
"Url to Text": "Url to Text",
"Take a screenshot of the webpage": "Take a screenshot of the webpage",
"Extract website metadata": "Extract website metadata",
"Convert website content to Markdown format": "Convert website content to Markdown format",
"Convert website content to text": "Convert website content to text"
},
"Landing": {
"settings": "Settings",
+60 -11
View File
@@ -49,7 +49,46 @@
"Learn more about": "了解更多关于",
"Create Api Key": "创建 API 密钥",
"Show": "显示",
"per page": "条/页"
"per page": "条/页",
"Total Subdomains": "总计",
"Subdomain List": "子域名列表",
"Please read the": "请阅读",
"legitimacy review": "链接合法性审查",
"before using": "在使用之前",
"See": "查看",
"examples": "示例",
"for more usage": "了解更多用法",
"Add Record": "添加记录",
"Type": "类型",
"Name": "名称",
"Content": "内容",
"TTL": "TTL",
"Status": "状态",
"Pending": "审核中",
"The record is currently pending for admin approval": "正在等待管理员审核",
"The target is currently inaccessible": "目标链接目前无法访问",
"Please check the target and try again": "请检查解析记录并重试",
"If the target is not activated within 3 days": "如果目标链接在 3 天内依然无法访问",
"the administrator will": "管理员将",
"delete this record": "删除此记录",
"Review": "审核",
"No Subdomains": "暂无子域名",
"No urls": "暂无短链接",
"Create record": "创建记录",
"Edit record": "编辑记录",
"The administrator has enabled application mode": "管理员已启用 [用户申请 - 管理员审核] 模式",
"After submission, you need to wait for administrator approval before the record takes effect": "提交后, 您需要等待管理员审核才能生效",
"What are you planning to use the subdomain for?": "您计划使用此域名做什么?",
"At least 20 characters": "至少 20 个字符",
"User email": "用户邮箱",
"Domain": "根域名",
"No domains configured": "未配置域名",
"Select a domain": "选择一个域名",
"IPv4 address": "IPv4 地址",
"Example": "例如",
"Time To Live": "生效时间",
"Proxy": "代理记录",
"Proxy status": "DNS 响应被 Cloudflare Anycast IP 替代"
},
"Components": {
"Dashboard": "用户面板",
@@ -97,16 +136,26 @@
"Domains Management": "域名管理",
"List and manage domains": "展示域名列表并管理你的域名服务",
"User Management": "用户管理",
"List and manage all users": "展示用户列表并管理所有用户"
"List and manage all users": "展示用户列表并管理所有用户",
"Email box": "邮件箱",
"monthly": "每月",
"total": "总计",
"Short URLs": "短链接",
"DNS Records": "DNS 记录",
"Url to Text": "网址转文本",
"Take a screenshot of the webpage": "使用 API 提取网页的截图",
"Extract website metadata": "使用 API 提取网页的元数据",
"Convert website content to Markdown format": "使用 API 将网页内容转换为 Markdown 格式",
"Convert website content to text": "使用 API 将网页内容转换为文本"
},
"Landing": {
"settings": "设置",
"Dashboard": "管理面板",
"Dashboard": "控制面板",
"deployWithVercel": "使用",
"now": "部署私有版本",
"onePlatformPowers": "一站式域名",
"endlessSolutions": "服务平台",
"platformDescription": " 短链生成、子域名托管、无限邮箱服务,开放API接口,一站式域名管理解决方案",
"onePlatformPowers": " ",
"endlessSolutions": "一站式域名服务平台",
"platformDescription": "集成短链生成、子域名托管、无限邮箱服务,以及开放API接口,一站式域名管理解决方案,释放你的域名潜力",
"documents": "参考文档",
"signInForFree": "免费登录",
"exampleImageAlt": "示例",
@@ -140,11 +189,11 @@
},
"System": {
"MENU": "菜单",
"Dashboard": "用户面板",
"Dashboard": "控制台",
"Short Urls": "短链接",
"Emails": "邮件",
"DNS Records": "DNS 记录",
"WRoom": "WRoom",
"Emails": "邮件",
"DNS Records": "子域名",
"WRoom": "聊天室",
"OPEN API": "开放API",
"Overview": "概览面板",
"Screenshot": "截图API",
@@ -156,7 +205,7 @@
"Domains": "域名管理",
"Users": "用户管理",
"URLs": "短链管理",
"Records": "DNS 记录管理",
"Records": "子域名管理",
"OPTIONS": "选项",
"Settings": "账户设置",
"Documentation": "使用文档",
+1 -1
View File
File diff suppressed because one or more lines are too long