feat(docs): Add gray-matter package and update documentation components

- Introduced `gray-matter` for frontmatter parsing in markdown files.
- Refactored documentation components to utilize new constants and improve slug handling.
- Enhanced language switcher functionality to maintain pathnames during locale changes.
- Updated navigation and search components to support dynamic content loading and improved user experience.
- Revised documentation text to reflect the new branding from Quaros to Goodgo.
This commit is contained in:
Ho Ngoc Hai
2026-01-08 15:09:17 +07:00
parent 866034d07a
commit 11169246ac
14 changed files with 402 additions and 202 deletions

View File

@@ -17,6 +17,7 @@
"@next/mdx": "^16.0.4",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.4.0",
"gray-matter": "^4.0.3",
"lucide-react": "^0.555.0",
"mermaid": "^11.12.2",
"next": "16.0.4",

View File

@@ -1,11 +1,12 @@
import DocsLayout from '@/components/layout/DocsLayout';
import DocsContentClient from '@/components/docs/DocsContentClient';
import { defaultDocSlug } from '@/docs/registry';
import { DEFAULT_DOC_SLUG } from '@/lib/docs-types';
import { docsNavigation } from '@/docs/navigation';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';
import type { DocsLocale } from '@/docs/navigation';
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://quaros.network';
type PageProps = {
@@ -30,7 +31,7 @@ function findDocBySlug(slug: string, locale: DocsLocale) {
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { locale, slug } = await params;
const slugPath = slug && slug.length > 0 ? slug.join('/') : defaultDocSlug;
const slugPath = slug && slug.length > 0 ? slug.join('/') : DEFAULT_DOC_SLUG;
const t = await getTranslations({ locale, namespace: 'Docs' });
const docInfo = findDocBySlug(slugPath, locale);
@@ -78,7 +79,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
export default async function DocsCatchAllPage({ params }: PageProps) {
const { locale, slug } = await params;
const slugPath = slug && slug.length > 0 ? slug.join('/') : defaultDocSlug;
const slugPath = slug && slug.length > 0 ? slug.join('/') : DEFAULT_DOC_SLUG;
const docInfo = findDocBySlug(slugPath, locale);
return (
@@ -112,9 +113,10 @@ export default async function DocsCatchAllPage({ params }: PageProps) {
}}
/>
)}
<DocsLayout locale={locale} currentSlug={slugPath}>
<DocsLayout locale={locale} currentSlug={slugPath} docsNavigation={docsNavigation}>
<DocsContentClient locale={locale} slug={slugPath} />
</DocsLayout>
</>
);
}

View File

@@ -1,7 +1,7 @@
'use client';
import { useLocale } from 'next-intl';
import { useRouter } from 'next/navigation';
import { useRouter, usePathname } from 'next/navigation';
import { useTransition } from 'react';
import { Globe } from 'lucide-react';
import styles from '../layout/Navbar.module.css';
@@ -9,12 +9,18 @@ import styles from '../layout/Navbar.module.css';
export default function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const [isPending, startTransition] = useTransition();
const toggleLanguage = () => {
const nextLocale = locale === 'en' ? 'vi' : 'en';
// EN: Remove current locale from pathname and add new locale
// VI: Xóa locale hiện tại khỏi pathname và thêm locale mới
const currentPath = pathname.replace(`/${locale}`, '') || '';
startTransition(() => {
router.replace(`/${nextLocale}`);
router.replace(`/${nextLocale}${currentPath}`);
});
};
@@ -23,10 +29,18 @@ export default function LanguageSwitcher() {
onClick={toggleLanguage}
className={styles.link}
disabled={isPending}
style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', background: 'none', border: 'none' }}
aria-label={`Switch to ${locale === 'en' ? 'Vietnamese' : 'English'}`}
style={{
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
background: 'none',
border: 'none',
cursor: 'pointer'
}}
>
<Globe size={16} />
{locale === 'en' ? 'VI' : 'EN'}
<span>{locale === 'en' ? 'VI' : 'EN'}</span>
</button>
);
}

View File

