Adding Vietnamese version of IPV6, Rewrite content of README.md

This commit is contained in:
2026-05-13 21:09:35 +07:00
parent 778f5f735d
commit b0d6bde50c
22 changed files with 1412 additions and 385 deletions
+8 -4
View File
@@ -1,15 +1,19 @@
"use client";
import Link from "next/link";
import siteData from "@/app/data/site.json";
import { useTranslation } from "@/app/hooks/useTranslation";
export default function Footer() {
const { site } = useTranslation();
return (
<footer className="w-full bg-surface-container-low border-t border-white/5 py-10 lg:py-16">
<div className="flex flex-col lg:flex-row justify-between items-center px-[var(--spacing-gutter)] gap-10 lg:gap-8 w-full max-w-hd mx-auto">
<div className="font-[var(--font-label-caps)] text-base lg:text-xl text-primary tracking-widest font-bold uppercase">
{siteData.brand.name}
{site.brand.name}
</div>
<div className="flex flex-wrap justify-center gap-8 lg:gap-16">
{siteData.footer.links.map((l) => (
{site.footer.links.map((l) => (
<Link
key={l.label}
className="text-on-surface-variant font-[var(--font-label-caps)] text-xs lg:text-sm hover:text-primary transition-colors"
@@ -20,7 +24,7 @@ export default function Footer() {
))}
</div>
<div className="text-[13px] lg:text-[14px] text-on-surface-variant/40 text-center lg:text-right max-w-sm leading-relaxed uppercase tracking-widest">
{siteData.footer.copyright}
{site.footer.copyright}
</div>
</div>
</footer>
+68 -11
View File
@@ -3,6 +3,8 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useLanguage } from "@/app/context/LanguageContext";
import { useTranslation } from "@/app/hooks/useTranslation";
type NavItem = { label: string; href: string };
@@ -15,9 +17,15 @@ export default function HeaderClient({
}) {
const pathname = usePathname();
const [mobileOpen, setMobileOpen] = useState(false);
const { lang, setLang } = useLanguage();
const { site } = useTranslation();
const isHome = pathname === "/";
const navItems = useMemo(() => nav.filter((x) => x.href !== "/"), [nav]);
const navItems = useMemo(() => site.nav.filter((x) => x.href !== "/"), [site.nav]);
const registerLabel = lang === "vi" ? "Đăng Ký" : "Register";
const registerNowLabel = lang === "vi" ? "Đăng Ký Ngay" : "Register Now";
const backLabel = lang === "vi" ? "Trang Chủ" : "Home";
useEffect(() => {
if (!mobileOpen) return;
@@ -45,7 +53,7 @@ export default function HeaderClient({
} transition-colors duration-300`}
href="/"
>
Home
{backLabel}
</Link>
{navItems.map((item) => {
const active = pathname === item.href;
@@ -63,15 +71,41 @@ export default function HeaderClient({
})}
</nav>
<div className="flex items-center gap-4 lg:gap-10">
<span className="hidden sm:block text-on-surface-variant/60 font-[var(--font-label-caps)] text-xs">
EN
</span>
<div className="flex items-center gap-4 lg:gap-6">
{/* Language Switcher — Desktop */}
<div className="hidden sm:flex items-center gap-1 font-[var(--font-label-caps)] text-xs">
<button
type="button"
onClick={() => setLang("en")}
className={`flex items-center gap-1 px-2 py-1 rounded transition-all duration-200 ${
lang === "en"
? "text-primary border-b-2 border-primary"
: "text-on-surface-variant/60 hover:text-primary"
}`}
title="English"
>
<span>EN</span>
</button>
<span className="text-on-surface-variant/30">|</span>
<button
type="button"
onClick={() => setLang("vi")}
className={`flex items-center gap-1 px-2 py-1 rounded transition-all duration-200 ${
lang === "vi"
? "text-primary border-b-2 border-primary"
: "text-on-surface-variant/60 hover:text-primary"
}`}
title="Tiếng Việt"
>
<span>VIE</span>
</button>
</div>
<Link
href="/registration"
className="hidden sm:inline-flex gold-gradient-bg text-on-primary px-4 lg:px-6 py-2 lg:py-2.5 font-[var(--font-body-base)] text-xs lg:text-sm font-bold uppercase tracking-wider rounded-lg active:scale-95 transition-transform shadow-lg shadow-primary/10"
>
Register
{registerLabel}
</Link>
<button
type="button"
@@ -120,7 +154,7 @@ export default function HeaderClient({
href="/"
onClick={() => setMobileOpen(false)}
>
Home
{backLabel}
</Link>
{navItems.map((item) => {
const active = pathname === item.href;
@@ -140,15 +174,38 @@ export default function HeaderClient({
</nav>
<div className="mt-auto pt-10 border-t border-white/5 flex flex-col gap-6">
<div className="text-on-surface-variant/60 font-[var(--font-label-caps)] text-sm uppercase tracking-widest">
Language: EN
{/* Language Switcher — Mobile */}
<div className="flex items-center gap-2 font-[var(--font-label-caps)] text-sm">
<button
type="button"
onClick={() => setLang("en")}
className={`flex items-center gap-1.5 px-3 py-2 rounded-lg border transition-all duration-200 ${
lang === "en"
? "border-primary text-primary bg-primary/10"
: "border-white/10 text-on-surface-variant/60 hover:text-primary hover:border-primary/40"
}`}
>
<span>EN</span>
</button>
<button
type="button"
onClick={() => setLang("vi")}
className={`flex items-center gap-1.5 px-3 py-2 rounded-lg border transition-all duration-200 ${
lang === "vi"
? "border-primary text-primary bg-primary/10"
: "border-white/10 text-on-surface-variant/60 hover:text-primary hover:border-primary/40"
}`}
>
<span>VIE</span>
</button>
</div>
<Link
href="/registration"
className="gold-gradient-bg text-on-primary w-full py-5 font-[var(--font-body-base)] text-lg font-bold uppercase tracking-widest rounded-xl text-center shadow-2xl"
onClick={() => setMobileOpen(false)}
>
Register Now
{registerNowLabel}
</Link>
</div>
</div>