2026-05-16 00:18:22 +08:00
import React , {
createContext ,
useCallback ,
useContext ,
useMemo ,
useState ,
} from "react" ;
2026-05-26 10:03:12 +08:00
export type Lang = "zh-CN" | "en" | "ja" | "ko" | "vi" | "id" | "ms" ;
2026-05-16 00:18:22 +08:00
type Dict = Record < string , string > ;
2026-05-26 07:36:53 +08:00
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_ID( Reown Cloud) , 否则无法使用 WalletConnect/扫码。" ,
walletSetupNeeded : "钱包扫码未启用(请在服务器配置环境变量)" ,
2026-05-26 10:03:12 +08:00
lang_zh_CN : "中文" ,
2026-05-26 07:36:53 +08:00
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)" ,
2026-05-26 10:03:12 +08:00
lang_zh_CN : "Chinese" ,
2026-05-26 07:36:53 +08:00
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 > = {
2026-05-26 10:03:12 +08:00
"zh-CN" : {
lang_zh_CN : "中文" ,
2026-05-16 00:18:22 +08:00
lang_en : "English" ,
2026-05-26 07:36:53 +08:00
lang_ja : "日本語" ,
lang_ko : "한국어" ,
lang_vi : "Tiếng Việt" ,
lang_id : "Bahasa Indonesia" ,
lang_ms : "Bahasa Melayu" ,
2026-05-16 00:18:22 +08:00
} ,
en : {
2026-05-26 10:03:12 +08:00
lang_zh_CN : "Chinese" ,
2026-05-16 00:18:22 +08:00
lang_en : "English" ,
2026-05-26 07:36:53 +08:00
lang_ja : "Japanese" ,
lang_ko : "Korean" ,
lang_vi : "Vietnamese" ,
lang_id : "Indonesian" ,
lang_ms : "Malay" ,
} ,
ja : {
2026-05-26 10:03:12 +08:00
lang_zh_CN : "中国語" ,
2026-05-26 07:36:53 +08:00
lang_en : "英語" ,
lang_ja : "日本語" ,
lang_ko : "韓国語" ,
lang_vi : "ベトナム語" ,
lang_id : "インドネシア語" ,
lang_ms : "マレー語" ,
2026-05-16 00:18:22 +08:00
} ,
2026-05-26 07:36:53 +08:00
ko : {
2026-05-26 10:03:12 +08:00
lang_zh_CN : "중국어" ,
2026-05-26 07:36:53 +08:00
lang_en : "영어" ,
lang_ja : "일본어" ,
lang_ko : "한국어" ,
lang_vi : "베트남어" ,
lang_id : "인도네시아어" ,
lang_ms : "말레이어" ,
} ,
vi : {
2026-05-26 10:03:12 +08:00
lang_zh_CN : "Tiếng Trung" ,
2026-05-26 07:36:53 +08:00
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 : {
2026-05-26 10:03:12 +08:00
lang_zh_CN : "Bahasa Tionghoa" ,
2026-05-26 07:36:53 +08:00
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 : {
2026-05-26 10:03:12 +08:00
lang_zh_CN : "Bahasa Cina" ,
2026-05-26 07:36:53 +08:00
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 > = {
2026-05-26 10:03:12 +08:00
"zh-CN" : { . . . zhDict , . . . languageNames [ "zh-CN" ] } ,
2026-05-26 07:36:53 +08:00
en : { . . . enDict , . . . languageNames . en } ,
ja : { . . . enDict , . . . languageNames . ja } ,
ko : { . . . enDict , . . . languageNames . ko } ,
vi : { . . . enDict , . . . languageNames . vi } ,
id : { . . . enDict , . . . languageNames . id } ,
ms : { . . . enDict , . . . languageNames . ms } ,
2026-05-16 00:18:22 +08:00
} ;
2026-05-26 07:36:53 +08:00
/** Fixed locale lookup (admin UI uses Simplified Chinese). */
2026-05-16 00:18:22 +08:00
export function tLang ( lang : Lang , key : string ) : string {
2026-05-26 07:36:53 +08:00
return dict [ lang ] [ key ] || dict . en [ key ] || key ;
2026-05-16 00:18:22 +08:00
}
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 > ( ( ) = > {
2026-05-26 07:36:53 +08:00
const s = localStorage . getItem ( LANG_KEY ) ;
2026-05-26 10:03:12 +08:00
if ( s === "zh" || s === "zh-TW" ) return "zh-CN" ;
2026-05-26 07:36:53 +08:00
if (
2026-05-26 10:03:12 +08:00
s === "zh-CN" ||
2026-05-26 07:36:53 +08:00
s === "en" ||
s === "ja" ||
s === "ko" ||
s === "vi" ||
s === "id" ||
s === "ms"
)
return s ;
return "en" ;
2026-05-16 00:18:22 +08:00
} ) ;
const setLang = ( l : Lang ) = > {
localStorage . setItem ( LANG_KEY , l ) ;
setLangState ( l ) ;
} ;
const t = useCallback (
2026-05-26 07:36:53 +08:00
( k : string ) = > dict [ lang ] [ k ] || dict . en [ k ] || k ,
2026-05-16 00:18:22 +08:00
[ 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 ) {
2026-05-26 10:03:12 +08:00
return lang ;
2026-05-16 00:18:22 +08:00
}