diff --git a/apps/web-docs/package.json b/apps/web-docs/package.json index af19a80f..116c5688 100644 --- a/apps/web-docs/package.json +++ b/apps/web-docs/package.json @@ -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", diff --git a/apps/web-docs/src/app/[locale]/docs/[[...slug]]/page.tsx b/apps/web-docs/src/app/[locale]/docs/[[...slug]]/page.tsx index c76610c9..31e2abb7 100644 --- a/apps/web-docs/src/app/[locale]/docs/[[...slug]]/page.tsx +++ b/apps/web-docs/src/app/[locale]/docs/[[...slug]]/page.tsx @@ -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 { 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 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) { }} /> )} - + + ); } diff --git a/apps/web-docs/src/components/common/LanguageSwitcher.tsx b/apps/web-docs/src/components/common/LanguageSwitcher.tsx index 036ae403..c729861d 100644 --- a/apps/web-docs/src/components/common/LanguageSwitcher.tsx +++ b/apps/web-docs/src/components/common/LanguageSwitcher.tsx @@ -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' + }} > - {locale === 'en' ? 'VI' : 'EN'} + {locale === 'en' ? 'VI' : 'EN'} ); } diff --git a/apps/web-docs/src/components/docs/DocsContentClient.tsx b/apps/web-docs/src/components/docs/DocsContentClient.tsx index 9095869c..bdbc4605 100644 --- a/apps/web-docs/src/components/docs/DocsContentClient.tsx +++ b/apps/web-docs/src/components/docs/DocsContentClient.tsx @@ -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 = () => (

Loading documentation...

); -const docsRegistry = { - en: { - 'getting-started': dynamic(() => import('../../../../../docs/en/templates/README.md'), { - loading: () => , - ssr: true - }) - }, - vi: { - 'getting-started': dynamic(() => import('../../../../../docs/vi/templates/README.md'), { - loading: () => , - ssr: true - }) - } -} as const; +// EN: Error component for missing docs +// VI: Component lỗi cho docs thiếu +const DocNotFound = ({ slug }: { slug: string }) => ( +
+

Documentation Not Found

+

The requested documentation "{slug}" could not be found.

+

Please check the URL or navigate using the sidebar.

+
+); 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: () => }; + }), + { + loading: () => , + ssr: true + } + ); + return ; } diff --git a/apps/web-docs/src/components/docs/DocsSearch.tsx b/apps/web-docs/src/components/docs/DocsSearch.tsx index cfb6a8a1..b9788576 100644 --- a/apps/web-docs/src/components/docs/DocsSearch.tsx +++ b/apps/web-docs/src/components/docs/DocsSearch.tsx @@ -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 => ({ diff --git a/apps/web-docs/src/components/layout/DocsLayout.tsx b/apps/web-docs/src/components/layout/DocsLayout.tsx index 360e43f2..4c61649a 100644 --- a/apps/web-docs/src/components/layout/DocsLayout.tsx +++ b/apps/web-docs/src/components/layout/DocsLayout.tsx @@ -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 {item.label[locale]} @@ -126,10 +128,12 @@ export default function DocsLayout({ locale, currentSlug, children }: DocsLayout
{pageTitle}
)} - +
{children} diff --git a/apps/web-docs/src/components/layout/Navbar.tsx b/apps/web-docs/src/components/layout/Navbar.tsx index 9d185fca..7914cd2b 100644 --- a/apps/web-docs/src/components/layout/Navbar.tsx +++ b/apps/web-docs/src/components/layout/Navbar.tsx @@ -25,7 +25,7 @@ export default function Navbar() {
diff --git a/apps/web-docs/src/docs/navigation.ts b/apps/web-docs/src/docs/navigation.ts index 6d08ebf2..c3bd5475 100644 --- a/apps/web-docs/src/docs/navigation.ts +++ b/apps/web-docs/src/docs/navigation.ts @@ -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(); + diff --git a/apps/web-docs/src/docs/registry.ts b/apps/web-docs/src/docs/registry.ts index 1b8dbf93..9f7ee307 100644 --- a/apps/web-docs/src/docs/registry.ts +++ b/apps/web-docs/src/docs/registry.ts @@ -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 = { - 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 diff --git a/apps/web-docs/src/lib/docs-generator.ts b/apps/web-docs/src/lib/docs-generator.ts new file mode 100644 index 00000000..a1e626d4 --- /dev/null +++ b/apps/web-docs/src/lib/docs-generator.ts @@ -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 = { + '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(); + + 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(); + + 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); +} diff --git a/apps/web-docs/src/lib/docs-types.ts b/apps/web-docs/src/lib/docs-types.ts new file mode 100644 index 00000000..6af25b59 --- /dev/null +++ b/apps/web-docs/src/lib/docs-types.ts @@ -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'; diff --git a/apps/web-docs/src/messages/en.json b/apps/web-docs/src/messages/en.json index 2cf75b25..f6348c7d 100644 --- a/apps/web-docs/src/messages/en.json +++ b/apps/web-docs/src/messages/en.json @@ -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." } } }, diff --git a/apps/web-docs/src/messages/vi.json b/apps/web-docs/src/messages/vi.json index 1662567a..90accd4d 100644 --- a/apps/web-docs/src/messages/vi.json +++ b/apps/web-docs/src/messages/vi.json @@ -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." } } }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62bdf2e9..1629b871 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -219,6 +219,9 @@ importers: '@react-three/fiber': specifier: ^9.4.0 version: 9.5.0(@types/react@19.2.7)(react-dom@19.2.0)(react@19.2.0)(three@0.182.0) + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 lucide-react: specifier: ^0.555.0 version: 0.555.0(react@19.2.0) @@ -8581,7 +8584,6 @@ packages: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 - dev: true /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -10586,7 +10588,6 @@ packages: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - dev: true /esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} @@ -10748,6 +10749,13 @@ packages: /exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + /extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: false + /extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -11207,6 +11215,16 @@ packages: /grammex@3.1.12: resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} + /gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + dev: false + /hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} dev: false @@ -11630,6 +11648,11 @@ packages: hasBin: true dev: true + /is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: false + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -12307,7 +12330,6 @@ packages: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true /js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} @@ -12459,6 +12481,11 @@ packages: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} dev: false + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: false + /koa-compose@4.1.0: resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} dev: false @@ -15121,6 +15148,14 @@ packages: /scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + /section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + dev: false + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -15338,7 +15373,6 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true /sqlstring@2.3.3: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} @@ -15547,6 +15581,11 @@ packages: ansi-regex: 6.2.2 dev: true + /strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + dev: false + /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'}