@@ -1,30 +1,27 @@
'use client';
import dynamic from 'next/dynamic';
import type { DocsLocale } from '@/docs/navigation';
import { defaultDocSlug } from '@/docs/registry';
import type { DocsLocale } from '@/lib/docs-types';
import { DEFAULT_DOC_SLUG } from '@/lib/docs-types';
// Loading component for MDX content
// EN: Loading component for MDX content
// VI: Component loading cho nội dung MDX
const LoadingDocs = () => (
<div style={{ padding: '2rem', textAlign: 'center' }}>
<p>Loading documentation...</p>
</div>
);
const docsRegistry = {
en: {
'getting-started': dynamic(() => import('../../../../../docs/en/templates/README.md'), {
loading: () => <LoadingDocs />,
ssr: true
})
},
vi: {
'getting-started': dynamic(() => import('../../../../../docs/vi/templates/README.md'), {
loading: () => <LoadingDocs />,
ssr: true
})
}
} as const;
// EN: Error component for missing docs
// VI: Component lỗi cho docs thiếu
const DocNotFound = ({ slug }: { slug: string }) => (
<div style={{ padding: '2rem', textAlign: 'center' }}>
<h2>Documentation Not Found</h2>
<p>The requested documentation "{slug}" could not be found.</p>
<p>Please check the URL or navigate using the sidebar.</p>
</div>
);
type DocsContentClientProps = {
locale: DocsLocale;
@@ -32,8 +29,27 @@ type DocsContentClientProps = {
};
export default function DocsContentClient({ locale, slug }: DocsContentClientProps) {
const localeRegistry = docsRegistry[locale] ?? docsRegistry.en;
const Component = localeRegistry[slug as keyof typeof localeRegistry] ?? localeRegistry[defaultDocSlug];
// EN: Use slug directly (already includes category path e.g., 'guides/getting-started')
// VI: Dùng slug trực tiếp (đã bao gồm đường dẫn category)
const slugPath = slug || DEFAULT_DOC_SLUG;
// EN: Dynamic import based on locale and slug with category path
// VI: Dynamic import dựa trên locale và slug với đường dẫn category
const Component = dynamic(
() => import(`../../../../../docs/${locale}/${slugPath}.md`)
.catch((error) => {
console.error(`Failed to load doc: ${locale}/${slugPath}`, error);
// EN: Return error component if doc doesn't exist
// VI: Trả về error component nếu doc không tồn tại
return { default: () => <DocNotFound slug={slugPath} /> };
}),
{
loading: () => <LoadingDocs />,
ssr: true
}
);
return <Component />;
}

View File

@@ -3,15 +3,16 @@
import { useState, useEffect, useRef, useMemo } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import { Search, X, FileText } from 'lucide-react';
import { docsNavigation } from '@/docs/navigation';
import type { DocsLocale } from '@/docs/navigation';
import type { DocsLocale, NavGroup } from '@/lib/docs-types';
import styles from './DocsSearch.module.css';
type DocsSearchProps = {
placeholder?: string;
locale?: DocsLocale;
docsNavigation: NavGroup[];
};
type SearchResult = {
id: string;
slug: string;
@@ -20,7 +21,7 @@ type SearchResult = {
href: string;
};
export default function DocsSearch({ placeholder, locale = 'en' }: DocsSearchProps) {
export default function DocsSearch({ placeholder, locale = 'en', docsNavigation }: DocsSearchProps) {
const [isOpen, setIsOpen] = useState(false);
const [query, setQuery] = useState('');
const [selectedIndex, setSelectedIndex] = useState(0);
@@ -31,6 +32,7 @@ export default function DocsSearch({ placeholder, locale = 'en' }: DocsSearchPro
// Extract locale from pathname
const currentLocale = (pathname?.split('/')[1] as DocsLocale) || locale;
// Build search index from navigation
const searchIndex: SearchResult[] = useMemo(() => docsNavigation.flatMap(group =>
group.items.map(item => ({

View File

@@ -3,7 +3,7 @@
import { useState, useEffect } from 'react';
import Navbar from '@/components/layout/Navbar';
import Footer from '@/components/layout/Footer';
import { docsNavigation, type DocsLocale } from '@/docs/navigation';
import type { DocsLocale, NavGroup } from '@/lib/docs-types';
import Link from 'next/link';
import { Menu, X } from 'lucide-react';
import styles from './DocsLayout.module.css';
@@ -14,10 +14,13 @@ type DocsLayoutProps = {
locale: DocsLocale;
currentSlug: string;
children: React.ReactNode;
docsNavigation: NavGroup[];
};
export default function DocsLayout({ locale, currentSlug, children }: DocsLayoutProps) {
export default function DocsLayout({ locale, currentSlug, children, docsNavigation }: DocsLayoutProps) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const currentGroup = docsNavigation.find(group =>
group.items.some(item => item.slug === currentSlug)
);
@@ -102,9 +105,8 @@ export default function DocsLayout({ locale, currentSlug, children }: DocsLayout
<Link
key={item.id}
href={href}
className={`${styles.sidebarLink} ${
isActive ? styles.sidebarLinkActive : ''
}`}
className={`${styles.sidebarLink} ${isActive ? styles.sidebarLinkActive : ''
}`}
onClick={closeSidebar}
>
{item.label[locale]}
@@ -126,10 +128,12 @@ export default function DocsLayout({ locale, currentSlug, children }: DocsLayout
<div className={styles.headerTitle}>{pageTitle}</div>
</header>
)}
<DocsSearch
placeholder={locale === 'en' ? 'Search documentation…' : 'Tìm trong tài liệu…'}
<DocsSearch
placeholder={locale === 'en' ? 'Search documentation…' : 'Tìm trong tài liệu…'}
locale={locale}
docsNavigation={docsNavigation}
/>
</div>
<div data-docs-content className={styles.contentBody}>
{children}

View File

@@ -25,7 +25,7 @@ export default function Navbar() {
<nav className={styles.navbar}>
<div className={styles.navbarContainer}>
<Link href={`/${locale}`} className={styles.logo} onClick={closeMenu}>QUAROS</Link>
<button
<button
className={styles.menuButton}
onClick={toggleMenu}
aria-label="Toggle menu"
@@ -49,7 +49,7 @@ export default function Navbar() {
</div>
<Link href="/" className={styles.link} onClick={closeMenu}>{tHome('learnMore')}</Link>
<Link href="/docs" className={styles.link} onClick={closeMenu}>{tDocs('title')}</Link>
{/* <LanguageSwitcher /> */}
<LanguageSwitcher />
</div>
</div>
</nav>

View File

@@ -1,138 +1,26 @@
export type DocsLocale = 'en' | 'vi';
export type DocsNavItem = {
id: string;
slug: string; // e.g. 'getting-started' or 'protocol/architecture'
label: {
en: string;
vi: string;
};
};
export type DocsNavGroup = {
id: string;
label: {
en: string;
vi: string;
};
items: DocsNavItem[];
};
export const docsNavigation: DocsNavGroup[] = [
{
id: 'getting-started',
label: {
en: 'Getting Started',
vi: 'Bắt đầu'
},
items: [
{
id: 'getting-started',
slug: 'getting-started',
label: {
en: 'Overview',
vi: 'Tổng quan'
}
},
{
id: 'why-post-quantum',
slug: 'why-post-quantum',
label: {
en: 'Why Post-Quantum Cryptography?',
vi: 'Tại sao cần Mật mã Hậu Lượng tử?'
}
},
{
id: 'ethereum-1',
slug: 'ethereum-1',
label: {
en: 'Ethereum 1',
vi: 'Ethereum 1'
}
},
{
id: 'ethereum-2',
slug: 'ethereum-2',
label: {
en: 'Ethereum 2',
vi: 'Ethereum 2'
}
},
{
id: 'qrl',
slug: 'qrl',
label: {
en: 'QRL',
vi: 'QRL'
}
},
{
id: 'go-zond',
slug: 'go-zond',
label: {
en: 'Go Zond',
vi: 'Go Zond'
}
},
{
id: 'xmss',
slug: 'xmss',
label: {
en: 'XMSS (Stateful)',
vi: 'XMSS (Có trạng thái)'
}
},
{
id: 'mldsa',
slug: 'mldsa',
label: {
en: 'ML-DSA (Stateless)',
vi: 'ML-DSA (Không trạng thái)'
}
}
]
},
{
id: 'protocol',
label: {
en: 'Protocol',
vi: 'Giao thức'
},
items: [
{
id: 'architecture',
slug: 'architecture',
label: {
en: 'Architecture',
vi: 'Kiến trúc'
}
},
{
id: 'execution-layer',
slug: 'execution-layer',
label: {
en: 'Execution Layer',
vi: 'Lớp Thực thi'
}
},
{
id: 'consensus-layer',
slug: 'consensus-layer',
label: {
en: 'Consensus Layer',
vi: 'Lớp Đồng thuận'
}
},
{
id: 'data-availability-layer',
slug: 'data-availability-layer',
label: {
en: 'Data Availability Layer',
vi: 'Lớp Khả dụng Dữ liệu'
}
}
]
}
];
// EN: Type definitions for documentation navigation
// VI: Định nghĩa types cho documentation navigation
export type { DocsLocale, NavItem as DocsNavItem, NavGroup as DocsNavGroup } from '@/lib/docs-types';
// EN: Dynamic navigation generation
// VI: Tạo navigation động
import { generateDocsNavigation } from '@/lib/docs-generator';
// EN: Cache navigation to avoid regenerating on every request
// VI: Cache navigation để tránh tạo lại với mỗi request
let cachedNavigation: DocsNavGroup[] | null = null;
export function getDocsNavigation(): DocsNavGroup[] {
if (!cachedNavigation) {
cachedNavigation = generateDocsNavigation();
}
return cachedNavigation;
}
// EN: Export cached navigation for backwards compatibility
// VI: Export cached navigation cho tương thích ngược
export const docsNavigation = getDocsNavigation();

View File

@@ -1,11 +1,13 @@
import type { DocsLocale } from './navigation';
import type { DocsLocale, DEFAULT_DOC_SLUG } from '@/lib/docs-types';
// Thông tin registry phía server (chỉ metadata, không import MDX ở Server Component)
// EN: Server-side registry - DO NOT import into client components
// VI: Registry phía server - KHÔNG import vào client components
// This file should only be imported by Server Components
export const defaultDocSlug = 'getting-started';
export const availableDocs: Record<DocsLocale, string[]> = {
en: ['getting-started', 'why-post-quantum', 'ethereum-1', 'ethereum-2', 'qrl', 'go-zond', 'architecture', 'execution-layer', 'consensus-layer', 'data-availability-layer', 'xmss', 'mldsa'],
vi: ['getting-started', 'why-post-quantum', 'ethereum-1', 'ethereum-2', 'qrl', 'go-zond', 'architecture', 'execution-layer', 'consensus-layer', 'data-availability-layer', 'xmss', 'mldsa']
};
// EN: Default doc slug to load when no slug is specified
// VI: Slug mặc định khi không có slug được chỉ định
export const defaultDocSlug = DEFAULT_DOC_SLUG;
// EN: Note: getAvailableDocs is a server-side function that uses fs
// VI: Lưu ý: getAvailableDocs là hàm server-side sử dụng fs
// It should only be called from Server Components, not Client Components

View File

@@ -0,0 +1,212 @@
// EN: Server-side utility to generate docs navigation from filesystem
// VI: Utility phía server để tạo docs navigation từ filesystem
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import type { DocsLocale, NavItem, NavGroup } from './docs-types';
interface DocFile {
slug: string;
title: string;
category: string;
filePath: string;
}
// EN: Get absolute path to docs directory
// VI: Lấy đường dẫn tuyệt đối tới thư mục docs
const DOCS_ROOT = path.join(process.cwd(), '../../docs');
// EN: Category display order and labels
// VI: Thứ tự hiển thị và nhãn cho các danh mục
const CATEGORY_CONFIG: Record<string, { order: number; label: { en: string; vi: string } }> = {
'guides': { order: 1, label: { en: 'Guides', vi: 'Hướng dẫn' } },
'architecture': { order: 2, label: { en: 'Architecture', vi: 'Kiến trúc' } },
'skills': { order: 3, label: { en: 'Skills', vi: 'Kỹ năng' } },
'templates': { order: 4, label: { en: 'Templates', vi: 'Mẫu' } },
'api': { order: 5, label: { en: 'API Reference', vi: 'Tài liệu API' } },
'runbooks': { order: 6, label: { en: 'Runbooks', vi: 'Sổ tay vận hành' } },
'onboarding': { order: 7, label: { en: 'Onboarding', vi: 'Hướng dẫn mới' } },
};
/**
* EN: Extract title from markdown file
* VI: Trích xuất tiêu đề từ file markdown
*/
function extractTitle(filePath: string): string {
try {
const content = fs.readFileSync(filePath, 'utf-8');
// EN: Try to parse frontmatter first
// VI: Thử parse frontmatter trước
try {
const { data } = matter(content);
if (data.name) return data.name;
if (data.title) return data.title;
} catch (e) {
// Ignore frontmatter parsing errors
}
// EN: Fallback to first H1 heading
// VI: Fallback về H1 heading đầu tiên
const h1Match = content.match(/^#\s+(.+)$/m);
if (h1Match) {
// EN: Remove bilingual suffix if exists (e.g., "Title / Tiêu đề" -> "Title")
// VI: Xóa hậu tố bilingual nếu tồn tại
const title = h1Match[1].split('/')[0].trim();
return title;
}
// EN: Final fallback to filename
// VI: Fallback cuối cùng là tên file
const filename = path.basename(filePath, '.md');
return filename
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
} catch (error) {
// EN: If file read fails, use filename
// VI: Nếu đọc file thất bại, dùng tên file
const filename = path.basename(filePath, '.md');
return filename
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
}
/**
* EN: Scan docs directory for a specific locale
* VI: Quét thư mục docs cho một locale cụ thể
*/
function scanDocsForLocale(locale: DocsLocale): DocFile[] {
const localeDir = path.join(DOCS_ROOT, locale);
const docs: DocFile[] = [];
if (!fs.existsSync(localeDir)) {
return docs;
}
// EN: Read all category directories
// VI: Đọc tất cả thư mục danh mục
const categories = fs.readdirSync(localeDir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
for (const category of categories) {
const categoryPath = path.join(localeDir, category);
const files = fs.readdirSync(categoryPath)
.filter(file => file.endsWith('.md') && file !== 'README.md');
for (const file of files) {
const filePath = path.join(categoryPath, file);
const slug = `${category}/${path.basename(file, '.md')}`;
const title = extractTitle(filePath);
docs.push({
slug,
title,
category,
filePath
});
}
}
return docs;
}
/**
* EN: Generate navigation structure with bilingual support
* VI: Tạo cấu trúc navigation với hỗ trợ đa ngôn ngữ
*/
export function generateDocsNavigation(): NavGroup[] {
const enDocs = scanDocsForLocale('en');
const viDocs = scanDocsForLocale('vi');
// EN: Create a map of slug to titles in both languages
// VI: Tạo map từ slug tới tiêu đề ở cả hai ngôn ngữ
const docTitles = new Map<string, { en: string; vi: string }>();
enDocs.forEach(doc => {
docTitles.set(doc.slug, {
en: doc.title,
vi: doc.title // Default to EN if VI not found
});
});
viDocs.forEach(doc => {
const existing = docTitles.get(doc.slug);
if (existing) {
existing.vi = doc.title;
} else {
docTitles.set(doc.slug, { en: doc.title, vi: doc.title });
}
});
// EN: Group by category
// VI: Nhóm theo danh mục
const groups = new Map<string, NavItem[]>();
docTitles.forEach((labels, slug) => {
const category = slug.split('/')[0];
if (!groups.has(category)) {
groups.set(category, []);
}
groups.get(category)!.push({
id: slug.replace(/\//g, '-'),
slug,
label: labels
});
});
// EN: Convert to NavGroup array with sorting
// VI: Chuyển thành mảng NavGroup với sắp xếp
const navGroups: NavGroup[] = [];
groups.forEach((items, category) => {
const config = CATEGORY_CONFIG[category] || {
order: 999,
label: { en: category.charAt(0).toUpperCase() + category.slice(1), vi: category.charAt(0).toUpperCase() + category.slice(1) }
};
// EN: Sort items alphabetically
// VI: Sắp xếp items theo thứ tự bảng chữ cái
items.sort((a, b) => a.label.en.localeCompare(b.label.en));
navGroups.push({
id: category,
label: config.label,
items
});
});
// EN: Sort groups by order
// VI: Sắp xếp groups theo thứ tự
navGroups.sort((a, b) => {
const orderA = CATEGORY_CONFIG[a.id]?.order || 999;
const orderB = CATEGORY_CONFIG[b.id]?.order || 999;
return orderA - orderB;
});
return navGroups;
}
/**
* EN: Get list of available doc slugs for a locale
* VI: Lấy danh sách slugs có sẵn cho một locale
*/
export function getAvailableDocs(locale: DocsLocale): string[] {
const docs = scanDocsForLocale(locale);
return docs.map(doc => doc.slug);
}
/**
* EN: Check if a doc exists for a locale
* VI: Kiểm tra xem một doc có tồn tại cho locale không
*/
export function docExists(locale: DocsLocale, slug: string): boolean {
const slugPath = slug.replace(/\//g, path.sep);
const filePath = path.join(DOCS_ROOT, locale, `${slugPath}.md`);
return fs.existsSync(filePath);
}

View File

@@ -0,0 +1,20 @@
// EN: Shared types and constants for docs (safe for client components)
// VI: Types và constants chung cho docs (an toàn cho client components)
export type DocsLocale = 'en' | 'vi';
export interface NavItem {
id: string;
slug: string;
label: { en: string; vi: string };
}
export interface NavGroup {
id: string;
label: { en: string; vi: string };
items: NavItem[];
}
// EN: Default doc slug to load when no slug is specified
// VI: Slug mặc định khi không có slug được chỉ định
export const DEFAULT_DOC_SLUG = 'guides/getting-started';

View File

@@ -13,16 +13,16 @@
"apiDesc": "Build dApps with post-quantum security. Seamlessly integrate with existing Ethereum tooling and infrastructure.",
"apiCta": "Start building",
"docsTitle": "Documentation",
"docsDesc": "Learn how to integrate Quaros into your applications. Explore guides covering architecture, security, and common use cases.",
"docsDesc": "Learn how to integrate Goodgo into your applications. Explore guides covering architecture, security, and common use cases.",
"docsCta": "Read docs",
"largeText": "Secure",
"largeText2": "The Future",
"ctaTitle": "Build on Quaros",
"ctaTitle": "Build on Goodgo",
"ctaDesc": "Join the post-quantum blockchain revolution. Start building secure, future-proof applications today.",
"ctaButton": "Get started"
},
"Features": {
"title": "Why Quaros?",
"title": "Why Goodgo?",
"xmssTitle": "XMSS (Stateful)",
"xmssDesc": "Offers robust security but limits the number of signatures per wallet.",
"mldsaTitle": "ML-DSA (Stateless)",
@@ -32,11 +32,11 @@
},
"Docs": {
"title": "Goodgo Documentation",
"intro": "This page gives you a high-level overview of how Quaros works and how to start integrating it.",
"intro": "This page gives you a high-level overview of how Goodgo works and how to start integrating it.",
"sections": {
"arch": {
"title": "Architecture Overview",
"desc1": "Quaros is designed as a post-quantum secure, EVM-compatible network that focuses on long-term security.",
"desc1": "Goodgo is designed as a post-quantum secure, EVM-compatible network that focuses on long-term security.",
"desc2": "The protocol separates execution, consensus and data availability layers to enable scalability and flexibility."
},
"security": {
@@ -46,8 +46,8 @@
},
"evm": {
"title": "EVM & Web3 Tooling",
"desc1": "Quaros keeps compatibility with the Ethereum JSON-RPC and tooling ecosystem so existing dApps can be ported easily.",
"desc2": "Most common frameworks (Hardhat, Foundry, wagmi, etc.) can connect to Quaros with minimal configuration changes."
"desc1": "Goodgo keeps compatibility with the Ethereum JSON-RPC and tooling ecosystem so existing dApps can be ported easily.",
"desc2": "Most common frameworks (Hardhat, Foundry, wagmi, etc.) can connect to Goodgo with minimal configuration changes."
}
}
},

View File

@@ -1,6 +1,6 @@
{
"HomePage": {
"title": "Mạng Lưới Quaros",
"title": "Mạng Lưới Goodgo",
"subtitle": "Bảo mật Hậu Lượng Tử. Tương thích EVM. Sẵn sàng cho Web3.",
"getStarted": "Bắt đầu ngay",
"learnMore": "Tìm hiểu thêm",
@@ -13,16 +13,16 @@
"apiDesc": "Xây dựng dApp với bảo mật hậu lượng tử. Tích hợp liền mạch với các công cụ và cơ sở hạ tầng Ethereum hiện có.",
"apiCta": "Bắt đầu xây dựng",
"docsTitle": "Tài liệu",
"docsDesc": "Tìm hiểu cách tích hợp Quaros vào ứng dụng của bạn. Khám phá các hướng dẫn về kiến trúc, bảo mật và các trường hợp sử dụng phổ biến.",
"docsDesc": "Tìm hiểu cách tích hợp Goodgo vào ứng dụng của bạn. Khám phá các hướng dẫn về kiến trúc, bảo mật và các trường hợp sử dụng phổ biến.",
"docsCta": "Đọc tài liệu",
"largeText": "Bảo vệ",
"largeText2": "Tương lai",
"ctaTitle": "Xây dựng trên Quaros",
"ctaTitle": "Xây dựng trên Goodgo",
"ctaDesc": "Tham gia cuộc cách mạng blockchain hậu lượng tử. Bắt đầu xây dựng các ứng dụng an toàn, sẵn sàng cho tương lai ngay hôm nay.",
"ctaButton": "Bắt đầu"
},
"Features": {
"title": "Tại sao chọn Quaros?",
"title": "Tại sao chọn Goodgo?",
"xmssTitle": "XMSS (Có trạng thái)",
"xmssDesc": "Cung cấp bảo mật mạnh mẽ nhưng giới hạn số lượng chữ ký mỗi ví.",
"mldsaTitle": "ML-DSA (Không trạng thái)",
@@ -32,11 +32,11 @@
},
"Docs": {
"title": "Tài liệu Goodgo Documentation",
"intro": "Trang này cung cấp cái nhìn tổng quan ở mức cao về cách Quaros hoạt động và cách bắt đầu tích hợp.",
"intro": "Trang này cung cấp cái nhìn tổng quan ở mức cao về cách Goodgo hoạt động và cách bắt đầu tích hợp.",
"sections": {
"arch": {
"title": "Tổng quan kiến trúc",
"desc1": "Quaros được thiết kế như một mạng lưới bảo mật hậu lượng tử, tương thích EVM và tập trung vào bảo mật dài hạn.",
"desc1": "Goodgo được thiết kế như một mạng lưới bảo mật hậu lượng tử, tương thích EVM và tập trung vào bảo mật dài hạn.",
"desc2": "Giao thức tách biệt các lớp thực thi, đồng thuận và khả dụng dữ liệu để đạt được khả năng mở rộng và linh hoạt."
},
"security": {
@@ -46,8 +46,8 @@
},
"evm": {
"title": "EVM & Hệ sinh thái Web3",
"desc1": "Quaros giữ tương thích với JSON-RPC và hệ sinh thái công cụ Ethereum nên dApp hiện tại có thể được port dễ dàng.",
"desc2": "Hầu hết framework phổ biến (Hardhat, Foundry, wagmi, v.v.) có thể kết nối với Quaros chỉ với vài thay đổi cấu hình."
"desc1": "Goodgo giữ tương thích với JSON-RPC và hệ sinh thái công cụ Ethereum nên dApp hiện tại có thể được port dễ dàng.",
"desc2": "Hầu hết framework phổ biến (Hardhat, Foundry, wagmi, v.v.) có thể kết nối với Goodgo chỉ với vài thay đổi cấu hình."
}
}
},