Files
Arkie-Library-Frontend/src/i18n.tsx
2026-05-26 10:03:12 +08:00

388 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, {
createContext,
useCallback,
useContext,
useMemo,
useState,
} from "react";
export type Lang = "zh-CN" | "en" | "ja" | "ko" | "vi" | "id" | "ms";
type Dict = Record<string, string>;
const zhDict: Dict = {
brand: "ARK 数据库",
mainNav: "网站导航",
home: "首页",
all: "全部资料",
categories: "分类浏览",
latest: "最新更新",
official: "官方推荐",
popular: "热门资料",
search: "搜索",
searchPlaceholder: "搜索资料...",
searchNow: "立即搜索资料",
viewAll: "查看全部",
heroTitle: "ARK 官方数据库",
heroSub:
"集中、分类、管理 ARK 数据库,让你快速找到所需资源,推动社群共识与成长。",
categorySection: "资料分类",
officialSection: "官方推荐",
latestSection: "最新更新",
popularSection: "热门资料",
preview: "预览",
download: "下载",
share: "分享",
profile: "个人中心",
langLabel: "语言",
admin: "后台",
login: "登录",
logout: "退出",
email: "邮箱",
password: "密码",
dashboard: "仪表盘",
resources: "资料管理",
newResource: "新增资料",
save: "保存",
title: "标题",
description: "简介",
type: "类型",
language: "语言",
category: "分类",
status: "状态",
public: "公开",
downloadable: "可下载",
recommended: "首页推荐",
cover: "封面图 URL",
fileUrl: "文件 URL",
externalUrl: "外部链接",
body: "文案内容",
badge: "推荐标签",
published: "已发布",
draft: "草稿",
archived: "归档",
noResults: "找不到符合的资料,请换个关键字或浏览分类。",
copyLink: "复制链接",
related: "相关资料",
total: "总资料",
views: "浏览",
downloads: "下载",
wallet: "钱包",
walletPageTitle: "钱包登录",
walletPageIntro:
"连接 Web3 钱包以使用会员相关功能。采用标准签名登录,不发送交易、不消耗 gas。",
walletStepExtension:
"电脑已安装浏览器扩展钱包(如 MetaMask可直接连接。",
walletStepQR:
"电脑未安装钱包时:在连接窗口选择 WalletConnect用手机钱包扫描 QR Code。",
walletStepSign: "连接成功后,点击「签署登录」并在钱包内签名即可完成验证。",
signInWallet: "签署登录",
walletSignedIn: "已验证登录",
walletLogout: "退出钱包",
walletMissingProjectId:
"请配置 VITE_WALLETCONNECT_PROJECT_IDReown Cloud否则无法使用 WalletConnect/扫码。",
walletSetupNeeded: "钱包扫码未启用(请在服务器配置环境变量)",
lang_zh_CN: "中文",
lang_en: "English",
lang_ja: "日本語",
lang_ko: "한국어",
lang_vi: "Tiếng Việt",
lang_id: "Bahasa Indonesia",
lang_ms: "Bahasa Melayu",
filterAll: "全部",
sortPublished: "发布时间",
type_ppt: "PPT",
type_music: "音乐",
type_video: "视频",
type_image: "图片",
type_pdf: "PDF",
type_link: "链接",
type_text: "文字",
type_archive: "压缩包",
type_zip: "ZIP",
adminLoginTitle: "管理后台登录",
adminEditResource: "编辑资料",
adminVideoFileHint:
"上传视频文件MP4/WebM/MOV 等),类型请选择「视频」;保存后前台自动播放(默认静音,可点喇叭开声音)。",
adminStatTodayNew: "今日新增",
adminStatFavorites: "收藏",
adminMetricDownloads: "下载",
adminMetricFavorites: "收藏",
adminMetricViews: "浏览",
edit: "编辑",
backToList: "返回列表",
sortOrderLabel: "排序权重",
previewUrlLabel: "预览网址",
tagsCommaLabel: "标签(逗号分隔)",
uploadFile: "上传文件",
loading: "加载中…",
paginationPrev: "上一页",
paginationNext: "下一页",
listRange: "显示 {{from}}{{to}},共 {{total}} 条",
pageIndicator: "{{c}} / {{p}} 页",
resourceLangFilter: "资料语言",
filterTagClear: "清除标签",
filterLanguageAll: "全部语言",
aboutTitle: "关于本站",
aboutIntro:
"ARK 数据库汇总官方教材、公告、视频与常用文件,帮助社区快速获取一致版本的可信内容。\n\n本站仅供展示与索引权利归属以官方公告为准。",
footerAbout: "关于本站",
footerAdminLogin: "管理员登录",
adminSearchLogs: "搜索记录",
adminMetricShares: "分享",
adminSearchQuery: "查询词",
adminSearchTime: "时间",
adminSearchId: "编号",
};
const enDict: Dict = {
brand: "ARK Library",
mainNav: "Site menu",
home: "Home",
all: "All assets",
categories: "Categories",
latest: "Latest",
official: "Official picks",
popular: "Popular",
search: "Search",
searchPlaceholder: "Search resources...",
searchNow: "Search now",
viewAll: "View all",
heroTitle: "ARK Official Library",
heroSub:
"Centralize, organize, and manage the ARK library so you can find what you need fast and help the community grow together.",
categorySection: "Categories",
officialSection: "Official recommendations",
latestSection: "Latest updates",
popularSection: "Popular assets",
preview: "Preview",
download: "Download",
share: "Share",
profile: "Profile",
langLabel: "Language",
admin: "Admin",
login: "Sign in",
logout: "Sign out",
email: "Email",
password: "Password",
dashboard: "Dashboard",
resources: "Resources",
newResource: "New resource",
save: "Save",
title: "Title",
description: "Description",
type: "Type",
language: "Language",
category: "Category",
status: "Status",
public: "Public",
downloadable: "Downloadable",
recommended: "Featured",
cover: "Cover image URL",
fileUrl: "File URL",
externalUrl: "External URL",
body: "Text body",
badge: "Badge label",
published: "Published",
draft: "Draft",
archived: "Archived",
noResults: "No results. Try another keyword or browse categories.",
copyLink: "Copy link",
related: "Related",
total: "Total items",
views: "Views",
downloads: "Downloads",
wallet: "Wallet",
walletPageTitle: "Wallet sign-in",
walletPageIntro:
"Connect a Web3 wallet for member features. This uses a standard signed message — no transaction and no gas.",
walletStepExtension:
"On desktop with a browser extension (e.g. MetaMask), connect directly.",
walletStepQR:
"On desktop without an extension: choose WalletConnect in the modal and scan the QR code with your mobile wallet.",
walletStepSign:
'After connecting, tap "Sign in" and approve the message in your wallet to verify.',
signInWallet: "Sign in",
walletSignedIn: "Signed in",
walletLogout: "Disconnect",
walletMissingProjectId:
"Set VITE_WALLETCONNECT_PROJECT_ID (free on Reown Cloud). Required for WalletConnect / QR login.",
walletSetupNeeded: "Wallet QR login disabled (set env on server)",
lang_zh_CN: "Chinese",
lang_en: "English",
lang_ja: "Japanese",
lang_ko: "Korean",
lang_vi: "Vietnamese",
lang_id: "Indonesian",
lang_ms: "Malay",
filterAll: "All types",
sortPublished: "Published date",
type_ppt: "PPT",
type_music: "Music",
type_video: "Video",
type_image: "Image",
type_pdf: "PDF",
type_link: "Link",
type_text: "Text",
type_archive: "Archive",
type_zip: "ZIP",
adminLoginTitle: "Admin sign in",
adminEditResource: "Edit resource",
adminVideoFileHint:
"Upload a video file (MP4/WebM/MOV, etc.) and set type to Video; the site will autoplay (muted by default — user can unmute).",
adminStatTodayNew: "New today",
adminStatFavorites: "Favorites",
adminMetricDownloads: "Downloads",
adminMetricFavorites: "Favorites",
adminMetricViews: "Views",
edit: "Edit",
backToList: "Back to list",
sortOrderLabel: "Sort order",
previewUrlLabel: "Preview URL",
tagsCommaLabel: "Tags (comma-separated)",
uploadFile: "Upload",
loading: "Loading…",
paginationPrev: "Previous",
paginationNext: "Next",
listRange: "Showing {{from}}{{to}} of {{total}}",
pageIndicator: "Page {{c}} / {{p}}",
resourceLangFilter: "Resource language",
filterTagClear: "Clear tag",
filterLanguageAll: "All languages",
aboutTitle: "About this site",
aboutIntro:
"The ARK library brings together official decks, announcements, videos, and common files so the community can find consistent, trustworthy versions quickly.\n\nThis site is for discovery and indexing only; rights remain with official notices.",
footerAbout: "About",
footerAdminLogin: "Admin sign-in",
adminSearchLogs: "Search logs",
adminMetricShares: "Shares",
adminSearchQuery: "Query",
adminSearchTime: "Time",
adminSearchId: "ID",
};
const languageNames: Record<Lang, Dict> = {
"zh-CN": {
lang_zh_CN: "中文",
lang_en: "English",
lang_ja: "日本語",
lang_ko: "한국어",
lang_vi: "Tiếng Việt",
lang_id: "Bahasa Indonesia",
lang_ms: "Bahasa Melayu",
},
en: {
lang_zh_CN: "Chinese",
lang_en: "English",
lang_ja: "Japanese",
lang_ko: "Korean",
lang_vi: "Vietnamese",
lang_id: "Indonesian",
lang_ms: "Malay",
},
ja: {
lang_zh_CN: "中国語",
lang_en: "英語",
lang_ja: "日本語",
lang_ko: "韓国語",
lang_vi: "ベトナム語",
lang_id: "インドネシア語",
lang_ms: "マレー語",
},
ko: {
lang_zh_CN: "중국어",
lang_en: "영어",
lang_ja: "일본어",
lang_ko: "한국어",
lang_vi: "베트남어",
lang_id: "인도네시아어",
lang_ms: "말레이어",
},
vi: {
lang_zh_CN: "Tiếng Trung",
lang_en: "Tiếng Anh",
lang_ja: "Tiếng Nhật",
lang_ko: "Tiếng Hàn",
lang_vi: "Tiếng Việt",
lang_id: "Tiếng Indonesia",
lang_ms: "Tiếng Mã Lai",
},
id: {
lang_zh_CN: "Bahasa Tionghoa",
lang_en: "Bahasa Inggris",
lang_ja: "Bahasa Jepang",
lang_ko: "Bahasa Korea",
lang_vi: "Bahasa Vietnam",
lang_id: "Bahasa Indonesia",
lang_ms: "Bahasa Melayu",
},
ms: {
lang_zh_CN: "Bahasa Cina",
lang_en: "Bahasa Inggeris",
lang_ja: "Bahasa Jepun",
lang_ko: "Bahasa Korea",
lang_vi: "Bahasa Vietnam",
lang_id: "Bahasa Indonesia",
lang_ms: "Bahasa Melayu",
},
};
const dict: Record<Lang, Dict> = {
"zh-CN": { ...zhDict, ...languageNames["zh-CN"] },
en: { ...enDict, ...languageNames.en },
ja: { ...enDict, ...languageNames.ja },
ko: { ...enDict, ...languageNames.ko },
vi: { ...enDict, ...languageNames.vi },
id: { ...enDict, ...languageNames.id },
ms: { ...enDict, ...languageNames.ms },
};
/** Fixed locale lookup (admin UI uses Simplified Chinese). */
export function tLang(lang: Lang, key: string): string {
return dict[lang][key] || dict.en[key] || key;
}
type Ctx = { lang: Lang; setLang: (l: Lang) => void; t: (k: string) => string };
const I18nCtx = createContext<Ctx | null>(null);
const LANG_KEY = "ark_lang";
export function I18nProvider({ children }: { children: React.ReactNode }) {
const [lang, setLangState] = useState<Lang>(() => {
const s = localStorage.getItem(LANG_KEY);
if (s === "zh" || s === "zh-TW") return "zh-CN";
if (
s === "zh-CN" ||
s === "en" ||
s === "ja" ||
s === "ko" ||
s === "vi" ||
s === "id" ||
s === "ms"
)
return s;
return "en";
});
const setLang = (l: Lang) => {
localStorage.setItem(LANG_KEY, l);
setLangState(l);
};
const t = useCallback(
(k: string) => dict[lang][k] || dict.en[k] || k,
[lang],
);
const v = useMemo(() => ({ lang, setLang, t }), [lang, t]);
return <I18nCtx.Provider value={v}>{children}</I18nCtx.Provider>;
}
export function useI18n() {
const v = useContext(I18nCtx);
if (!v) throw new Error("I18nProvider missing");
return v;
}
export function langQuery(lang: Lang) {
return lang;
}