增加 wordcount 工具

This commit is contained in:
selfboot 2026-05-05 11:18:31 +08:00
parent 7709ab46e0
commit 8f6c0da7a1
11 changed files with 478 additions and 0 deletions

View File

@ -92,6 +92,10 @@ export const TOOL_METADATA_DATES = {
publishedDate: "2026-05-05T08:00:00.000Z",
updatedDate: "2026-05-05T08:00:00.000Z",
},
wordcount: {
publishedDate: "2026-05-05T09:00:00.000Z",
updatedDate: "2026-05-05T09:00:00.000Z",
},
yamljson: {
publishedDate: "2026-05-05T07:00:00.000Z",
updatedDate: "2026-05-05T07:00:00.000Z",

View File

@ -0,0 +1,13 @@
"use client";
import dynamic from "next/dynamic";
import ToolContentLoading from "@/app/components/ToolContentLoading";
const WordCountContent = dynamic(() => import("./content"), {
ssr: false,
loading: () => <ToolContentLoading />,
});
export default function ClientContent() {
return <WordCountContent />;
}

View File

@ -0,0 +1,24 @@
import { analyzeText, countChineseCharacters, countEnglishWords, countParagraphs, getLimitStatus } from "../logic";
describe("wordcount logic", () => {
test("counts Chinese characters and English words separately", () => {
const text = "你好 world, this is GPT-5.\n第二段 text.";
expect(countChineseCharacters(text)).toBe(5);
expect(countEnglishWords(text)).toBe(5);
});
test("analyzes text structure", () => {
const stats = analyzeText("第一句。Second sentence!\n\nNew paragraph.");
expect(stats.paragraphs).toBe(2);
expect(stats.sentences).toBe(3);
expect(stats.charsNoSpaces).toBeLessThan(stats.chars);
expect(stats.readingMinutes).toBe(1);
});
test("counts paragraphs and social limits", () => {
expect(countParagraphs("a\n\nb\n\n\nc")).toBe(3);
const status = getLimitStatus(analyzeText("hello"), { metric: "chars", limit: 3 });
expect(status.exceeded).toBe(true);
expect(status.remaining).toBe(-2);
});
});

View File

@ -0,0 +1,157 @@
"use client";
import { useMemo, useRef, useState } from "react";
import { saveAs } from "file-saver";
import { useI18n } from "@/app/i18n/client";
import { SOCIAL_LIMITS, analyzeText, getLimitStatus, makeStatsReport } from "./logic";
const EXAMPLES = {
zh: "这是一段用于字数统计的中文文案。它可以统计中文字符、英文 words、标点、段落和句子。\n\n如果你正在写小红书标题、公众号摘要、SEO 标题或产品介绍,可以把文本粘贴到这里,实时查看长度是否超出限制。",
en: "This is a sample paragraph for word counting. It counts English words, characters, sentences, paragraphs, reading time, and common social media limits.\n\nPaste a blog intro, SEO title, product copy, or social post here to check whether the text fits your target channel.",
};
function StatCard({ label, value, hint }) {
return (
<div className="rounded border border-gray-200 bg-white p-4">
<p className="text-sm text-gray-500">{label}</p>
<p className="mt-1 text-2xl font-bold text-gray-950">{value}</p>
{hint && <p className="mt-1 text-xs text-gray-500">{hint}</p>}
</div>
);
}
export default function WordCountContent() {
const { t, lang } = useI18n();
const fileInputRef = useRef(null);
const [text, setText] = useState("");
const [copyStatus, setCopyStatus] = useState("");
const stats = useMemo(() => analyzeText(text), [text]);
const hasText = text.trim().length > 0;
const loadExample = () => {
setText(EXAMPLES[lang] || EXAMPLES.en);
setCopyStatus("");
};
const clearText = () => {
setText("");
setCopyStatus("");
if (fileInputRef.current) fileInputRef.current.value = "";
};
const copyReport = async () => {
await navigator.clipboard.writeText(makeStatsReport(stats));
setCopyStatus(t("wordcount_copied"));
};
const downloadReport = () => {
saveAs(new Blob([makeStatsReport(stats)], { type: "application/json;charset=utf-8" }), "word-count-report.json");
};
const uploadTextFile = async (event) => {
const file = event.target.files?.[0];
if (!file) return;
setText(await file.text());
setCopyStatus("");
};
return (
<div className="mx-auto mt-4 w-full space-y-6">
<section className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
<div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
<div>
<h2 className="text-xl font-semibold text-gray-950">{t("wordcount_workspace_title")}</h2>
<p className="mt-1 max-w-3xl text-sm leading-6 text-gray-600">{t("wordcount_workspace_hint")}</p>
</div>
<div className="flex flex-wrap gap-2">
<button onClick={() => fileInputRef.current?.click()} className="rounded bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200">
{t("wordcount_upload")}
</button>
<button onClick={loadExample} className="rounded bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200">
{t("wordcount_example")}
</button>
<button onClick={clearText} className="rounded bg-red-50 px-4 py-2 text-sm font-medium text-red-700 hover:bg-red-100">
{t("wordcount_clear")}
</button>
</div>
<input ref={fileInputRef} type="file" accept=".txt,.md,.csv,.json,.yaml,.yml,text/*,application/json" onChange={uploadTextFile} className="hidden" />
</div>
<div className="mt-5 grid grid-cols-1 gap-5 xl:grid-cols-[minmax(0,1fr)_420px]">
<label className="block">
<span className="mb-2 block text-sm font-semibold text-gray-700">{t("wordcount_input_label")}</span>
<textarea
value={text}
onChange={(event) => {
setText(event.target.value);
setCopyStatus("");
}}
placeholder={t("wordcount_placeholder")}
className="min-h-[520px] w-full resize-y rounded border border-gray-300 bg-white px-3 py-2 text-sm leading-6 text-gray-950 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
/>
</label>
<aside className="space-y-4">
<div className="grid grid-cols-2 gap-3">
<StatCard label={t("wordcount_mixed_words")} value={stats.mixedWordCount} hint={t("wordcount_mixed_words_hint")} />
<StatCard label={t("wordcount_chars")} value={stats.chars} />
<StatCard label={t("wordcount_chars_no_spaces")} value={stats.charsNoSpaces} />
<StatCard label={t("wordcount_chinese_chars")} value={stats.chineseCharacters} />
<StatCard label={t("wordcount_english_words")} value={stats.englishWords} />
<StatCard label={t("wordcount_reading_time")} value={stats.readingMinutes ? t("wordcount_minutes", { count: stats.readingMinutes }) : "0"} />
</div>
<section className="rounded border border-gray-200 bg-gray-50 p-4">
<h3 className="text-sm font-semibold text-gray-900">{t("wordcount_structure_title")}</h3>
<div className="mt-3 grid grid-cols-2 gap-3 text-sm">
<div className="flex justify-between gap-3"><span>{t("wordcount_paragraphs")}</span><strong>{stats.paragraphs}</strong></div>
<div className="flex justify-between gap-3"><span>{t("wordcount_sentences")}</span><strong>{stats.sentences}</strong></div>
<div className="flex justify-between gap-3"><span>{t("wordcount_lines")}</span><strong>{stats.lines}</strong></div>
<div className="flex justify-between gap-3"><span>{t("wordcount_numbers")}</span><strong>{stats.numbers}</strong></div>
<div className="flex justify-between gap-3"><span>{t("wordcount_punctuation")}</span><strong>{stats.punctuation}</strong></div>
<div className="flex justify-between gap-3"><span>{t("wordcount_seconds")}</span><strong>{stats.readingSeconds}</strong></div>
</div>
</section>
<section className="rounded border border-gray-200 bg-white p-4">
<h3 className="text-sm font-semibold text-gray-900">{t("wordcount_limits_title")}</h3>
<div className="mt-3 space-y-3">
{SOCIAL_LIMITS.map((limit) => {
const status = getLimitStatus(stats, limit);
return (
<div key={limit.id}>
<div className="mb-1 flex justify-between gap-3 text-xs text-gray-600">
<span>{t(limit.labelKey)}</span>
<span className={status.exceeded ? "font-semibold text-red-700" : "text-gray-500"}>
{status.value} / {limit.limit}
</span>
</div>
<div className="h-2 overflow-hidden rounded bg-gray-100">
<div className={`h-full ${status.exceeded ? "bg-red-500" : "bg-blue-600"}`} style={{ width: `${status.percent}%` }} />
</div>
<p className={`mt-1 text-xs ${status.exceeded ? "text-red-700" : "text-gray-500"}`}>
{status.exceeded ? t("wordcount_exceeded", { count: Math.abs(status.remaining) }) : t("wordcount_remaining", { count: status.remaining })}
</p>
</div>
);
})}
</div>
</section>
<section className="rounded border border-gray-200 bg-gray-50 p-4">
<h3 className="text-sm font-semibold text-gray-900">{t("wordcount_export_title")}</h3>
<div className="mt-3 grid grid-cols-2 gap-2">
<button onClick={copyReport} disabled={!hasText} className="rounded bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-gray-300">
{copyStatus || t("wordcount_copy_report")}
</button>
<button onClick={downloadReport} disabled={!hasText} className="rounded bg-green-600 px-3 py-2 text-sm font-medium text-white hover:bg-green-700 disabled:cursor-not-allowed disabled:bg-gray-300">
{t("wordcount_download_report")}
</button>
</div>
</section>
</aside>
</div>
</section>
</div>
);
}

View File

@ -0,0 +1,29 @@
# How to Use the Online Word Counter
This online word counter is built for Chinese writing, English writing, SEO content, social media posts, product copy, blog articles, abstracts, and marketing drafts. Paste text into the editor to instantly count Chinese characters, English words, total characters, characters without spaces, paragraphs, sentences, punctuation, numbers, lines, and estimated reading time.
All counting runs locally in your browser. Your text is not uploaded to a server, which is useful for unpublished articles, contract drafts, product copy, internal notes, and campaign content.
## What It Counts
- Chinese characters: useful for Chinese articles, titles, summaries, and social posts.
- English words: useful for English articles, emails, essays, and documentation.
- Characters and characters without spaces: useful for SEO titles, meta descriptions, and platform limits.
- Paragraphs, sentences, and lines: useful for readability and structure checks.
- Reading time: estimates how long the text takes to read based on Chinese and English content.
- Social media limits: checks X / Twitter, Threads, Xiaohongshu titles, WeChat titles, SEO titles, and SEO descriptions.
## Steps
1. Paste text, or upload a `.txt`, `.md`, `.json`, or other text file.
2. Review the live statistics on the right.
3. Use the platform limit bars to see whether the text is too long.
4. Copy the report or download a JSON report for records.
## Common Use Cases
Use this tool to check whether a Xiaohongshu title is too long, whether a WeChat title fits, whether an SEO title or meta description is search-friendly, how many words an English article has, how many Chinese characters a draft contains, and whether the estimated reading time is appropriate. It is useful for frontend developers, marketers, editors, SEO writers, and content creators.
## Counting Rules
Chinese counting focuses on Han characters. English counting focuses on English words. The mixed word count combines Chinese characters and English words to estimate mixed-language copy length. Platform limits can change over time, so the social media limit checks are practical writing references rather than official publishing rules.

View File

@ -0,0 +1,99 @@
export const SOCIAL_LIMITS = [
{ id: "x", labelKey: "wordcount_limit_x", limit: 280, metric: "chars" },
{ id: "threads", labelKey: "wordcount_limit_threads", limit: 500, metric: "chars" },
{ id: "metaTitle", labelKey: "wordcount_limit_meta_title", limit: 60, metric: "chars" },
{ id: "metaDescription", labelKey: "wordcount_limit_meta_description", limit: 160, metric: "chars" },
{ id: "xiaohongshuTitle", labelKey: "wordcount_limit_xhs_title", limit: 20, metric: "charsNoSpaces" },
{ id: "wechatTitle", labelKey: "wordcount_limit_wechat_title", limit: 64, metric: "charsNoSpaces" },
];
export function normalizeText(text) {
return String(text || "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
}
export function countChineseCharacters(text) {
return normalizeText(text).match(/\p{Script=Han}/gu)?.length || 0;
}
export function countEnglishWords(text) {
return normalizeText(text).match(/[A-Za-z]+(?:['-][A-Za-z]+)*/g)?.length || 0;
}
export function countNumbers(text) {
return normalizeText(text).match(/\b\d+(?:[.,]\d+)*\b/g)?.length || 0;
}
export function countParagraphs(text) {
const normalized = normalizeText(text).trim();
if (!normalized) return 0;
return normalized.split(/\n\s*\n/g).filter((paragraph) => paragraph.trim()).length;
}
export function countSentences(text) {
const normalized = normalizeText(text).trim();
if (!normalized) return 0;
const matches = normalized.match(/[^。!?!?。.]+[。!?!?。.]?/g) || [];
return matches.filter((sentence) => sentence.trim()).length;
}
export function countLines(text) {
const normalized = normalizeText(text);
if (!normalized) return 0;
return normalized.split("\n").length;
}
export function estimateReadingTime(text) {
const chineseCharacters = countChineseCharacters(text);
const englishWords = countEnglishWords(text);
const minutes = chineseCharacters / 300 + englishWords / 200;
if (!normalizeText(text).trim()) return { minutes: 0, seconds: 0 };
return {
minutes: Math.max(1, Math.ceil(minutes)),
seconds: Math.max(15, Math.round(minutes * 60)),
};
}
export function analyzeText(text) {
const normalized = normalizeText(text);
const chars = normalized.length;
const charsNoSpaces = normalized.replace(/\s/g, "").length;
const chineseCharacters = countChineseCharacters(normalized);
const englishWords = countEnglishWords(normalized);
const numbers = countNumbers(normalized);
const punctuation = normalized.match(/[\p{P}\p{S}]/gu)?.length || 0;
const paragraphs = countParagraphs(normalized);
const sentences = countSentences(normalized);
const lines = countLines(normalized);
const readingTime = estimateReadingTime(normalized);
return {
chars,
charsNoSpaces,
chineseCharacters,
englishWords,
numbers,
punctuation,
paragraphs,
sentences,
lines,
mixedWordCount: chineseCharacters + englishWords,
readingMinutes: readingTime.minutes,
readingSeconds: readingTime.seconds,
};
}
export function getLimitStatus(stats, limit) {
const value = stats[limit.metric] || 0;
const ratio = limit.limit > 0 ? value / limit.limit : 0;
return {
value,
remaining: limit.limit - value,
ratio,
percent: Math.min(100, Math.round(ratio * 100)),
exceeded: value > limit.limit,
};
}
export function makeStatsReport(stats) {
return JSON.stringify(stats, null, 2);
}

View File

@ -0,0 +1,27 @@
import ClientContent from "./ClientContent";
import PageHeader from "@/app/components/PageHeader";
import CommonComments from "@/app/components/GiscusComments";
import BlogMarkdown from "@/app/components/BlogMarkdown";
import { createToolMetadata, ToolStructuredData } from "../toolMetadata";
export async function generateMetadata(props) {
const params = await props.params;
const { lang } = params;
return createToolMetadata("wordcount", lang);
}
export default async function WordCountPage(props) {
const params = await props.params;
const { lang } = params;
return (
<div className="container mx-auto mt-4">
<PageHeader lang={lang} pathname={`/${lang}/tools/wordcount`} />
<ClientContent />
<BlogMarkdown lang={lang} directory="src/app/[lang]/tools/wordcount" />
<ToolStructuredData toolId="wordcount" lang={lang} />
<CommonComments lang={lang} />
</div>
);
}

View File

@ -0,0 +1,29 @@
# 在线字数统计工具怎么用
这个在线字数统计工具适合中文写作、英文写作、SEO 内容、社媒文案、产品介绍、论文摘要、博客文章和运营内容检查。你可以把文本粘贴进输入框,实时查看中文字符数、英文单词数、字符数、去空格字符数、段落数、句子数、标点数、数字数量和阅读时间。
统计过程全部在浏览器本地完成,不会上传文本内容。对于未发布文章、合同草稿、产品文案、账号运营内容和内部资料,本地统计更安全。
## 支持哪些统计
- 中文字符统计:统计汉字数量,适合中文稿件、标题和摘要。
- 英文单词统计:统计英文 words适合英文文章、邮件和论文。
- 总字符数和去空格字符数:适合 SEO 标题、Meta Description、社媒标题限制。
- 段落、句子、行数:适合检查文章结构和可读性。
- 阅读时间:按中文和英文阅读速度估算读完需要多久。
- 社媒长度限制:显示 X / Twitter、Threads、小红书标题、公众号标题、SEO 标题和 SEO 描述长度是否超限。
## 使用步骤
1. 粘贴文本,或上传 `.txt`、`.md`、`.json` 等文本文件。
2. 查看右侧实时统计结果。
3. 根据社媒长度限制进度条判断是否需要删减。
4. 复制统计报告,或下载 JSON 报告用于归档。
## 常见使用场景
你可以用它检查小红书标题是否太长、公众号标题是否超出建议长度、SEO 标题和描述是否适合搜索结果展示、英文文章大概有多少 words、中文文章有多少汉字、博客阅读时间是否合适。对于前端、运营、编辑、SEO 和内容创作者,这类字数统计工具可以减少来回复制到多个平台检查长度的麻烦。
## 统计口径说明
中文统计以汉字为主,英文统计以英文单词为主。混合字数会把中文字符和英文单词合并,用于估算中英文混排内容长度。不同平台的长度规则可能会调整,工具里的社媒限制用于写作时快速参考,最终发布前仍建议以平台实际提示为准。

View File

@ -655,6 +655,18 @@ const Projects = {
link: "/tools/difftext/",
tags: ["text", "developer"],
},
{
id: "wordcount",
title: "wordcount_title",
description: "wordcount_description",
image: "https://slefboot-1251736664.file.myqcloud.com/20260505_ai_gallery_wordcount_en.svg",
images: {
en: "https://slefboot-1251736664.file.myqcloud.com/20260505_ai_gallery_wordcount_en.svg",
zh: "https://slefboot-1251736664.file.myqcloud.com/20260505_ai_gallery_wordcount_zh.svg",
},
link: "/tools/wordcount/",
tags: ["text", "data", "developer", "seo"],
},
{
id: "yamljson",
title: "yamljson_title",

View File

@ -594,6 +594,8 @@
"pdfwhiteout_description": "Upload a PDF, select any area, erase or cover it with a white block, move and resize the region, and export a new PDF locally in your browser.",
"difftext_title": "Free Online Text Diff Tool",
"difftext_description": "Compare two pieces of text online with line-by-line and word-level differences. Useful for code, copywriting, contracts, config files, and Markdown documents with local browser processing.",
"wordcount_title": "Free Online Word Counter",
"wordcount_description": "Count Chinese characters, English words, characters, paragraphs, sentences, reading time, and social media length limits for SEO, marketing copy, articles, and social posts.",
"yamljson_title": "Free Online YAML JSON Converter",
"yamljson_description": "Convert YAML to JSON or JSON to YAML online with auto detection, formatting, compact JSON, copy, and download for developer configuration files.",
"difftext_input_title": "Input Text",
@ -622,6 +624,41 @@
"difftext_download_report": "Download TXT",
"difftext_empty_result": "Enter two text blocks or load the example to see the diff.",
"difftext_copied": "Copied",
"wordcount_workspace_title": "Word Count Workspace",
"wordcount_workspace_hint": "Paste articles, titles, summaries, social copy, or upload a text file to count Chinese characters, English words, paragraphs, sentences, reading time, and platform limits.",
"wordcount_upload": "Upload Text",
"wordcount_example": "Load Example",
"wordcount_clear": "Clear",
"wordcount_input_label": "Input Text",
"wordcount_placeholder": "Paste Chinese articles, English copy, SEO titles, meta descriptions, Xiaohongshu titles, WeChat summaries, or any text here...",
"wordcount_mixed_words": "Mixed count",
"wordcount_mixed_words_hint": "Chinese characters + English words",
"wordcount_chars": "Characters",
"wordcount_chars_no_spaces": "No-space chars",
"wordcount_chinese_chars": "Chinese chars",
"wordcount_english_words": "English words",
"wordcount_reading_time": "Reading time",
"wordcount_minutes": "{{count}} min",
"wordcount_structure_title": "Text Structure",
"wordcount_paragraphs": "Paragraphs",
"wordcount_sentences": "Sentences",
"wordcount_lines": "Lines",
"wordcount_numbers": "Numbers",
"wordcount_punctuation": "Punctuation",
"wordcount_seconds": "Read seconds",
"wordcount_limits_title": "Social and SEO Length Limits",
"wordcount_limit_x": "X / Twitter post",
"wordcount_limit_threads": "Threads post",
"wordcount_limit_meta_title": "SEO title guide",
"wordcount_limit_meta_description": "SEO description guide",
"wordcount_limit_xhs_title": "Xiaohongshu title guide",
"wordcount_limit_wechat_title": "WeChat title guide",
"wordcount_remaining": "{{count}} remaining",
"wordcount_exceeded": "{{count}} over",
"wordcount_export_title": "Export Stats",
"wordcount_copy_report": "Copy Report",
"wordcount_download_report": "Download JSON",
"wordcount_copied": "Copied",
"yamljson_workspace_title": "YAML / JSON Conversion Workspace",
"yamljson_workspace_hint": "Paste YAML or JSON configuration, or upload a .yaml, .yml, or .json file. Conversion and formatting run locally in your browser.",
"yamljson_input_label": "Input",
@ -3051,6 +3088,11 @@
"description": "Compare two pieces of text online with line diff, word diff, split view, unified view, copied diff reports, and TXT export. Useful for code review, copywriting edits, contracts, config files, and Markdown documents with local browser processing.",
"keywords": "text diff, online text compare, compare text online, code diff, copywriting diff, contract comparison, line diff, word diff, diff checker, text comparison tool"
},
"wordcount": {
"title": "Free Online Word Counter for Chinese Characters, English Words, and Social Limits",
"description": "Count Chinese characters, English words, total characters, no-space characters, paragraphs, sentences, reading time, and social media length limits online. Useful for SEO titles, meta descriptions, Xiaohongshu titles, WeChat titles, English articles, Chinese copy, and marketing content with local browser processing.",
"keywords": "word counter, online word count, character counter, Chinese character counter, English word counter, paragraph counter, sentence counter, reading time estimator, SEO title length, meta description length, social media character limit, Xiaohongshu title length"
},
"yamljson": {
"title": "Free Online YAML JSON Converter for Developer Config Files",
"description": "Convert YAML to JSON and JSON to YAML online with auto detection, formatting, compact JSON, YAML indentation, key sorting, copy, and download. Useful for Kubernetes, Docker Compose, GitHub Actions, CI/CD, package.json, API payloads, and developer configuration files. Processing runs locally in your browser.",

View File

@ -595,6 +595,8 @@
"pdfwhiteout_description": "上传 PDF 后直接框选区域,用白色块擦除或遮挡指定内容,支持拖动、缩放、多页处理后导出新的 PDF浏览器本地处理不上传服务器。",
"difftext_title": "在线免费文本对比工具",
"difftext_description": "在线对比两段文本差异,支持逐行对比和逐词对比,适合代码、文案、合同段落、配置文件和 Markdown 文档,本地浏览器处理不上传服务器。",
"wordcount_title": "在线免费字数统计工具",
"wordcount_description": "在线统计中文字符、英文单词、字符数、段落、句子、阅读时间和社媒长度限制,适合 SEO、运营文案、文章和社媒内容检查。",
"yamljson_title": "在线免费 YAML JSON 互转工具",
"yamljson_description": "在线把 YAML 转 JSON或把 JSON 转 YAML支持自动识别、格式化、压缩、复制和下载适合开发者处理配置文件。",
"difftext_input_title": "输入文本",
@ -623,6 +625,41 @@
"difftext_download_report": "下载 TXT",
"difftext_empty_result": "请输入两段文本,或加载示例查看对比效果。",
"difftext_copied": "已复制",
"wordcount_workspace_title": "字数统计工作区",
"wordcount_workspace_hint": "粘贴文章、标题、摘要、社媒文案或上传文本文件,实时统计中文字符、英文单词、段落、句子、阅读时间和平台长度限制。",
"wordcount_upload": "上传文本",
"wordcount_example": "加载示例",
"wordcount_clear": "清空",
"wordcount_input_label": "输入文本",
"wordcount_placeholder": "在这里粘贴中文文章、英文文案、SEO 标题、Meta Description、小红书标题、公众号摘要或任意文本...",
"wordcount_mixed_words": "混合字数",
"wordcount_mixed_words_hint": "中文字符 + 英文单词",
"wordcount_chars": "字符数",
"wordcount_chars_no_spaces": "去空格字符",
"wordcount_chinese_chars": "中文字符",
"wordcount_english_words": "英文单词",
"wordcount_reading_time": "阅读时间",
"wordcount_minutes": "{{count}} 分钟",
"wordcount_structure_title": "文本结构",
"wordcount_paragraphs": "段落",
"wordcount_sentences": "句子",
"wordcount_lines": "行数",
"wordcount_numbers": "数字",
"wordcount_punctuation": "标点",
"wordcount_seconds": "阅读秒数",
"wordcount_limits_title": "社媒和 SEO 长度限制",
"wordcount_limit_x": "X / Twitter 帖子",
"wordcount_limit_threads": "Threads 帖子",
"wordcount_limit_meta_title": "SEO 标题建议",
"wordcount_limit_meta_description": "SEO 描述建议",
"wordcount_limit_xhs_title": "小红书标题建议",
"wordcount_limit_wechat_title": "公众号标题建议",
"wordcount_remaining": "还可输入 {{count}}",
"wordcount_exceeded": "已超出 {{count}}",
"wordcount_export_title": "导出统计",
"wordcount_copy_report": "复制报告",
"wordcount_download_report": "下载 JSON",
"wordcount_copied": "已复制",
"yamljson_workspace_title": "YAML / JSON 转换工作区",
"yamljson_workspace_hint": "粘贴 YAML 或 JSON 配置,也可以上传 .yaml、.yml、.json 文件。转换和格式化都在浏览器本地完成。",
"yamljson_input_label": "输入内容",
@ -3054,6 +3091,11 @@
"description": "在线对比两段文本差异,支持逐行对比、逐词对比、左右视图、统一视图、复制差异报告和 TXT 下载。适合代码对比、文案改稿、合同段落、配置文件和 Markdown 文档,本地浏览器处理不上传服务器。",
"keywords": "文本对比, 在线文本对比, 文本差异对比, 代码对比, 文案对比, 合同对比, 逐行对比, 逐词对比, diff工具, text diff"
},
"wordcount": {
"title": "在线免费字数统计工具,中文字符英文单词和社媒长度检查",
"description": "在线统计中文字符数、英文单词数、总字符数、去空格字符数、段落数、句子数、阅读时间和社媒长度限制。适合 SEO 标题、Meta Description、小红书标题、公众号标题、英文文章、中文文案和运营内容检查浏览器本地处理不上传服务器。",
"keywords": "字数统计, 在线字数统计, 字符统计, 中文字符统计, 英文单词统计, 单词统计, 段落统计, 句子统计, 阅读时间估算, SEO标题长度, Meta Description长度, 小红书标题字数, 社媒字数限制"
},
"yamljson": {
"title": "在线免费 YAML JSON 互转工具,配置文件格式转换",
"description": "在线把 YAML 转 JSON或把 JSON 转 YAML支持自动识别、格式化、压缩 JSON、YAML 缩进、按 key 排序、复制和下载。适合 Kubernetes、Docker Compose、GitHub Actions、CI/CD、package.json、接口数据和开发配置文件处理浏览器本地运行不上传服务器。",