diff --git a/src/app/page.tsx b/src/app/page.tsx
index f132c2f..26f00a4 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,4 +1,5 @@
import type { Metadata } from "next";
+import Link from "next/link";
import PublicPreviewWrapper from "@/uhm/components/preview/PublicPreviewWrapper";
export const metadata: Metadata = {
@@ -27,10 +28,10 @@ export default function Page() {
{/* Header (SSR & SEO) */}
@@ -43,9 +44,9 @@ export default function Page() {
Tính năng chính bao gồm:
- - Xem bản đồ lịch sử theo dòng thời gian (Timeline).
- - Trình phát diễn biến lịch sử và chiến trận (Replay).
- - Tra cứu thông tin sự kiện lịch sử (Wiki & Entities).
+ - Xem bản đồ lịch sử theo dòng thời gian.
+ - Trình phát diễn biến lịch sử và chiến trận.
+ - Tra cứu thông tin sự kiện lịch sử.
@@ -55,7 +56,7 @@ export default function Page() {
{/* Footer (SSR & SEO) */}
- © {new Date().getFullYear()} Ultimate History Map. All rights reserved.
+ © {new Date().getFullYear()} Ultimate History Map. Đã đăng ký bản quyền.
);
diff --git a/src/uhm/components/Map.tsx b/src/uhm/components/Map.tsx
index c168846..77e728a 100644
--- a/src/uhm/components/Map.tsx
+++ b/src/uhm/components/Map.tsx
@@ -333,7 +333,7 @@ const Map = memo(forwardRef(function Map({
}}
>
- Map khong khoi tao duoc
+ Không khởi tạo được bản đồ
{fatalInitError}
@@ -478,8 +478,8 @@ const Map = memo(forwardRef
(function Map({
(function Map({
type="checkbox"
checked={isGlobeProjection}
onChange={(e) => setIsGlobeProjection(e.target.checked)}
- aria-label="Toggle globe projection"
+ aria-label="Chuyển chế độ hiển thị hình cầu"
style={{ display: "none" }}
/>
@@ -511,7 +511,7 @@ const Map = memo(forwardRef(function Map({
}}
className="hidden sm:block"
>
- {isGlobeProjection ? "Globe" : "Flat"}
+ {isGlobeProjection ? "Cầu" : "Phẳng"}
@@ -532,7 +532,7 @@ const Map = memo(forwardRef(function Map({
transition: "background 150ms, color 150ms",
}}
>
- LOCAL
+ CỤC BỘ
(function Map({
transition: "background 150ms, color 150ms",
}}
>
- GLOBAL
+ TOÀN CỤC
) : null}
@@ -568,10 +568,10 @@ const Map = memo(forwardRef(function Map({
color: isPreviewMode ? "#ffffff" : "#34d399",
flexShrink: 0,
}}
- aria-label={isPreviewMode ? "Exit preview" : "Enter preview"}
- title={isPreviewMode ? "Thoat preview" : "Xem nhu nguoi dung"}
+ aria-label={isPreviewMode ? "Thoát xem trước" : "Vào chế độ xem trước"}
+ title={isPreviewMode ? "Thoát xem trước" : "Xem như người dùng"}
>
- {isPreviewMode ? "Editor" : "Preview"}
+ {isPreviewMode ? "Trình sửa" : "Xem trước"}
) : null}
@@ -592,8 +592,8 @@ const Map = memo(forwardRef(function Map({
fontSize: "13px",
flexShrink: 0,
}}
- aria-label="Play selected replay"
- title="Play replay của geometry đang chọn"
+ aria-label="Phát diễn biến đã chọn"
+ title="Phát diễn biến của hình đang chọn"
>
(function Map({
borderLeft: "8px solid currentColor",
}}
/>
- Play
+ Phát
) : null}
@@ -615,7 +615,7 @@ const Map = memo(forwardRef(function Map({
onClick={() => handleZoomByStep(-0.8)}
className="premium-zoom-btn"
style={{ flexShrink: 0 }}
- aria-label="Zoom out"
+ aria-label="Thu nhỏ bản đồ"
>
-
@@ -648,7 +648,7 @@ const Map = memo(forwardRef(function Map({
onPointerCancel={endZoomSliderDrag}
onBlur={endZoomSliderDrag}
onChange={(event) => handleZoomSliderChange(Number(event.target.value))}
- aria-label="Map zoom"
+ aria-label="Mức thu phóng bản đồ"
/>
(function Map({
onClick={() => handleZoomByStep(0.8)}
className="premium-zoom-btn"
style={{ flexShrink: 0 }}
- aria-label="Zoom in"
+ aria-label="Phóng to bản đồ"
>
+
diff --git a/src/uhm/components/editor/PresentPlaceSearch.tsx b/src/uhm/components/editor/PresentPlaceSearch.tsx
index f3091d3..1de645c 100644
--- a/src/uhm/components/editor/PresentPlaceSearch.tsx
+++ b/src/uhm/components/editor/PresentPlaceSearch.tsx
@@ -117,7 +117,7 @@ export default function PresentPlaceSearch({
.catch((err) => {
if (controller.signal.aborted || requestSeqRef.current !== seq) return;
setResults([]);
- setError(err instanceof Error ? err.message : "Không search được địa điểm.");
+ setError(err instanceof Error ? err.message : "Không tìm được địa điểm.");
})
.finally(() => {
if (requestSeqRef.current === seq) {
@@ -159,7 +159,7 @@ export default function PresentPlaceSearch({
.catch((err) => {
if (historicalRequestSeqRef.current !== seq) return;
setHistoricalResults([]);
- setHistoricalError(err instanceof Error ? err.message : "Không search được entity lịch sử.");
+ setHistoricalError(err instanceof Error ? err.message : "Không tìm được thực thể lịch sử.");
})
.finally(() => {
if (historicalRequestSeqRef.current === seq) {
@@ -196,7 +196,7 @@ export default function PresentPlaceSearch({
.catch((err) => {
if (wikiRequestSeqRef.current !== seq) return;
setWikiResults([]);
- setWikiError(err instanceof Error ? err.message : "Không search được wiki.");
+ setWikiError(err instanceof Error ? err.message : "Không tìm được bài viết wiki.");
})
.finally(() => {
if (wikiRequestSeqRef.current === seq) {
@@ -364,11 +364,11 @@ export default function PresentPlaceSearch({
- {mode === "present" ? "Present" : mode === "history" ? "History" : "Wiki"}
+ {getSearchModeLabel(mode)}
x
@@ -535,9 +535,9 @@ function HistoricalResults({
onSelectEntity: (item: EntityGeometriesSearchItem) => void;
onSelectGeometry: (item: EntityGeometriesSearchItem, geometry: EntityGeometrySearchGeo) => void;
}) {
- if (isLoading) return Đang tìm entity...
;
+ if (isLoading) return Đang tìm thực thể...
;
if (error) return {error}
;
- if (!results.length && query.trim().length >= 2) return Không có entity phù hợp.
;
+ if (!results.length && query.trim().length >= 2) return Không có thực thể phù hợp.
;
return (
<>
@@ -563,8 +563,8 @@ function HistoricalResults({
{item.name || item.entity_id}
{item.geometries.length
- ? `${item.geometries.length} geometry${item.geometries.length > 1 ? "s" : ""}`
- : "Không có geometry"}
+ ? `${item.geometries.length} hình bản đồ`
+ : "Không có hình bản đồ"}
{item.description ? ` · ${item.description}` : ""}
@@ -672,7 +672,7 @@ function formatAdminLabel(state: AdminLabelState | undefined): string {
}
function formatGeometryMeta(geometry: EntityGeometrySearchGeo): string {
- const type = geometry.type || "geometry";
+ const type = geometry.type || "hình bản đồ";
const timeStart = geometry.time_start ?? null;
const timeEnd = geometry.time_end ?? null;
const time =
@@ -780,3 +780,9 @@ const statusStyle = {
fontSize: 12,
fontWeight: 700,
} satisfies CSSProperties;
+
+function getSearchModeLabel(mode: SearchMode): string {
+ if (mode === "present") return "Hiện tại";
+ if (mode === "history") return "Lịch sử";
+ return "Wiki";
+}
diff --git a/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx b/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx
index 05f27ca..235b205 100644
--- a/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx
+++ b/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx
@@ -2,7 +2,6 @@
import type { BackgroundLayerId } from "@/uhm/lib/map/styles/backgroundLayers";
import { BACKGROUND_LAYER_OPTIONS } from "@/uhm/lib/map/styles/backgroundLayers";
-import { GEO_TYPE_KEYS } from "@/uhm/lib/map/geo/geoTypeMap";
type Props = {
backgroundVisibility: Record;
@@ -165,9 +164,6 @@ const LAYER_ICONS: Record = {
),
};
-// Class name helper for tooltips using CSS
-const buttonClassName = "preview-layer-btn";
-
export default function ReplayPreviewLayerPanel({
backgroundVisibility,
geometryVisibility,
@@ -245,7 +241,7 @@ export default function ReplayPreviewLayerPanel({
}}
>
{/* Background layers */}
- Map
+ Bản đồ
{BACKGROUND_LAYER_OPTIONS.map((layer) => {
const active = Boolean(backgroundVisibility[layer.id]);
@@ -266,11 +262,11 @@ export default function ReplayPreviewLayerPanel({
{/* Territories / Polygons */}
-
Areas
+
Khu vực
{polygonKeys.map((typeKey) => {
const active = geometryVisibility[typeKey] !== false;
- const label = typeKey.replace("_", " ").toUpperCase();
+ const label = getGeometryTypeLabel(typeKey);
return (
{/* Routes / Lines */}
-
Routes
+
Tuyến
{lineKeys.map((typeKey) => {
const active = geometryVisibility[typeKey] !== false;
- const label = typeKey.replace("_", " ").toUpperCase();
+ const label = getGeometryTypeLabel(typeKey);
return (
{/* Places & Events / Points */}
-
Points
+
Điểm
{pointKeys.map((typeKey) => {
const active = geometryVisibility[typeKey] !== false;
- const label = typeKey.replace("_", " ").toUpperCase();
+ const label = getGeometryTypeLabel(typeKey);
return (
= {
+ country: "Quốc gia",
+ state: "Nhà nước / vùng",
+ faction: "Phe phái",
+ rebellion_zone: "Vùng nổi dậy",
+ defense_line: "Tuyến phòng thủ",
+ military_route: "Đường hành quân",
+ retreat_route: "Đường rút lui",
+ migration_route: "Đường di cư",
+ trade_route: "Tuyến thương mại",
+ battle: "Trận đánh",
+ person_event: "Nhân vật / sự kiện",
+ temple: "Đền miếu",
+ capital: "Kinh đô",
+ city: "Thành phố",
+ fortification: "Công sự",
+ ruin: "Di tích",
+ port: "Cảng",
+ };
+ return labels[typeKey] || typeKey.replaceAll("_", " ");
+}
diff --git a/src/uhm/components/editor/ReplayPreviewOverlay.tsx b/src/uhm/components/editor/ReplayPreviewOverlay.tsx
index ea70a89..c8c308d 100644
--- a/src/uhm/components/editor/ReplayPreviewOverlay.tsx
+++ b/src/uhm/components/editor/ReplayPreviewOverlay.tsx
@@ -121,7 +121,7 @@ export default function ReplayPreviewOverlay({
{dialog.image_url?.trim() ? (
- Preview
+ Xem trước
{activeStepLabel ? (
- Step {activeStepNumber || 0}/{totalSteps}
+ Bước {activeStepNumber || 0}/{totalSteps}
) : null}
@@ -289,7 +289,7 @@ export default function ReplayPreviewOverlay({
onClick={onExitPreview}
style={previewButtonStyle("#334155")}
>
- Thoát preview
+ Thoát xem trước
diff --git a/src/uhm/components/preview/GeometrySelectionPanel.tsx b/src/uhm/components/preview/GeometrySelectionPanel.tsx
index 88a3c57..43f067f 100644
--- a/src/uhm/components/preview/GeometrySelectionPanel.tsx
+++ b/src/uhm/components/preview/GeometrySelectionPanel.tsx
@@ -83,7 +83,7 @@ export default function GeometrySelectionPanel({
color: "#94a3b8",
}}
>
- Geometry
+ Hình bản đồ
- Chọn entity để zoom
+ Chọn thực thể để phóng tới
{wikiSlug ? (
x
@@ -234,7 +234,7 @@ export default function GeometrySelectionPanel({
) : (
- Wiki này chưa có entity hoặc geometry liên quan.
+ Wiki này chưa có thực thể hoặc hình bản đồ liên quan.
)}
diff --git a/src/uhm/components/preview/MapPlaceholder.tsx b/src/uhm/components/preview/MapPlaceholder.tsx
index 05705a9..f09e405 100644
--- a/src/uhm/components/preview/MapPlaceholder.tsx
+++ b/src/uhm/components/preview/MapPlaceholder.tsx
@@ -28,7 +28,7 @@ export default function MapPlaceholder({ onEnter }: MapPlaceholderProps) {
{/* eslint-disable-next-line @next/next/no-img-element */}
- hiện dự án chỉ đang hỗ trợ người dùng máy tính, các phiên bản di động hiên không ổn định
+ Hiện dự án chỉ hỗ trợ tốt trên máy tính; phiên bản di động chưa ổn định.
diff --git a/src/uhm/components/preview/PreviewLayout.tsx b/src/uhm/components/preview/PreviewLayout.tsx
index f69d5d1..1702257 100644
--- a/src/uhm/components/preview/PreviewLayout.tsx
+++ b/src/uhm/components/preview/PreviewLayout.tsx
@@ -186,7 +186,7 @@ const PreviewLayout = forwardRef(({
) {
return null;
}
- return `Stage #${replayPreviewActiveCursor.stageId} · Step ${replayPreviewActiveCursor.stepIndex + 1}`;
+ return `Cảnh #${replayPreviewActiveCursor.stageId} · Bước ${replayPreviewActiveCursor.stepIndex + 1}`;
}, [replayPreviewActiveCursor.stageId, replayPreviewActiveCursor.stepIndex]);
// Active wiki snapshot
diff --git a/src/uhm/components/preview/PreviewMapShell.tsx b/src/uhm/components/preview/PreviewMapShell.tsx
index a052275..22ad7a3 100644
--- a/src/uhm/components/preview/PreviewMapShell.tsx
+++ b/src/uhm/components/preview/PreviewMapShell.tsx
@@ -300,12 +300,12 @@ export default function PreviewMapShell({
type="button"
onClick={() => {
if (isMobileOrTablet) {
- alert("Tính năng quản trị và chỉnh sửa chỉ hỗ trợ trên máy tính (Desktop)");
+ alert("Tính năng quản trị và chỉnh sửa chỉ hỗ trợ trên máy tính.");
} else {
window.location.href = "/user";
}
}}
- title={isMobileOrTablet ? "Tính năng này chỉ hoạt động trên Desktop" : "Quản trị & Chỉnh sửa (Edit)"}
+ title={isMobileOrTablet ? "Tính năng này chỉ hoạt động trên máy tính" : "Quản trị và chỉnh sửa"}
style={{
...menuOptionStyle,
opacity: isMobileOrTablet ? 0.5 : 1,
@@ -349,7 +349,7 @@ export default function PreviewMapShell({
{ window.location.href = "/faq"; }}
- title="Hỏi đáp & Hướng dẫn (FAQ)"
+ title="Hỏi đáp và hướng dẫn"
style={menuOptionStyle}
>
@@ -360,7 +360,7 @@ export default function PreviewMapShell({
{ window.location.href = "/about-us"; }}
- title="Về chúng tôi (About Us)"
+ title="Về chúng tôi"
style={menuOptionStyle}
>
diff --git a/src/uhm/components/preview/PublicPreviewClientPage.tsx b/src/uhm/components/preview/PublicPreviewClientPage.tsx
index 26d0b94..1448922 100644
--- a/src/uhm/components/preview/PublicPreviewClientPage.tsx
+++ b/src/uhm/components/preview/PublicPreviewClientPage.tsx
@@ -349,7 +349,7 @@ export default function PublicPreviewClientPage({
wikiSlug: nextSlug,
rows: [],
isLoading: false,
- error: "Wiki này chưa có entity liên quan.",
+ error: "Wiki này chưa có thực thể liên quan.",
});
return;
}
@@ -727,7 +727,7 @@ export default function PublicPreviewClientPage({
) {
return null;
}
- return `Stage #${replayPreview.activeCursor.stageId} · Step ${replayPreview.activeCursor.stepIndex + 1}`;
+ return `Cảnh #${replayPreview.activeCursor.stageId} · Bước ${replayPreview.activeCursor.stepIndex + 1}`;
}, [replayPreview.activeCursor.stageId, replayPreview.activeCursor.stepIndex]);
const isWikiChooserOpen = rightPanelMode === "selection" && Boolean(wikiSelectionPanelAnchor);
@@ -1292,14 +1292,14 @@ function PublicMapZoomPanel({
type="button"
onClick={toggleProjection}
className="uhm-public-projection-toggle"
- aria-label="Toggle globe projection"
- title={isGlobeProjection ? "Dang o che do hinh cau (globe)" : "Dang o che do trai phang (flat)"}
+ aria-label="Chuyển chế độ hiển thị hình cầu"
+ title={isGlobeProjection ? "Đang ở chế độ hình cầu" : "Đang ở chế độ bản đồ phẳng"}
>
- {isGlobeProjection ? "Globe" : "Flat"}
+ {isGlobeProjection ? "Cầu" : "Phẳng"}
{onPlayPreviewReplay ? (
@@ -1307,11 +1307,11 @@ function PublicMapZoomPanel({
type="button"
onClick={onPlayPreviewReplay}
className="uhm-public-play-btn"
- aria-label="Play selected replay"
- title="Play replay cua geometry dang chon"
+ aria-label="Phát diễn biến đã chọn"
+ title="Phát diễn biến của hình đang chọn"
>
- Play
+ Phát
) : null}
{onResumePreviewReplay ? (
@@ -1319,8 +1319,8 @@ function PublicMapZoomPanel({
type="button"
onClick={onResumePreviewReplay}
className="uhm-public-play-btn resume"
- aria-label="Resume selected replay"
- title="Tiep tuc replay dang tam dung"
+ aria-label="Tiếp tục diễn biến đã chọn"
+ title="Tiếp tục diễn biến đang tạm dừng"
>
Tiếp tục
@@ -1331,8 +1331,8 @@ function PublicMapZoomPanel({
type="button"
onClick={onStopPreviewReplay}
className="uhm-public-play-btn stop"
- aria-label="Stop selected replay"
- title="Dung replay dang phat"
+ aria-label="Dừng diễn biến đã chọn"
+ title="Dừng diễn biến đang phát"
>
Dừng
@@ -1342,7 +1342,7 @@ function PublicMapZoomPanel({
type="button"
onClick={() => zoomByStep(-0.8)}
className="uhm-public-zoom-btn"
- aria-label="Zoom out"
+ aria-label="Thu nhỏ bản đồ"
>
-
@@ -1368,13 +1368,13 @@ function PublicMapZoomPanel({
isDraggingRef.current = false;
}}
onChange={(event) => handleSliderChange(Number(event.target.value))}
- aria-label="Map zoom"
+ aria-label="Mức thu phóng bản đồ"
/>
zoomByStep(0.8)}
className="uhm-public-zoom-btn"
- aria-label="Zoom in"
+ aria-label="Phóng to bản đồ"
>
+
diff --git a/src/uhm/components/preview/WikiSelectionPanel.tsx b/src/uhm/components/preview/WikiSelectionPanel.tsx
index 64651ff..6ff454d 100644
--- a/src/uhm/components/preview/WikiSelectionPanel.tsx
+++ b/src/uhm/components/preview/WikiSelectionPanel.tsx
@@ -104,7 +104,7 @@ export default function WikiSelectionPanel({
outline: "none",
}}
className="hover:bg-slate-700/50 hover:text-slate-100"
- aria-label="Close wiki chooser"
+ aria-label="Đóng bảng chọn wiki"
>
x
diff --git a/src/uhm/components/wiki/PublicWikiSidebar.tsx b/src/uhm/components/wiki/PublicWikiSidebar.tsx
index 8739b21..f49e49a 100644
--- a/src/uhm/components/wiki/PublicWikiSidebar.tsx
+++ b/src/uhm/components/wiki/PublicWikiSidebar.tsx
@@ -596,7 +596,7 @@ function PublicWikiSidebar({
outline: "none",
}}
className="hover:bg-slate-700/50 hover:text-slate-100"
- aria-label="Close wiki sidebar"
+ aria-label="Đóng khung wiki"
>
x