Merge remote-tracking branch 'origin/main' into feat/pay-v2-integration
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 25s
ci-pangolin / Lint — shellcheck (push) Successful in 29s
ci-pangolin / Cleartext Scan — Android 禁明文 (push) Successful in 22s
ci-pangolin / OpenAPI Sync Check (push) Successful in 40s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 19s
ci-pangolin / Flutter — analyze + test (push) Failing after 4m59s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 1m51s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Failing after 1m33s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Failing after 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m59s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (push) Failing after 4s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 25s
ci-pangolin / Lint — shellcheck (push) Successful in 29s
ci-pangolin / Cleartext Scan — Android 禁明文 (push) Successful in 22s
ci-pangolin / OpenAPI Sync Check (push) Successful in 40s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 19s
ci-pangolin / Flutter — analyze + test (push) Failing after 4m59s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 1m51s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (push) Successful in 5s
ci-pangolin / Go — build + test (push) Failing after 1m33s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Failing after 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m59s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (push) Failing after 4s
# Conflicts: # docs/index.html # server/cmd/server/main.go
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
---
|
||||
import Icon from './Icon.astro';
|
||||
import type { T } from '../i18n/strings';
|
||||
import type { T, Lang } from '../i18n/strings';
|
||||
|
||||
interface Props { t: T }
|
||||
const { t } = Astro.props;
|
||||
interface Props { t: T; lang: Lang }
|
||||
const { t, lang } = Astro.props;
|
||||
|
||||
// 中文走 /zh/docs/*,其余语言暂共用英文文档(/docs/*)。
|
||||
const base = lang === 'zh' ? '/zh/docs' : '/docs';
|
||||
|
||||
const docs = [
|
||||
{ icon: 'rocket', t: 'docs.1t', d: 'docs.1d' },
|
||||
{ icon: 'circle-help', t: 'docs.2t', d: 'docs.2d' },
|
||||
{ icon: 'shield-check', t: 'docs.3t', d: 'docs.3d' },
|
||||
{ icon: 'lock', t: 'docs.4t', d: 'docs.4d' },
|
||||
{ icon: 'rocket', t: 'docs.1t', d: 'docs.1d', slug: 'quickstart' },
|
||||
{ icon: 'circle-help', t: 'docs.2t', d: 'docs.2d', slug: 'faq' },
|
||||
{ icon: 'shield-check', t: 'docs.3t', d: 'docs.3d', slug: 'protocol' },
|
||||
{ icon: 'lock', t: 'docs.4t', d: 'docs.4d', slug: 'privacy' },
|
||||
];
|
||||
---
|
||||
<section id="docs">
|
||||
@@ -21,11 +24,11 @@ const docs = [
|
||||
<div class="wrap">
|
||||
<div class="docs-grid">
|
||||
{docs.map((d) => (
|
||||
<a class="doc">
|
||||
<a class="doc" href={`${base}/${d.slug}/`}>
|
||||
<div class="ico"><Icon name={d.icon} /></div>
|
||||
<h3>{t(d.t)}</h3>
|
||||
<p>{t(d.d)}</p>
|
||||
<span class="ln">{t('docs.read')}</span>
|
||||
<span class="ln">{t('docs.read')}<Icon name="arrow-right" /></span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
---
|
||||
import Icon from './Icon.astro';
|
||||
import type { T } from '../i18n/strings';
|
||||
import { SITE } from '../config/site';
|
||||
|
||||
interface Props { t: T }
|
||||
const { t } = Astro.props;
|
||||
|
||||
const plats = [
|
||||
// href 缺省 = 本轮未接入下载(iOS 走 TestFlight / Linux),按钮渲染为禁用态占位。
|
||||
// Android + Windows + macOS 已由客户端 CI 产出真实产物并部署到 /downloads。
|
||||
const plats: { icon: string; name: string; ver: string; href?: string }[] = [
|
||||
{ icon: 'smartphone', name: 'iOS', ver: 'iOS 16+' },
|
||||
{ icon: 'smartphone', name: 'Android', ver: 'Android 9+' },
|
||||
{ icon: 'laptop', name: 'macOS', ver: 'macOS 12+' },
|
||||
{ icon: 'monitor', name: 'Windows', ver: 'Win 10/11' },
|
||||
{ icon: 'smartphone', name: 'Android', ver: 'Android 9+', href: SITE.downloads.android },
|
||||
{ icon: 'laptop', name: 'macOS', ver: 'macOS 12+', href: SITE.downloads.macos },
|
||||
{ icon: 'monitor', name: 'Windows', ver: 'Win 10/11', href: SITE.downloads.windows },
|
||||
{ icon: 'terminal', name: 'Linux', ver: 'deb / rpm' },
|
||||
];
|
||||
---
|
||||
@@ -26,7 +29,11 @@ const plats = [
|
||||
<div class="ico"><Icon name={p.icon} /></div>
|
||||
<div class="pn">{p.name}</div>
|
||||
<div class="pv">{p.ver}</div>
|
||||
<a class="gb"><Icon name="download" /><span>{t('dl.get')}</span></a>
|
||||
{p.href ? (
|
||||
<a class="gb" href={p.href}><Icon name="download" /><span>{t('dl.get')}</span></a>
|
||||
) : (
|
||||
<span class="gb disabled" aria-disabled="true"><Icon name="download" /><span>{t('dl.soon')}</span></span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
import Brand from './Brand.astro';
|
||||
import type { T } from '../i18n/strings';
|
||||
import { SITE } from '../config/site';
|
||||
|
||||
interface Props { t: T }
|
||||
const { t } = Astro.props;
|
||||
@@ -11,7 +12,7 @@ const { t } = Astro.props;
|
||||
<div>
|
||||
<div class="nm">
|
||||
<Brand variant="footer" size={28} />
|
||||
穿山甲
|
||||
{t('nav.brand')}
|
||||
</div>
|
||||
<p class="tag">{t('ft.tag')}</p>
|
||||
</div>
|
||||
@@ -27,7 +28,6 @@ const { t } = Astro.props;
|
||||
<h4>{t('ft.resources')}</h4>
|
||||
<ul>
|
||||
<li><a href="#docs">{t('ft.docs')}</a></li>
|
||||
<li><a href="#blog">{t('ft.blog')}</a></li>
|
||||
<li><a href="#docs">{t('ft.faq')}</a></li>
|
||||
<li><a href="#docs">{t('ft.privacy')}</a></li>
|
||||
</ul>
|
||||
@@ -35,10 +35,10 @@ const { t } = Astro.props;
|
||||
<div>
|
||||
<h4>{t('ft.contact')}</h4>
|
||||
<ul>
|
||||
<li><a class="mono">shop.pangolin.vpn</a></li>
|
||||
<li><a class="mono">Telegram @PangolinVPN_bot</a></li>
|
||||
<li><a class="mono">LINE @pangolinvpn</a></li>
|
||||
<li><a class="mono">support@pangolin.vpn</a></li>
|
||||
<li><a class="mono">{SITE.store.label}</a></li>
|
||||
<li><a class="mono" href={SITE.telegram.url} target="_blank" rel="noopener">Telegram {SITE.telegram.handle}</a></li>
|
||||
<li><a class="mono">LINE {SITE.line.handle}</a></li>
|
||||
<li><a class="mono" href={`mailto:${SITE.email}`}>{SITE.email}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
* 迁自 design/ui_kits/website/index.html 的 <header> + site.js 的菜单/滚动毛玻璃逻辑。
|
||||
* 语言切换由原型的 JS 文本替换改为「路由跳转」(zh=/, en=/en/),单显不并排(铁律 6)。
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Download, Menu } from 'lucide-react';
|
||||
import { SITE } from '../config/site';
|
||||
|
||||
function Mark() {
|
||||
return (
|
||||
@@ -28,6 +29,55 @@ function Mark() {
|
||||
export default function Header({ lang = 'zh', t = {} }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [langOpen, setLangOpen] = useState(false);
|
||||
const [userOpen, setUserOpen] = useState(false);
|
||||
const [loggedIn, setLoggedIn] = useState(false);
|
||||
const [email, setEmail] = useState('');
|
||||
const langRef = useRef(null);
|
||||
const userRef = useRef(null);
|
||||
|
||||
// 登录后回跳主页:用户中心带 ?redirect=/(配合 usercenter 登录成功后回跳)。
|
||||
const loginHref = `${SITE.usercenter}?redirect=/`;
|
||||
// 用户名截断显示:优先邮箱 @ 前部分,缺失时回退通用词。
|
||||
const displayName = (email && email.split('@')[0]) || 'Account';
|
||||
|
||||
// 与用户中心同源:登录后 localStorage 存 pg_uc_refresh(+ pg_uc_email)→ 显示用户名下拉。
|
||||
useEffect(() => {
|
||||
try {
|
||||
setLoggedIn(!!localStorage.getItem('pg_uc_refresh'));
|
||||
setEmail(localStorage.getItem('pg_uc_email') || '');
|
||||
} catch {
|
||||
setLoggedIn(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 清登录态并跳转(切换用户 = 回登录页;退出 = 回主页)。
|
||||
const clearSession = () => {
|
||||
try {
|
||||
localStorage.removeItem('pg_uc_refresh');
|
||||
localStorage.removeItem('pg_uc_email');
|
||||
} catch { /* ignore */ }
|
||||
};
|
||||
const onSwitch = () => { clearSession(); window.location.href = loginHref; };
|
||||
const onLogout = () => { clearSession(); window.location.href = '/'; };
|
||||
|
||||
useEffect(() => {
|
||||
if (!langOpen) return;
|
||||
const onDoc = (e) => { if (langRef.current && !langRef.current.contains(e.target)) setLangOpen(false); };
|
||||
const onKey = (e) => { if (e.key === 'Escape') setLangOpen(false); };
|
||||
document.addEventListener('mousedown', onDoc);
|
||||
document.addEventListener('keydown', onKey);
|
||||
return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
|
||||
}, [langOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userOpen) return;
|
||||
const onDoc = (e) => { if (userRef.current && !userRef.current.contains(e.target)) setUserOpen(false); };
|
||||
const onKey = (e) => { if (e.key === 'Escape') setUserOpen(false); };
|
||||
document.addEventListener('mousedown', onDoc);
|
||||
document.addEventListener('keydown', onKey);
|
||||
return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
|
||||
}, [userOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 8);
|
||||
@@ -41,15 +91,21 @@ export default function Header({ lang = 'zh', t = {} }) {
|
||||
['#pricing', t.pricing],
|
||||
['#download', t.download],
|
||||
['#docs', t.docs],
|
||||
['#blog', t.blog],
|
||||
];
|
||||
|
||||
// 6 语言:默认 en=/,其余 /<lang>/。段控横排会挤,用原生下拉。
|
||||
const langs = [
|
||||
['en', 'English'], ['zh', '中文'], ['ja', '日本語'],
|
||||
['ko', '한국어'], ['ru', 'Русский'], ['es', 'Español'],
|
||||
];
|
||||
const langHref = (l) => (l === 'en' ? '/' : `/${l}/`);
|
||||
|
||||
return (
|
||||
<header class={`hdr${scrolled ? ' scrolled' : ''}`}>
|
||||
<div class="wrap row">
|
||||
<a class="brand" href="#top">
|
||||
<Mark />
|
||||
<span class="nm">穿山甲</span>
|
||||
<span class="nm">{t.brand || 'Pangolin'}</span>
|
||||
</a>
|
||||
<nav class="nav">
|
||||
{nav.map(([href, label]) => (
|
||||
@@ -57,11 +113,58 @@ export default function Header({ lang = 'zh', t = {} }) {
|
||||
))}
|
||||
</nav>
|
||||
<div class="right">
|
||||
<div class="langseg">
|
||||
<a data-lang="zh" class={lang === 'zh' ? 'on' : undefined} href="/">中文</a>
|
||||
<a data-lang="en" class={lang === 'en' ? 'on' : undefined} href="/en/">EN</a>
|
||||
<div ref={langRef} class="langwrap">
|
||||
<button
|
||||
type="button"
|
||||
class="langsel"
|
||||
onClick={() => setLangOpen((o) => !o)}
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded={langOpen}
|
||||
aria-label="Language"
|
||||
>
|
||||
<span>{(langs.find(([c]) => c === lang) || ['', 'English'])[1]}</span>
|
||||
<svg class="caret" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
{langOpen && (
|
||||
<div class="langmenu" role="listbox">
|
||||
{langs.map(([code, label]) => {
|
||||
const on = code === lang;
|
||||
return (
|
||||
<a key={code} role="option" aria-selected={on} href={langHref(code)}>
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span class="linklogin">{t.login}</span>
|
||||
{loggedIn ? (
|
||||
<div ref={userRef} class="langwrap usermenu">
|
||||
<button
|
||||
type="button"
|
||||
class="langsel userbtn"
|
||||
onClick={() => setUserOpen((o) => !o)}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={userOpen}
|
||||
>
|
||||
<span class="uname">{displayName}</span>
|
||||
<svg class="caret" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
{userOpen && (
|
||||
<div class="langmenu" role="menu">
|
||||
<a role="menuitem" href={SITE.usercenter}>{t.mcenter}</a>
|
||||
<button role="menuitem" type="button" onClick={onSwitch}>{t.mswitch}</button>
|
||||
<button role="menuitem" type="button" onClick={onLogout}>{t.mlogout}</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<a class="linklogin" href={loginHref}>{t.login}</a>
|
||||
)}
|
||||
<a class="btn btn-primary" href="#download">
|
||||
<Download />
|
||||
<span>{t.get}</span>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
---
|
||||
import Icon from './Icon.astro';
|
||||
import PricingPlans from './PricingPlans.jsx';
|
||||
import { PRICES, type T } from '../i18n/strings';
|
||||
import { PRICES, type T, type Lang } from '../i18n/strings';
|
||||
import { SITE } from '../config/site';
|
||||
|
||||
interface Props { t: T }
|
||||
const { t } = Astro.props;
|
||||
interface Props { t: T; lang: Lang }
|
||||
const { t, lang } = Astro.props;
|
||||
const P = PRICES[lang];
|
||||
|
||||
const plansData = {
|
||||
monthly: t('price.monthly'),
|
||||
@@ -16,7 +18,7 @@ const plansData = {
|
||||
{
|
||||
key: 'free',
|
||||
name: t('price.free'),
|
||||
price: PRICES.free,
|
||||
price: P.free,
|
||||
desc: t('price.freedesc'),
|
||||
feats: [t('pf.free1'), t('pf.free2'), t('pf.free3'), t('pf.free4')],
|
||||
cta: t('price.cta_free'),
|
||||
@@ -26,7 +28,7 @@ const plansData = {
|
||||
{
|
||||
key: 'pro',
|
||||
name: t('price.pro'),
|
||||
price: PRICES.pro,
|
||||
price: P.pro,
|
||||
desc: t('price.prodesc'),
|
||||
feats: [t('pf.pro1'), t('pf.pro2'), t('pf.pro3'), t('pf.pro4'), t('pf.pro5')],
|
||||
cta: t('price.cta_pro'),
|
||||
@@ -36,7 +38,7 @@ const plansData = {
|
||||
{
|
||||
key: 'team',
|
||||
name: t('price.team'),
|
||||
price: PRICES.team,
|
||||
price: P.team,
|
||||
desc: t('price.teamdesc'),
|
||||
feats: [t('pf.team1'), t('pf.team2'), t('pf.team3'), t('pf.team4')],
|
||||
cta: t('price.cta_team'),
|
||||
@@ -48,7 +50,7 @@ const plansData = {
|
||||
|
||||
// 功能对比表行:[名称键, free, pro, team];值可为字符串或 'check' / 'dash'。
|
||||
const rows = [
|
||||
{ label: 'cmp.locations', free: '1', pro: '80+', team: '80+' },
|
||||
{ label: 'cmp.locations', free: '1', pro: t('cmp.loc_all'), team: t('cmp.loc_all') },
|
||||
{ label: 'cmp.data', free: t('cmp.daily'), pro: t('cmp.unlimited'), team: t('cmp.unlimited') },
|
||||
{ label: 'cmp.time', free: t('cmp.time_free'), pro: t('cmp.unlimited'), team: t('cmp.unlimited') },
|
||||
{ label: 'cmp.devices', free: '1', pro: '5', team: '10' },
|
||||
@@ -81,9 +83,9 @@ function cell(v: string) {
|
||||
<div class="chips">
|
||||
<span class="chip"><Icon name="shopping-bag" /><span>{t('pay.store')}</span></span>
|
||||
<span class="chip"><Icon name="credit-card" /><span>{t('pay.usdt')}</span></span>
|
||||
<span class="chip"><Icon name="send" />Telegram @PangolinVPN_bot</span>
|
||||
<span class="chip"><Icon name="message-circle" />LINE @pangolinvpn</span>
|
||||
<span class="chip"><Icon name="mail" />buy@pangolin.vpn</span>
|
||||
<a class="chip" href={SITE.telegram.url} target="_blank" rel="noopener"><Icon name="send" />Telegram {SITE.telegram.handle}</a>
|
||||
<span class="chip"><Icon name="message-circle" />LINE {SITE.line.handle}</span>
|
||||
<a class="chip" href={`mailto:${SITE.email}`}><Icon name="mail" />{SITE.email}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* site.ts — 站点级配置「单一真相源」:域名、联系方式、对外渠道。
|
||||
*
|
||||
* 改这一处即可:组件(Footer / Pricing)与 astro.config 的 canonical 域名都从这里读。
|
||||
* 此处只承载「事实值」(URL / 邮箱 / handle);可翻译的展示文案仍走 i18n/strings.ts。
|
||||
*/
|
||||
export const SITE = {
|
||||
/** canonical 主域名。镜像部署可用 SITE_URL 环境变量覆盖(见 astro.config.mjs)。 */
|
||||
url: 'https://pangolin.yanmeiai.com',
|
||||
|
||||
/** 联系邮箱。Cloudflare Email Routing 转发到个人 Gmail。 */
|
||||
email: 'pangolin@yanmeiai.com',
|
||||
|
||||
/** Telegram 公开频道(群为私有,公开页不放)。 */
|
||||
telegram: { handle: '@pangolin_app', url: 'https://t.me/pangolin_app' },
|
||||
|
||||
/** LINE 官方账号 —— 待确认(#24)。 */
|
||||
line: { handle: '@pangolinvpn' },
|
||||
|
||||
/** 自助发卡商店 —— 占位待定(#24)。 */
|
||||
store: { label: 'shop.pangolin.vpn' },
|
||||
|
||||
/** Web 用户中心(web/usercenter,Next.js 静态导出 → CF Pages)。登录/订阅/设备管理入口。 */
|
||||
usercenter: 'https://pangolin.yanmeiai.com/user/',
|
||||
|
||||
/**
|
||||
* 客户端安装包直链。控制面 pangolin-server 通过 Cloudflare Tunnel 对外暴露
|
||||
* /downloads/<file>(origin = 127.0.0.1:8080),文件由
|
||||
* scripts/ci/deploy-client.sh 每次构建 scp 覆盖到 pangolin1:/var/lib/pangolin/downloads/,
|
||||
* 每平台固定文件名、只保留最新一份。macOS 自 client-v1.0.59 起接入;
|
||||
* iOS 走 TestFlight(无直接下载文件)、Linux 本轮未接入下载按钮。
|
||||
*/
|
||||
downloads: {
|
||||
android: 'https://api.yanmeiai.com/downloads/pangolin-android.apk',
|
||||
windows: 'https://api.yanmeiai.com/downloads/pangolin-windows-x64-setup.exe',
|
||||
macos: 'https://api.yanmeiai.com/downloads/pangolin-macos-x64.zip',
|
||||
},
|
||||
} as const;
|
||||
+209
-171
@@ -1,200 +1,238 @@
|
||||
// strings.ts — 官网 i18n 字典(中 / EN)
|
||||
// strings.ts — 官网 i18n 字典(中 / EN / 日 / 韩 / 俄 / 西 六语)
|
||||
// 文案逐字沿用 design/ui_kits/website/site.js 的 I18N(已脱敏,符合 design/CLAUDE.md 铁律 13)。
|
||||
// 与原型不同的是:这里是「构建期」按路由整页单显(zh = /,en = /en/),不在运行时 JS 切换文本。
|
||||
// zh/en 原样保留;ja/ko/ru/es 依据 English 自然本地化(营销官网口吻,非机翻)。
|
||||
// 与原型不同的是:这里是「构建期」按路由整页单显,不在运行时 JS 切换文本。
|
||||
|
||||
export type Lang = 'zh' | 'en';
|
||||
export type Lang = 'zh' | 'en' | 'ja' | 'ko' | 'ru' | 'es';
|
||||
|
||||
// 每条 = [中文, English]
|
||||
export const STRINGS: Record<string, [string, string]> = {
|
||||
'ann.txt': ['新用户注册即享 7 天免费试用', 'New users get a 7-day free trial'],
|
||||
'ann.cta': ['立即注册 →', 'Sign up →'],
|
||||
// 每条 = { zh, en, ja, ko, ru, es }
|
||||
export const STRINGS: Record<string, Record<Lang, string>> = {
|
||||
'ann.txt': { zh: '新用户注册即享 7 天免费试用', en: 'New users get a 7-day free trial', ja: '新規登録で7日間の無料トライアル', ko: '신규 가입 시 7일 무료 체험', ru: 'Новым пользователям — 7 дней бесплатно', es: 'Los nuevos usuarios reciben 7 días de prueba gratis' },
|
||||
'ann.cta': { zh: '立即注册 →', en: 'Sign up →', ja: '今すぐ登録 →', ko: '지금 가입 →', ru: 'Зарегистрироваться →', es: 'Registrarse →' },
|
||||
|
||||
'su.ph': ['输入邮箱,免费注册', 'Enter email to sign up free'],
|
||||
'su.btn': ['免费注册', 'Sign up free'],
|
||||
'su.ok': ['验证码已发送!下载 App 并用同一邮箱继续完成注册。', 'Code sent! Download the app and continue with the same email.'],
|
||||
'su.hint': ['免费注册 · 无需付款 · 7 天免费试用', 'Free to join · no payment · 7-day free trial'],
|
||||
'su.dl': ['或直接下载 App', 'or just download the app'],
|
||||
'su.ph': { zh: '输入邮箱,免费注册', en: 'Enter email to sign up free', ja: 'メールアドレスで無料登録', ko: '이메일로 무료 가입', ru: 'Введите e-mail для бесплатной регистрации', es: 'Introduce tu correo para registrarte gratis' },
|
||||
'su.btn': { zh: '免费注册', en: 'Sign up free', ja: '無料で登録', ko: '무료 가입', ru: 'Зарегистрироваться бесплатно', es: 'Registrarse gratis' },
|
||||
'su.ok': { zh: '验证码已发送!下载 App 并用同一邮箱继续完成注册。', en: 'Code sent! Download the app and continue with the same email.', ja: '認証コードを送信しました!アプリをダウンロードし、同じメールアドレスで登録を続けてください。', ko: '인증 코드를 보냈습니다! 앱을 내려받아 같은 이메일로 가입을 이어가세요.', ru: 'Код отправлен! Скачайте приложение и продолжите с той же почтой.', es: '¡Código enviado! Descarga la app y continúa con el mismo correo.' },
|
||||
'su.hint': { zh: '免费注册 · 无需付款 · 7 天免费试用', en: 'Free to join · no payment · 7-day free trial', ja: '登録無料 · 支払い不要 · 7日間無料トライアル', ko: '가입 무료 · 결제 불필요 · 7일 무료 체험', ru: 'Регистрация бесплатна · без оплаты · 7 дней бесплатно', es: 'Registro gratis · sin pago · 7 días de prueba' },
|
||||
'su.dl': { zh: '或直接下载 App', en: 'or just download the app', ja: 'アプリを直接ダウンロード', ko: '또는 앱 바로 내려받기', ru: 'или просто скачайте приложение', es: 'o simplemente descarga la app' },
|
||||
|
||||
'why.eyebrow': ['免费注册', 'Sign up'],
|
||||
'why.h': ['注册一次,处处可用', 'One account, everywhere'],
|
||||
'why.sub': ['一个邮箱就能开始。注册免费,不需要任何付款信息。', 'All you need is an email. Free to join — no payment details, ever.'],
|
||||
'why.1t': ['7 天免费试用', '7-day free trial'],
|
||||
'why.1d': ['新用户注册即享 7 天免费使用,无需任何付款信息。', 'New accounts get 7 days of free, unrestricted use — no payment details.'],
|
||||
'why.2t': ['多端同步', 'Multi-device sync'],
|
||||
'why.2d': ['手机、平板、电脑共用一个账户,套餐与设置随身走。', 'Phone, tablet and desktop share one account — your plan follows you.'],
|
||||
'why.3t': ['无需付款信息', 'No payment details'],
|
||||
'why.3d': ['注册只要邮箱。想升级时,再通过外部渠道兑换激活码。', 'Just an email to join. Upgrade later with an activation code.'],
|
||||
'why.s1': ['填写邮箱', 'Enter email'],
|
||||
'why.s2': ['输入验证码', 'Verify code'],
|
||||
'why.s3': ['开始畅连', 'Start connecting'],
|
||||
'why.cta': ['立即免费注册', 'Sign up free'],
|
||||
'why.eyebrow': { zh: '免费注册', en: 'Sign up', ja: '無料登録', ko: '가입하기', ru: 'Регистрация', es: 'Registrarse' },
|
||||
'why.h': { zh: '注册一次,处处可用', en: 'One account, everywhere', ja: '一つのアカウントで、どこでも', ko: '하나의 계정으로 어디서나', ru: 'Один аккаунт — везде', es: 'Una cuenta, en todas partes' },
|
||||
'why.sub': { zh: '一个邮箱就能开始。注册免费,不需要任何付款信息。', en: 'All you need is an email. Free to join — no payment details, ever.', ja: '必要なのはメールアドレスだけ。登録は無料、支払い情報は一切不要です。', ko: '필요한 건 이메일 하나뿐. 가입은 무료이며 결제 정보도 필요 없습니다.', ru: 'Нужна лишь почта. Регистрация бесплатна — платёжные данные не требуются.', es: 'Solo necesitas un correo. Registro gratis, sin datos de pago nunca.' },
|
||||
'why.1t': { zh: '7 天免费试用', en: '7-day free trial', ja: '7日間無料トライアル', ko: '7일 무료 체험', ru: '7 дней бесплатно', es: '7 días de prueba gratis' },
|
||||
'why.1d': { zh: '新用户注册即享 7 天免费使用,无需任何付款信息。', en: 'New accounts get 7 days of free, unrestricted use — no payment details.', ja: '新規アカウントは7日間、制限なしで無料利用。支払い情報は不要です。', ko: '신규 계정은 7일간 제한 없이 무료로 이용, 결제 정보도 필요 없습니다.', ru: 'Новые аккаунты — 7 дней свободного использования без ограничений и без платёжных данных.', es: 'Las cuentas nuevas disfrutan 7 días de uso libre y sin límites, sin datos de pago.' },
|
||||
'why.2t': { zh: '多端同步', en: 'Multi-device sync', ja: 'マルチデバイス同期', ko: '여러 기기 동기화', ru: 'Синхронизация устройств', es: 'Sincronización multidispositivo' },
|
||||
'why.2d': { zh: '手机、平板、电脑共用一个账户,套餐与设置随身走。', en: 'Phone, tablet and desktop share one account — your plan follows you.', ja: 'スマホ・タブレット・PCで一つのアカウントを共有。プランも設定も一緒に持ち運べます。', ko: '휴대폰, 태블릿, PC가 하나의 계정을 공유해 요금제와 설정이 따라다닙니다.', ru: 'Телефон, планшет и компьютер — один аккаунт, а тариф всегда с вами.', es: 'Teléfono, tableta y ordenador comparten una cuenta; tu plan te acompaña.' },
|
||||
'why.3t': { zh: '无需付款信息', en: 'No payment details', ja: '支払い情報は不要', ko: '결제 정보 불필요', ru: 'Без платёжных данных', es: 'Sin datos de pago' },
|
||||
'why.3d': { zh: '注册只要邮箱。想升级时,再通过外部渠道兑换激活码。', en: 'Just an email to join. Upgrade later with an activation code.', ja: '登録はメールだけ。アップグレードは後からアクティベーションコードで。', ko: '가입은 이메일만. 업그레이드는 나중에 활성화 코드로.', ru: 'Для входа нужна только почта. Обновиться можно позже кодом активации.', es: 'Solo un correo para unirte. Mejora luego con un código de activación.' },
|
||||
'why.s1': { zh: '填写邮箱', en: 'Enter email', ja: 'メールを入力', ko: '이메일 입력', ru: 'Введите почту', es: 'Introduce el correo' },
|
||||
'why.s2': { zh: '输入验证码', en: 'Verify code', ja: '認証コードを入力', ko: '인증 코드 입력', ru: 'Введите код', es: 'Verifica el código' },
|
||||
'why.s3': { zh: '开始畅连', en: 'Start connecting', ja: '接続を開始', ko: '연결 시작', ru: 'Начните подключение', es: 'Empieza a conectar' },
|
||||
'why.cta': { zh: '立即免费注册', en: 'Sign up free', ja: '今すぐ無料登録', ko: '지금 무료 가입', ru: 'Зарегистрироваться бесплатно', es: 'Registrarse gratis' },
|
||||
|
||||
'cta.btn2': ['免费注册', 'Sign up free'],
|
||||
'cta.btn2': { zh: '免费注册', en: 'Sign up free', ja: '無料で登録', ko: '무료 가입', ru: 'Зарегистрироваться бесплатно', es: 'Registrarse gratis' },
|
||||
|
||||
'nav.product': ['产品', 'Product'],
|
||||
'nav.pricing': ['定价', 'Pricing'],
|
||||
'nav.download': ['下载', 'Download'],
|
||||
'nav.docs': ['文档', 'Docs'],
|
||||
'nav.blog': ['Blog', 'Blog'],
|
||||
'nav.login': ['登录', 'Log in'],
|
||||
'nav.get': ['立即下载', 'Get the app'],
|
||||
'nav.product': { zh: '产品', en: 'Product', ja: '製品', ko: '제품', ru: 'Продукт', es: 'Producto' },
|
||||
'nav.pricing': { zh: '定价', en: 'Pricing', ja: '料金', ko: '요금제', ru: 'Цены', es: 'Precios' },
|
||||
'nav.download': { zh: '下载', en: 'Download', ja: 'ダウンロード', ko: '다운로드', ru: 'Скачать', es: 'Descargar' },
|
||||
'nav.docs': { zh: '文档', en: 'Docs', ja: 'ドキュメント', ko: '문서', ru: 'Документация', es: 'Documentación' },
|
||||
'nav.blog': { zh: 'Blog', en: 'Blog', ja: 'ブログ', ko: '블로그', ru: 'Блог', es: 'Blog' },
|
||||
'nav.login': { zh: '登录', en: 'Log in', ja: 'ログイン', ko: '로그인', ru: 'Войти', es: 'Iniciar sesión' },
|
||||
'nav.center': { zh: '用户中心', en: 'Account', ja: 'アカウント', ko: '계정', ru: 'Личный кабинет', es: 'Mi cuenta' },
|
||||
'nav.get': { zh: '立即下载', en: 'Get the app', ja: 'アプリを入手', ko: '앱 받기', ru: 'Получить приложение', es: 'Obtener la app' },
|
||||
// 品牌字标:中文显「穿山甲」,其余语言统一显「Pangolin」(英文版 logo 本地化)。
|
||||
'nav.brand': { zh: '穿山甲', en: 'Pangolin', ja: 'Pangolin', ko: 'Pangolin', ru: 'Pangolin', es: 'Pangolin' },
|
||||
// 登录态用户下拉菜单项(3 项 × 6 语)。
|
||||
'menu.center': { zh: '进入用户中心', en: 'Open account center', ja: 'アカウントセンターへ', ko: '계정 센터 열기', ru: 'Личный кабинет', es: 'Ir a mi cuenta' },
|
||||
'menu.switch': { zh: '切换用户', en: 'Switch account', ja: 'アカウントを切替', ko: '계정 전환', ru: 'Сменить аккаунт', es: 'Cambiar de cuenta' },
|
||||
'menu.logout': { zh: '退出登录', en: 'Log out', ja: 'ログアウト', ko: '로그아웃', ru: 'Выйти', es: 'Cerrar sesión' },
|
||||
|
||||
'hero.eyebrow': ['极速 · 稳定 · 省心', 'Fast · Stable · Effortless'],
|
||||
'hero.h1': ['极速畅连,\n网络如丝顺滑', 'Faster, smoother,\neverywhere'],
|
||||
'hero.lede': ['轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。', 'A lightweight, friendly cross-platform network accelerator. One tap, smart routing, rock-solid.'],
|
||||
'hero.cta1': ['免费下载', 'Download free'],
|
||||
'hero.cta2': ['查看定价', 'See pricing'],
|
||||
'hero.t1': ['80+ 全球加速线路', '80+ global routes'],
|
||||
'hero.t2': ['严格无日志', 'Strict no-logs'],
|
||||
'hero.t3': ['端到端加密', 'End-to-end encrypted'],
|
||||
'orb.cap': ['已连接', 'CONNECTED'],
|
||||
'orb.down': ['下载', 'Down'],
|
||||
'orb.up': ['上传', 'Up'],
|
||||
'orb.node': ['香港 · 流媒体', 'Hong Kong'],
|
||||
'orb.nodesub': ['延迟 18ms · 已连接', '18ms · Connected'],
|
||||
'hero.eyebrow': { zh: '极速 · 稳定 · 省心', en: 'Fast · Stable · Effortless', ja: '高速 · 安定 · 快適', ko: '빠름 · 안정 · 간편', ru: 'Быстро · Стабильно · Без забот', es: 'Rápido · Estable · Sin complicaciones' },
|
||||
'hero.h1': { zh: '极速畅连,\n网络如丝顺滑', en: 'Faster, smoother,\neverywhere', ja: 'もっと速く、もっと滑らかに、\nどこでも', ko: '더 빠르고 더 매끄럽게,\n어디서나', ru: 'Быстрее, плавнее,\nвезде', es: 'Más rápido, más fluido,\nen todas partes' },
|
||||
'hero.lede': { zh: '轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。', en: 'A lightweight, friendly cross-platform network accelerator. One tap, smart routing, rock-solid.', ja: '軽快で使いやすい、クロスプラットフォームのネットワークアクセラレーター。ワンタップ接続、スマートルーティング、安定した通信。', ko: '가볍고 친근한 크로스플랫폼 네트워크 가속 앱. 원 탭 연결, 스마트 라우팅, 흔들림 없는 안정성.', ru: 'Лёгкий и удобный кроссплатформенный ускоритель сети. Одно касание, умная маршрутизация, надёжное соединение.', es: 'Un acelerador de red multiplataforma, ligero y fácil de usar. Un toque, enrutamiento inteligente, total estabilidad.' },
|
||||
'hero.cta1': { zh: '免费下载', en: 'Download free', ja: '無料ダウンロード', ko: '무료 다운로드', ru: 'Скачать бесплатно', es: 'Descargar gratis' },
|
||||
'hero.cta2': { zh: '查看定价', en: 'See pricing', ja: '料金を見る', ko: '요금 보기', ru: 'Посмотреть цены', es: 'Ver precios' },
|
||||
'hero.t1': { zh: '全球加速线路', en: 'Global routes', ja: 'グローバル回線', ko: '글로벌 라우트', ru: 'Глобальные маршруты', es: 'Rutas globales' },
|
||||
'hero.t2': { zh: '严格无日志', en: 'Strict no-logs', ja: '厳格なノーログ', ko: '엄격한 노로그', ru: 'Строго без логов', es: 'Sin registros, estricto' },
|
||||
'hero.t3': { zh: '端到端加密', en: 'End-to-end encrypted', ja: 'エンドツーエンド暗号化', ko: '엔드투엔드 암호화', ru: 'Сквозное шифрование', es: 'Cifrado de extremo a extremo' },
|
||||
'orb.cap': { zh: '已连接', en: 'CONNECTED', ja: '接続済み', ko: '연결됨', ru: 'ПОДКЛЮЧЕНО', es: 'CONECTADO' },
|
||||
'orb.down': { zh: '下载', en: 'Down', ja: '下り', ko: '다운', ru: 'Приём', es: 'Bajada' },
|
||||
'orb.up': { zh: '上传', en: 'Up', ja: '上り', ko: '업', ru: 'Отдача', es: 'Subida' },
|
||||
'orb.node': { zh: '香港 · 流媒体', en: 'Hong Kong', ja: '香港', ko: '홍콩', ru: 'Гонконг', es: 'Hong Kong' },
|
||||
'orb.nodesub': { zh: '延迟 18ms · 已连接', en: '18ms · Connected', ja: '18ms · 接続済み', ko: '18ms · 연결됨', ru: '18 мс · Подключено', es: '18 ms · Conectado' },
|
||||
|
||||
'strip.txt': ['已支持', 'Available on'],
|
||||
'strip.txt': { zh: '已支持', en: 'Available on', ja: '対応プラットフォーム', ko: '지원 플랫폼', ru: 'Доступно на', es: 'Disponible en' },
|
||||
|
||||
'feat.eyebrow': ['产品', 'Product'],
|
||||
'feat.h': ['为日常而生的连接体验', 'Built for everyday connection'],
|
||||
'feat.sub': ['没有复杂配置,没有技术门槛。打开就用,把稳定与隐私留给我们。', 'No complex setup, no jargon. Just open and go — we handle speed and privacy.'],
|
||||
'feat.1t': ['一键连接', 'One-tap connect'],
|
||||
'feat.1d': ['一颗按钮,智能挑选最快线路。无需手动配置,也能秒连。', 'One button picks the fastest route. No manual setup, instant connect.'],
|
||||
'feat.2t': ['智能分流', 'Smart routing'],
|
||||
'feat.2d': ['按规则智能分流,该加速的加速,本地服务直连,互不打扰。', 'Smart rule-based routing — accelerate what needs it, keep local traffic direct.'],
|
||||
'feat.3t': ['80+ 全球加速线路', '80+ global routes'],
|
||||
'feat.3d': ['覆盖港日新美欧韩等地,视频与游戏优化线路随心选。', 'Routes across HK / JP / SG / US / EU and more, tuned for video & gaming.'],
|
||||
'feat.4t': ['严格无日志', 'Strict no-logs'],
|
||||
'feat.4d': ['我们不记录你的浏览数据。隐私是底线,不是卖点。', 'We never log your browsing. Privacy is the baseline, not a feature.'],
|
||||
'feat.5t': ['Kill Switch', 'Kill Switch'],
|
||||
'feat.5d': ['一旦连接中断,立即阻断网络,杜绝真实 IP 泄露。', 'If the tunnel drops, traffic is blocked instantly — no IP leaks.'],
|
||||
'feat.6t': ['多端同步', 'Multi-device'],
|
||||
'feat.6d': ['一个账户,手机、平板、电脑同时在线,最多 5 台设备。', 'One account across phone, tablet and desktop — up to 5 devices.'],
|
||||
'feat.eyebrow': { zh: '产品', en: 'Product', ja: '製品', ko: '제품', ru: 'Продукт', es: 'Producto' },
|
||||
'feat.h': { zh: '为日常而生的连接体验', en: 'Built for everyday connection', ja: '毎日の接続のために', ko: '일상을 위한 연결 경험', ru: 'Создан для повседневных подключений', es: 'Diseñado para la conexión de cada día' },
|
||||
'feat.sub': { zh: '没有复杂配置,没有技术门槛。打开就用,把稳定与隐私留给我们。', en: 'No complex setup, no jargon. Just open and go — we handle speed and privacy.', ja: '複雑な設定も専門用語も不要。開いて使うだけ。速度とプライバシーはお任せください。', ko: '복잡한 설정도, 어려운 용어도 없습니다. 열면 바로 사용, 속도와 개인정보는 저희가 책임집니다.', ru: 'Никаких сложных настроек и жаргона. Просто откройте и пользуйтесь — скорость и приватность на нас.', es: 'Sin configuraciones complejas ni tecnicismos. Ábrelo y listo: la velocidad y la privacidad corren por nuestra cuenta.' },
|
||||
'feat.1t': { zh: '一键连接', en: 'One-tap connect', ja: 'ワンタップ接続', ko: '원 탭 연결', ru: 'Подключение одним касанием', es: 'Conexión con un toque' },
|
||||
'feat.1d': { zh: '一颗按钮,智能挑选最快线路。无需手动配置,也能秒连。', en: 'One button picks the fastest route. No manual setup, instant connect.', ja: 'ボタン一つで最速回線を自動選択。手動設定なしで瞬時に接続。', ko: '버튼 하나로 가장 빠른 라우트를 자동 선택. 수동 설정 없이 바로 연결.', ru: 'Одна кнопка выбирает самый быстрый маршрут. Без ручных настроек — мгновенное подключение.', es: 'Un botón elige la ruta más rápida. Sin ajustes manuales, conexión instantánea.' },
|
||||
'feat.2t': { zh: '智能分流', en: 'Smart routing', ja: 'スマートルーティング', ko: '스마트 라우팅', ru: 'Умная маршрутизация', es: 'Enrutamiento inteligente' },
|
||||
'feat.2d': { zh: '按规则智能分流,该加速的加速,本地服务直连,互不打扰。', en: 'Smart rule-based routing — accelerate what needs it, keep local traffic direct.', ja: 'ルールベースのスマートルーティングで、必要な通信は高速化、ローカル通信は直結。', ko: '규칙 기반 스마트 라우팅으로 필요한 트래픽은 가속하고 로컬 트래픽은 직접 연결.', ru: 'Умная маршрутизация по правилам: ускоряем нужное, локальный трафик идёт напрямую.', es: 'Enrutamiento inteligente por reglas: acelera lo que lo necesita y deja el tráfico local directo.' },
|
||||
'feat.3t': { zh: '全球加速线路', en: 'Global routes', ja: 'グローバル回線', ko: '글로벌 라우트', ru: 'Глобальные маршруты', es: 'Rutas globales' },
|
||||
'feat.3d': { zh: '覆盖多地优质线路,视频与游戏优化随心选。', en: 'Premium routes across regions, tuned for video & gaming.', ja: '各地の高品質回線を網羅、動画・ゲーム向けに最適化。', ko: '여러 지역의 프리미엄 라우트, 영상과 게임에 맞춰 최적화.', ru: 'Премиум-маршруты по регионам, настроенные для видео и игр.', es: 'Rutas premium en varias regiones, optimizadas para vídeo y juegos.' },
|
||||
'feat.4t': { zh: '严格无日志', en: 'Strict no-logs', ja: '厳格なノーログ', ko: '엄격한 노로그', ru: 'Строго без логов', es: 'Sin registros, estricto' },
|
||||
'feat.4d': { zh: '我们不记录你的浏览数据。隐私是底线,不是卖点。', en: 'We never log your browsing. Privacy is the baseline, not a feature.', ja: '閲覧データは一切記録しません。プライバシーは売り文句ではなく、当然の前提です。', ko: '고객의 브라우징 데이터를 절대 기록하지 않습니다. 개인정보 보호는 기본이지 특별 기능이 아닙니다.', ru: 'Мы не ведём журнал вашего трафика. Приватность — это база, а не опция.', es: 'Nunca registramos tu navegación. La privacidad es la base, no un extra.' },
|
||||
'feat.5t': { zh: 'Kill Switch', en: 'Kill Switch', ja: 'キルスイッチ', ko: '킬 스위치', ru: 'Kill Switch', es: 'Kill Switch' },
|
||||
'feat.5d': { zh: '一旦连接中断,立即阻断网络,杜绝真实 IP 泄露。', en: 'If the tunnel drops, traffic is blocked instantly — no IP leaks.', ja: '接続が切れた瞬間に通信を遮断し、実IPの漏洩を防ぎます。', ko: '터널이 끊기면 즉시 트래픽을 차단해 실제 IP 노출을 막습니다.', ru: 'Если соединение обрывается, трафик мгновенно блокируется — без утечек IP.', es: 'Si la conexión se cae, el tráfico se bloquea al instante: sin filtraciones de IP.' },
|
||||
'feat.6t': { zh: '多端同步', en: 'Multi-device', ja: 'マルチデバイス', ko: '멀티 디바이스', ru: 'Много устройств', es: 'Multidispositivo' },
|
||||
'feat.6d': { zh: '一个账户,手机、平板、电脑同时在线,最多 5 台设备。', en: 'One account across phone, tablet and desktop — up to 5 devices.', ja: '一つのアカウントでスマホ・タブレット・PCを同時に、最大5台まで。', ko: '하나의 계정으로 휴대폰, 태블릿, PC를 동시에, 최대 5대까지.', ru: 'Один аккаунт на телефоне, планшете и компьютере — до 5 устройств.', es: 'Una cuenta en teléfono, tableta y ordenador: hasta 5 dispositivos.' },
|
||||
|
||||
'price.eyebrow': ['定价', 'Pricing'],
|
||||
'price.h': ['简单透明的价格', 'Simple, honest pricing'],
|
||||
'price.sub': ['随时升级或取消。所有套餐均含核心加密与无日志承诺。', 'Upgrade or cancel anytime. Every plan includes core encryption and our no-logs promise.'],
|
||||
'price.monthly': ['按月', 'Monthly'],
|
||||
'price.yearly': ['按年', 'Yearly'],
|
||||
'price.save': ['省 20%', 'Save 20%'],
|
||||
'price.permo': ['/月', '/mo'],
|
||||
'price.free': ['免费版', 'Free'],
|
||||
'price.freedesc': ['适合轻度使用与尝鲜', 'For light, casual use'],
|
||||
'price.pro': ['专业版', 'Pro'],
|
||||
'price.prodesc': ['个人用户的最佳选择', 'Best for individuals'],
|
||||
'price.team': ['团队版', 'Team'],
|
||||
'price.teamdesc': ['小团队集中管理', 'For small teams'],
|
||||
'price.popular': ['最受欢迎', 'Most popular'],
|
||||
'price.cta_free': ['免费下载', 'Download free'],
|
||||
'price.cta_pro': ['获取激活码', 'Get a code'],
|
||||
'price.cta_team': ['获取激活码', 'Get a code'],
|
||||
'pf.free1': ['每日 10 分钟时长', '10 min per day'],
|
||||
'pf.free2': ['仅 1 个基础节点', '1 basic node only'],
|
||||
'pf.free3': ['使用前观看广告', 'Watch an ad to start'],
|
||||
'pf.free4': ['核心加密 · 无日志', 'Core encryption · no-logs'],
|
||||
'pf.pro1': ['80+ 全球加速线路', '80+ global routes'],
|
||||
'pf.pro2': ['无限流量 · 极速', 'Unlimited · top speed'],
|
||||
'pf.pro3': ['5 台设备同时在线', '5 devices at once'],
|
||||
'pf.pro4': ['流媒体 & P2P 优化', 'Streaming & P2P routes'],
|
||||
'pf.pro5': ['Kill Switch · 智能分流', 'Kill Switch · smart routing'],
|
||||
'pf.team1': ['专业版全部功能', 'Everything in Pro'],
|
||||
'pf.team2': ['10 个成员席位', '10 seats'],
|
||||
'pf.team3': ['集中计费与管理', 'Central billing & admin'],
|
||||
'pf.team4': ['优先客服', 'Priority support'],
|
||||
'price.eyebrow': { zh: '定价', en: 'Pricing', ja: '料金', ko: '요금제', ru: 'Цены', es: 'Precios' },
|
||||
'price.h': { zh: '简单透明的价格', en: 'Simple, honest pricing', ja: 'シンプルで正直な料金', ko: '단순하고 정직한 가격', ru: 'Простые и честные цены', es: 'Precios simples y honestos' },
|
||||
'price.sub': { zh: '随时升级或取消。所有套餐均含核心加密与无日志承诺。', en: 'Upgrade or cancel anytime. Every plan includes core encryption and our no-logs promise.', ja: 'いつでもアップグレード・解約可能。すべてのプランにコア暗号化とノーログの約束が含まれます。', ko: '언제든 업그레이드하거나 해지하세요. 모든 요금제에 핵심 암호화와 노로그 약속이 포함됩니다.', ru: 'Меняйте тариф или отменяйте в любой момент. В каждый план входит базовое шифрование и обещание не вести логи.', es: 'Mejora o cancela cuando quieras. Cada plan incluye cifrado esencial y nuestra promesa sin registros.' },
|
||||
'price.monthly': { zh: '按月', en: 'Monthly', ja: '月払い', ko: '월간', ru: 'Помесячно', es: 'Mensual' },
|
||||
'price.yearly': { zh: '按年', en: 'Yearly', ja: '年払い', ko: '연간', ru: 'Ежегодно', es: 'Anual' },
|
||||
'price.save': { zh: '省 20%', en: 'Save 20%', ja: '20%お得', ko: '20% 절약', ru: 'Экономия 20%', es: 'Ahorra 20%' },
|
||||
'price.permo': { zh: '/月', en: '/mo', ja: '/月', ko: '/월', ru: '/мес', es: '/mes' },
|
||||
'price.free': { zh: '免费版', en: 'Free', ja: '無料版', ko: '무료', ru: 'Бесплатный', es: 'Gratis' },
|
||||
'price.freedesc': { zh: '适合轻度使用与尝鲜', en: 'For light, casual use', ja: 'ライトなお試し利用に', ko: '가볍게 써보는 사용자에게', ru: 'Для лёгкого, эпизодического использования', es: 'Para un uso ligero y ocasional' },
|
||||
'price.pro': { zh: '专业版', en: 'Pro', ja: 'プロ版', ko: '프로', ru: 'Pro', es: 'Pro' },
|
||||
'price.prodesc': { zh: '个人用户的最佳选择', en: 'Best for individuals', ja: '個人ユーザーに最適', ko: '개인 사용자에게 최적', ru: 'Оптимально для одного пользователя', es: 'Ideal para uso individual' },
|
||||
'price.team': { zh: '团队版', en: 'Team', ja: 'チーム版', ko: '팀', ru: 'Команда', es: 'Equipo' },
|
||||
'price.teamdesc': { zh: '小团队集中管理', en: 'For small teams', ja: '小規模チームの一括管理に', ko: '소규모 팀 통합 관리', ru: 'Для небольших команд', es: 'Para equipos pequeños' },
|
||||
'price.popular': { zh: '最受欢迎', en: 'Most popular', ja: '一番人気', ko: '가장 인기', ru: 'Самый популярный', es: 'El más popular' },
|
||||
'price.cta_free': { zh: '免费下载', en: 'Download free', ja: '無料ダウンロード', ko: '무료 다운로드', ru: 'Скачать бесплатно', es: 'Descargar gratis' },
|
||||
'price.cta_pro': { zh: '获取激活码', en: 'Get a code', ja: 'コードを入手', ko: '코드 받기', ru: 'Получить код', es: 'Obtener un código' },
|
||||
'price.cta_team': { zh: '获取激活码', en: 'Get a code', ja: 'コードを入手', ko: '코드 받기', ru: 'Получить код', es: 'Obtener un código' },
|
||||
'pf.free1': { zh: '每日 10 分钟时长', en: '10 min per day', ja: '1日10分', ko: '하루 10분', ru: '10 минут в день', es: '10 min al día' },
|
||||
'pf.free2': { zh: '仅 1 个基础节点', en: '1 basic node only', ja: '基本ノード1つのみ', ko: '기본 노드 1개만', ru: 'Только 1 базовый узел', es: 'Solo 1 nodo básico' },
|
||||
'pf.free3': { zh: '使用前观看广告', en: 'Watch an ad to start', ja: '利用前に広告を視聴', ko: '사용 전 광고 시청', ru: 'Просмотр рекламы перед запуском', es: 'Ver un anuncio para empezar' },
|
||||
'pf.free4': { zh: '核心加密 · 无日志', en: 'Core encryption · no-logs', ja: 'コア暗号化 · ノーログ', ko: '핵심 암호화 · 노로그', ru: 'Базовое шифрование · без логов', es: 'Cifrado esencial · sin registros' },
|
||||
'pf.pro1': { zh: '全球加速线路', en: 'Global routes', ja: 'グローバル回線', ko: '글로벌 라우트', ru: 'Глобальные маршруты', es: 'Rutas globales' },
|
||||
'pf.pro2': { zh: '无限流量 · 极速', en: 'Unlimited · top speed', ja: '無制限 · 最高速度', ko: '무제한 · 최고 속도', ru: 'Без лимитов · максимальная скорость', es: 'Ilimitado · máxima velocidad' },
|
||||
'pf.pro3': { zh: '5 台设备同时在线', en: '5 devices at once', ja: '5台同時接続', ko: '동시 5대', ru: '5 устройств одновременно', es: '5 dispositivos a la vez' },
|
||||
'pf.pro4': { zh: '流媒体 & P2P 优化', en: 'Streaming & P2P routes', ja: 'ストリーミング & P2P最適化', ko: '스트리밍 & P2P 라우트', ru: 'Маршруты для стриминга и P2P', es: 'Rutas para streaming y P2P' },
|
||||
'pf.pro5': { zh: 'Kill Switch · 智能分流', en: 'Kill Switch · smart routing', ja: 'キルスイッチ · スマートルーティング', ko: '킬 스위치 · 스마트 라우팅', ru: 'Kill Switch · умная маршрутизация', es: 'Kill Switch · enrutamiento inteligente' },
|
||||
'pf.team1': { zh: '专业版全部功能', en: 'Everything in Pro', ja: 'プロ版の全機能', ko: '프로의 모든 기능', ru: 'Всё из тарифа Pro', es: 'Todo lo de Pro' },
|
||||
'pf.team2': { zh: '10 个成员席位', en: '10 seats', ja: '10メンバー席', ko: '멤버 10석', ru: '10 мест для участников', es: '10 plazas' },
|
||||
'pf.team3': { zh: '集中计费与管理', en: 'Central billing & admin', ja: '一括請求と管理', ko: '통합 결제 및 관리', ru: 'Единый биллинг и администрирование', es: 'Facturación y administración centralizadas' },
|
||||
'pf.team4': { zh: '优先客服', en: 'Priority support', ja: '優先サポート', ko: '우선 지원', ru: 'Приоритетная поддержка', es: 'Soporte prioritario' },
|
||||
|
||||
'pay.title': ['为什么网页上没有支付按钮?', 'Why no checkout on this page?'],
|
||||
'pay.body': ['出于风控与隐私考虑,穿山甲不在网页或 App 内直接收款。你可以通过以下任一渠道获取激活码,在客户端内兑换即可开通。', 'For risk and privacy reasons, Pangolin never takes payment in the web or app. Get an activation code through any channel below, then redeem it in the client.'],
|
||||
'pay.store': ['自助发卡商店', 'Self-serve store'],
|
||||
'pay.usdt': ['USDT (TRC20) · 最隐私', 'USDT (TRC20) · most private'],
|
||||
'pay.title': { zh: '为什么网页上没有支付按钮?', en: 'Why no checkout on this page?', ja: 'なぜこのページに決済ボタンがないのですか?', ko: '이 페이지에 결제 버튼이 없는 이유는?', ru: 'Почему на этой странице нет оплаты?', es: '¿Por qué no hay pago en esta página?' },
|
||||
'pay.body': { zh: '出于风控与隐私考虑,穿山甲不在网页或 App 内直接收款。你可以通过以下任一渠道获取激活码,在客户端内兑换即可开通。', en: 'For risk and privacy reasons, Pangolin never takes payment in the web or app. Get an activation code through any channel below, then redeem it in the client.', ja: 'リスク管理とプライバシーの観点から、Pangolinはウェブやアプリ内で直接決済を受け付けません。以下のいずれかの窓口でアクティベーションコードを入手し、クライアント内で引き換えてください。', ko: '리스크 관리와 개인정보 보호를 위해 Pangolin은 웹이나 앱에서 직접 결제를 받지 않습니다. 아래 채널에서 활성화 코드를 받아 클라이언트에서 등록하세요.', ru: 'В целях безопасности и приватности Pangolin не принимает оплату в вебе или приложении. Получите код активации по любому из каналов ниже и активируйте его в клиенте.', es: 'Por motivos de seguridad y privacidad, Pangolin nunca cobra en la web ni en la app. Consigue un código de activación por cualquiera de estos canales y canjéalo en el cliente.' },
|
||||
'pay.store': { zh: '自助发卡商店', en: 'Self-serve store', ja: 'セルフサービスストア', ko: '셀프 발급 스토어', ru: 'Магазин самообслуживания', es: 'Tienda de autoservicio' },
|
||||
'pay.usdt': { zh: 'USDT (TRC20) · 最隐私', en: 'USDT (TRC20) · most private', ja: 'USDT (TRC20) · 最もプライベート', ko: 'USDT (TRC20) · 가장 프라이빗', ru: 'USDT (TRC20) · максимум приватности', es: 'USDT (TRC20) · lo más privado' },
|
||||
|
||||
'cmp.h': ['功能对比', 'Compare plans'],
|
||||
'cmp.feature': ['功能', 'Feature'],
|
||||
'cmp.locations': ['可用线路', 'Routes'],
|
||||
'cmp.data': ['流量', 'Data'],
|
||||
'cmp.time': ['每日时长', 'Daily time'],
|
||||
'cmp.time_free': ['10 分钟/天(试用期不限)', '10 min/day (trial: unlimited)'],
|
||||
'cmp.devices': ['设备数', 'Devices'],
|
||||
'cmp.speed': ['极速线路', 'Top-speed routes'],
|
||||
'cmp.stream': ['视频优化', 'Video routes'],
|
||||
'cmp.kill': ['Kill Switch', 'Kill Switch'],
|
||||
'cmp.support': ['客服', 'Support'],
|
||||
'cmp.unlimited': ['无限', 'Unlimited'],
|
||||
'cmp.daily': ['不限', 'Unlimited'],
|
||||
'cmp.basic': ['标准', 'Standard'],
|
||||
'cmp.priority': ['优先', 'Priority'],
|
||||
'cmp.h': { zh: '功能对比', en: 'Compare plans', ja: 'プラン比較', ko: '요금제 비교', ru: 'Сравнение тарифов', es: 'Comparar planes' },
|
||||
'cmp.feature': { zh: '功能', en: 'Feature', ja: '機能', ko: '기능', ru: 'Функция', es: 'Función' },
|
||||
'cmp.locations': { zh: '可用线路', en: 'Routes', ja: '利用可能な回線', ko: '이용 가능한 라우트', ru: 'Маршруты', es: 'Rutas' },
|
||||
'cmp.data': { zh: '流量', en: 'Data', ja: 'データ量', ko: '데이터', ru: 'Трафик', es: 'Datos' },
|
||||
'cmp.time': { zh: '每日时长', en: 'Daily time', ja: '1日の利用時間', ko: '하루 사용 시간', ru: 'Время в день', es: 'Tiempo diario' },
|
||||
'cmp.time_free': { zh: '10 分钟/天(试用期不限)', en: '10 min/day (trial: unlimited)', ja: '10分/日(トライアル中は無制限)', ko: '10분/일 (체험 중 무제한)', ru: '10 мин/день (в пробный период — без лимита)', es: '10 min/día (prueba: ilimitado)' },
|
||||
'cmp.devices': { zh: '设备数', en: 'Devices', ja: 'デバイス数', ko: '기기 수', ru: 'Устройства', es: 'Dispositivos' },
|
||||
'cmp.speed': { zh: '极速线路', en: 'Top-speed routes', ja: '最高速回線', ko: '최고 속도 라우트', ru: 'Скоростные маршруты', es: 'Rutas de máxima velocidad' },
|
||||
'cmp.stream': { zh: '视频优化', en: 'Video routes', ja: '動画向け回線', ko: '영상 라우트', ru: 'Маршруты для видео', es: 'Rutas para vídeo' },
|
||||
'cmp.kill': { zh: 'Kill Switch', en: 'Kill Switch', ja: 'キルスイッチ', ko: '킬 스위치', ru: 'Kill Switch', es: 'Kill Switch' },
|
||||
'cmp.support': { zh: '客服', en: 'Support', ja: 'サポート', ko: '고객 지원', ru: 'Поддержка', es: 'Soporte' },
|
||||
'cmp.unlimited': { zh: '无限', en: 'Unlimited', ja: '無制限', ko: '무제한', ru: 'Без лимита', es: 'Ilimitado' },
|
||||
'cmp.loc_all': { zh: '全球', en: 'Global', ja: 'グローバル', ko: '글로벌', ru: 'Глобально', es: 'Global' },
|
||||
'cmp.daily': { zh: '不限', en: 'Unlimited', ja: '無制限', ko: '무제한', ru: 'Без лимита', es: 'Ilimitado' },
|
||||
'cmp.basic': { zh: '标准', en: 'Standard', ja: '標準', ko: '표준', ru: 'Стандарт', es: 'Estándar' },
|
||||
'cmp.priority': { zh: '优先', en: 'Priority', ja: '優先', ko: '우선', ru: 'Приоритет', es: 'Prioritario' },
|
||||
|
||||
'dl.eyebrow': ['下载', 'Download'],
|
||||
'dl.h': ['全平台,随处可用', 'Every platform, everywhere'],
|
||||
'dl.sub': ['一个账户,所有设备同步。下载即用,无需配置。', 'One account syncs every device. Download and go.'],
|
||||
'dl.get': ['下载', 'Download'],
|
||||
'dl.eyebrow': { zh: '下载', en: 'Download', ja: 'ダウンロード', ko: '다운로드', ru: 'Скачать', es: 'Descargar' },
|
||||
'dl.h': { zh: '全平台,随处可用', en: 'Every platform, everywhere', ja: 'すべてのプラットフォーム、どこでも', ko: '모든 플랫폼, 어디서나', ru: 'Все платформы, везде', es: 'Todas las plataformas, en todas partes' },
|
||||
'dl.sub': { zh: '一个账户,所有设备同步。下载即用,无需配置。', en: 'One account syncs every device. Download and go.', ja: '一つのアカウントで全デバイスを同期。ダウンロードしてすぐ使えます。', ko: '하나의 계정으로 모든 기기를 동기화. 내려받아 바로 사용.', ru: 'Один аккаунт синхронизирует все устройства. Скачал — и в путь.', es: 'Una cuenta sincroniza todos tus dispositivos. Descarga y listo.' },
|
||||
'dl.get': { zh: '下载', en: 'Download', ja: 'ダウンロード', ko: '다운로드', ru: 'Скачать', es: 'Descargar' },
|
||||
'dl.soon': { zh: '敬请期待', en: 'Coming soon', ja: '近日公開', ko: '출시 예정', ru: 'Скоро', es: 'Próximamente' },
|
||||
|
||||
'docs.eyebrow': ['文档', 'Docs'],
|
||||
'docs.h': ['需要帮助?都在这里', 'Need help? It’s all here'],
|
||||
'docs.sub': ['从快速上手到协议细节,清晰直接。', 'From quickstart to protocol details — clear and direct.'],
|
||||
'docs.1t': ['快速开始', 'Quickstart'],
|
||||
'docs.1d': ['三分钟完成注册、下载与首次连接。', 'Sign up, download and connect in three minutes.'],
|
||||
'docs.2t': ['常见问题', 'FAQ'],
|
||||
'docs.2d': ['连接、计费、设备与兑换码的常见疑问。', 'Connection, billing, devices and redeem codes.'],
|
||||
'docs.3t': ['协议与安全', 'Protocol & security'],
|
||||
'docs.3d': ['WireGuard、加密方式与无日志架构说明。', 'WireGuard, encryption and our no-logs architecture.'],
|
||||
'docs.4t': ['隐私政策', 'Privacy policy'],
|
||||
'docs.4d': ['我们收集什么、不收集什么,一目了然。', 'Exactly what we collect — and what we never do.'],
|
||||
'docs.read': ['阅读', 'Read'],
|
||||
'docs.eyebrow': { zh: '文档', en: 'Docs', ja: 'ドキュメント', ko: '문서', ru: 'Документация', es: 'Documentación' },
|
||||
'docs.h': { zh: '需要帮助?都在这里', en: 'Need help? It’s all here', ja: 'お困りですか?すべてここに', ko: '도움이 필요하세요? 모두 여기에', ru: 'Нужна помощь? Всё здесь', es: '¿Necesitas ayuda? Todo está aquí' },
|
||||
'docs.sub': { zh: '从快速上手到协议细节,清晰直接。', en: 'From quickstart to protocol details — clear and direct.', ja: 'クイックスタートからプロトコルの詳細まで、明快に。', ko: '빠른 시작부터 프로토콜 세부까지, 명확하고 간결하게.', ru: 'От быстрого старта до деталей протокола — ясно и по делу.', es: 'Desde el inicio rápido hasta los detalles del protocolo, claro y directo.' },
|
||||
'docs.1t': { zh: '快速开始', en: 'Quickstart', ja: 'クイックスタート', ko: '빠른 시작', ru: 'Быстрый старт', es: 'Inicio rápido' },
|
||||
'docs.1d': { zh: '三分钟完成注册、下载与首次连接。', en: 'Sign up, download and connect in three minutes.', ja: '3分で登録・ダウンロード・初回接続まで完了。', ko: '3분 만에 가입, 다운로드, 첫 연결까지.', ru: 'Регистрация, загрузка и первое подключение за три минуты.', es: 'Regístrate, descarga y conéctate en tres minutos.' },
|
||||
'docs.2t': { zh: '常见问题', en: 'FAQ', ja: 'よくある質問', ko: '자주 묻는 질문', ru: 'Вопросы и ответы', es: 'Preguntas frecuentes' },
|
||||
'docs.2d': { zh: '连接、计费、设备与兑换码的常见疑问。', en: 'Connection, billing, devices and redeem codes.', ja: '接続・請求・デバイス・引き換えコードのよくある疑問。', ko: '연결, 결제, 기기, 등록 코드에 대한 궁금증.', ru: 'Подключение, оплата, устройства и коды активации.', es: 'Conexión, facturación, dispositivos y códigos de canje.' },
|
||||
'docs.3t': { zh: '协议与安全', en: 'Protocol & security', ja: 'プロトコルとセキュリティ', ko: '프로토콜 & 보안', ru: 'Протокол и безопасность', es: 'Protocolo y seguridad' },
|
||||
'docs.3d': { zh: 'sing-box + REALITY、加密方式与无日志架构说明。', en: 'sing-box + REALITY, encryption and our no-logs architecture.', ja: 'sing-box + REALITY、暗号化方式、ノーログ設計の解説。', ko: 'sing-box + REALITY, 암호화 방식, 노로그 아키텍처 설명.', ru: 'sing-box + REALITY, шифрование и наша архитектура без логов.', es: 'sing-box + REALITY, cifrado y nuestra arquitectura sin registros.' },
|
||||
'docs.4t': { zh: '隐私政策', en: 'Privacy policy', ja: 'プライバシーポリシー', ko: '개인정보 처리방침', ru: 'Политика конфиденциальности', es: 'Política de privacidad' },
|
||||
'docs.4d': { zh: '我们收集什么、不收集什么,一目了然。', en: 'Exactly what we collect — and what we never do.', ja: '収集するもの・しないものを明確に。', ko: '무엇을 수집하고 무엇을 수집하지 않는지 한눈에.', ru: 'Что мы собираем — и чего не собираем никогда.', es: 'Exactamente qué recopilamos y qué nunca hacemos.' },
|
||||
'docs.read': { zh: '阅读', en: 'Read', ja: '読む', ko: '읽기', ru: 'Читать', es: 'Leer' },
|
||||
|
||||
'blog.eyebrow': ['Blog', 'Blog'],
|
||||
'blog.h': ['来自团队的最新动态', 'Latest from the team'],
|
||||
'blog.sub': ['产品更新、安全科普与节点公告。', 'Product updates, security explainers and node news.'],
|
||||
'blog.1tag': ['产品更新', 'Product'],
|
||||
'blog.1t': ['v2.4:更快的智能分流与全新统计页', 'v2.4: faster smart routing & a new stats tab'],
|
||||
'blog.1d': ['本次更新重写了分流引擎,连接建立速度提升约 40%。', 'We rewrote the routing engine — connections are ~40% faster.'],
|
||||
'blog.2tag': ['安全', 'Security'],
|
||||
'blog.2t': ['无日志到底意味着什么?', 'What “no-logs” really means'],
|
||||
'blog.2d': ['我们逐条拆解收集与不收集的数据,以及背后的架构。', 'A line-by-line look at what we keep — and what we never touch.'],
|
||||
'blog.3tag': ['公告', 'News'],
|
||||
'blog.3t': ['新增首尔与法兰克福高速节点', 'New high-speed nodes: Seoul & Frankfurt'],
|
||||
'blog.3d': ['两条新线路已上线,为东北亚与欧洲用户带来更低延迟。', 'Two new routes are live, cutting latency for NE Asia and Europe.'],
|
||||
'blog.readmore': ['阅读全文', 'Read more'],
|
||||
'blog.eyebrow': { zh: 'Blog', en: 'Blog', ja: 'ブログ', ko: '블로그', ru: 'Блог', es: 'Blog' },
|
||||
'blog.h': { zh: '来自团队的最新动态', en: 'Latest from the team', ja: 'チームからの最新情報', ko: '팀의 최신 소식', ru: 'Свежие новости команды', es: 'Lo último del equipo' },
|
||||
'blog.sub': { zh: '产品更新、安全科普与节点公告。', en: 'Product updates, security explainers and node news.', ja: '製品アップデート、セキュリティ解説、ノード情報。', ko: '제품 업데이트, 보안 해설, 노드 소식.', ru: 'Обновления продукта, разборы по безопасности и новости об узлах.', es: 'Novedades del producto, explicaciones de seguridad y avisos de nodos.' },
|
||||
'blog.1tag': { zh: '产品更新', en: 'Product', ja: '製品', ko: '제품', ru: 'Продукт', es: 'Producto' },
|
||||
'blog.1t': { zh: 'v2.4:更快的智能分流与全新统计页', en: 'v2.4: faster smart routing & a new stats tab', ja: 'v2.4:より速いスマートルーティングと新しい統計タブ', ko: 'v2.4: 더 빨라진 스마트 라우팅과 새 통계 탭', ru: 'v2.4: быстрее умная маршрутизация и новая вкладка статистики', es: 'v2.4: enrutamiento inteligente más rápido y nueva pestaña de estadísticas' },
|
||||
'blog.1d': { zh: '本次更新重写了分流引擎,连接建立速度提升约 40%。', en: 'We rewrote the routing engine — connections are ~40% faster.', ja: 'ルーティングエンジンを刷新し、接続の確立が約40%高速化しました。', ko: '라우팅 엔진을 새로 작성해 연결 속도가 약 40% 빨라졌습니다.', ru: 'Мы переписали движок маршрутизации — соединения устанавливаются на ~40% быстрее.', es: 'Reescribimos el motor de enrutamiento: las conexiones son ~40% más rápidas.' },
|
||||
'blog.2tag': { zh: '安全', en: 'Security', ja: 'セキュリティ', ko: '보안', ru: 'Безопасность', es: 'Seguridad' },
|
||||
'blog.2t': { zh: '无日志到底意味着什么?', en: 'What “no-logs” really means', ja: '「ノーログ」とは本当はどういう意味か', ko: '“노로그”가 정말 의미하는 것', ru: 'Что на самом деле значит «без логов»', es: 'Qué significa realmente “sin registros”' },
|
||||
'blog.2d': { zh: '我们逐条拆解收集与不收集的数据,以及背后的架构。', en: 'A line-by-line look at what we keep — and what we never touch.', ja: '保存するデータとしないデータを一つずつ、その背後の設計とともに解説。', ko: '저장하는 데이터와 절대 건드리지 않는 데이터를 하나하나 살펴봅니다.', ru: 'Построчный разбор того, что мы храним — и чего не касаемся никогда.', es: 'Un repaso punto por punto de lo que guardamos y lo que nunca tocamos.' },
|
||||
'blog.3tag': { zh: '公告', en: 'News', ja: 'お知らせ', ko: '소식', ru: 'Новости', es: 'Novedades' },
|
||||
'blog.3t': { zh: '新增首尔与法兰克福高速节点', en: 'New high-speed nodes: Seoul & Frankfurt', ja: '高速ノードを新設:ソウルとフランクフルト', ko: '새 고속 노드: 서울과 프랑크푸르트', ru: 'Новые скоростные узлы: Сеул и Франкфурт', es: 'Nuevos nodos de alta velocidad: Seúl y Fráncfort' },
|
||||
'blog.3d': { zh: '两条新线路已上线,为东北亚与欧洲用户带来更低延迟。', en: 'Two new routes are live, cutting latency for NE Asia and Europe.', ja: '2つの新回線が稼働開始、東北アジアと欧州のユーザーに低遅延を。', ko: '두 개의 새 라우트가 가동되어 동북아와 유럽 사용자의 지연이 줄었습니다.', ru: 'Запущены два новых маршрута, снижающих задержку для Северо-Восточной Азии и Европы.', es: 'Dos rutas nuevas ya activas reducen la latencia para el noreste de Asia y Europa.' },
|
||||
'blog.readmore': { zh: '阅读全文', en: 'Read more', ja: '続きを読む', ko: '더 읽기', ru: 'Читать далее', es: 'Leer más' },
|
||||
|
||||
'cta.h': ['准备好体验极速畅连了吗?', 'Ready for a faster connection?'],
|
||||
'cta.p': ['免费下载,数分钟内开始你的第一次极速连接。', 'Download free and get connected in minutes.'],
|
||||
'cta.btn': ['免费下载', 'Download free'],
|
||||
'cta.h': { zh: '准备好体验极速畅连了吗?', en: 'Ready for a faster connection?', ja: 'より速い接続を体験する準備はできましたか?', ko: '더 빠른 연결을 경험할 준비가 되셨나요?', ru: 'Готовы к более быстрому соединению?', es: '¿Listo para una conexión más rápida?' },
|
||||
'cta.p': { zh: '免费下载,数分钟内开始你的第一次极速连接。', en: 'Download free and get connected in minutes.', ja: '無料でダウンロードして、数分で最初の高速接続を。', ko: '무료로 내려받아 몇 분 안에 연결하세요.', ru: 'Скачайте бесплатно и подключитесь за считанные минуты.', es: 'Descarga gratis y conéctate en minutos.' },
|
||||
'cta.btn': { zh: '免费下载', en: 'Download free', ja: '無料ダウンロード', ko: '무료 다운로드', ru: 'Скачать бесплатно', es: 'Descargar gratis' },
|
||||
|
||||
'ft.tag': ['极简、轻量、亲和的跨平台网络加速应用。守护你的每一次连接。', 'A minimal, friendly cross-platform network accelerator. Protecting every connection.'],
|
||||
'ft.product': ['产品', 'Product'],
|
||||
'ft.resources': ['资源', 'Resources'],
|
||||
'ft.contact': ['联系与渠道', 'Contact'],
|
||||
'ft.features': ['功能', 'Features'],
|
||||
'ft.pricing': ['定价', 'Pricing'],
|
||||
'ft.download': ['下载', 'Download'],
|
||||
'ft.docs': ['文档', 'Docs'],
|
||||
'ft.blog': ['Blog', 'Blog'],
|
||||
'ft.faq': ['常见问题', 'FAQ'],
|
||||
'ft.privacy': ['隐私政策', 'Privacy'],
|
||||
'ft.status': ['服务状态', 'Status'],
|
||||
'ft.hours': ['服务时间 每日 9:00–24:00 (GMT+8)', 'Hours: Daily 9:00–24:00 (GMT+8)'],
|
||||
'ft.copy': ['© 2026 穿山甲 · Pangolin', '© 2026 Pangolin'],
|
||||
'ft.madenote': ['App 内不支持直接支付 · 资金流全走外部渠道', 'No in-app payment · all purchases via external channels'],
|
||||
'ft.tag': { zh: '极简、轻量、亲和的跨平台网络加速应用。守护你的每一次连接。', en: 'A minimal, friendly cross-platform network accelerator. Protecting every connection.', ja: 'ミニマルで軽快、使いやすいクロスプラットフォームのネットワークアクセラレーター。すべての接続を守ります。', ko: '미니멀하고 가벼우며 친근한 크로스플랫폼 네트워크 가속 앱. 모든 연결을 지킵니다.', ru: 'Минималистичный, лёгкий и удобный кроссплатформенный ускоритель сети. Защищает каждое соединение.', es: 'Un acelerador de red multiplataforma minimalista, ligero y amable. Protegiendo cada conexión.' },
|
||||
'ft.product': { zh: '产品', en: 'Product', ja: '製品', ko: '제품', ru: 'Продукт', es: 'Producto' },
|
||||
'ft.resources': { zh: '资源', en: 'Resources', ja: 'リソース', ko: '리소스', ru: 'Ресурсы', es: 'Recursos' },
|
||||
'ft.contact': { zh: '联系与渠道', en: 'Contact', ja: 'お問い合わせ', ko: '문의 및 채널', ru: 'Контакты', es: 'Contacto' },
|
||||
'ft.features': { zh: '功能', en: 'Features', ja: '機能', ko: '기능', ru: 'Возможности', es: 'Funciones' },
|
||||
'ft.pricing': { zh: '定价', en: 'Pricing', ja: '料金', ko: '요금제', ru: 'Цены', es: 'Precios' },
|
||||
'ft.download': { zh: '下载', en: 'Download', ja: 'ダウンロード', ko: '다운로드', ru: 'Скачать', es: 'Descargar' },
|
||||
'ft.docs': { zh: '文档', en: 'Docs', ja: 'ドキュメント', ko: '문서', ru: 'Документация', es: 'Documentación' },
|
||||
'ft.blog': { zh: 'Blog', en: 'Blog', ja: 'ブログ', ko: '블로그', ru: 'Блог', es: 'Blog' },
|
||||
'ft.faq': { zh: '常见问题', en: 'FAQ', ja: 'よくある質問', ko: '자주 묻는 질문', ru: 'Вопросы и ответы', es: 'Preguntas frecuentes' },
|
||||
'ft.privacy': { zh: '隐私政策', en: 'Privacy', ja: 'プライバシー', ko: '개인정보', ru: 'Конфиденциальность', es: 'Privacidad' },
|
||||
'ft.status': { zh: '服务状态', en: 'Status', ja: 'サービス状況', ko: '서비스 상태', ru: 'Статус', es: 'Estado' },
|
||||
'ft.hours': { zh: '服务时间 每日 9:00–24:00 (GMT+8)', en: 'Hours: Daily 9:00–24:00 (GMT+8)', ja: '営業時間 毎日 9:00–24:00 (GMT+8)', ko: '운영 시간 매일 9:00–24:00 (GMT+8)', ru: 'Часы работы: ежедневно 9:00–24:00 (GMT+8)', es: 'Horario: todos los días 9:00–24:00 (GMT+8)' },
|
||||
'ft.copy': { zh: '© 2026 穿山甲 · Pangolin', en: '© 2026 Pangolin', ja: '© 2026 Pangolin', ko: '© 2026 Pangolin', ru: '© 2026 Pangolin', es: '© 2026 Pangolin' },
|
||||
'ft.madenote': { zh: 'App 内不支持直接支付 · 资金流全走外部渠道', en: 'No in-app payment · all purchases via external channels', ja: 'アプリ内決済なし · すべての購入は外部窓口で', ko: '앱 내 결제 없음 · 모든 구매는 외부 채널로', ru: 'Без оплаты в приложении · все покупки через внешние каналы', es: 'Sin pago en la app · todas las compras por canales externos' },
|
||||
|
||||
// 页面 <title> / meta(zh / en)
|
||||
'meta.title': ['穿山甲 · Pangolin — 极速 · 稳定 · 省心', 'Pangolin — Fast · Stable · Effortless'],
|
||||
'meta.desc': ['轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。', 'A lightweight, friendly cross-platform network accelerator. One tap, smart routing, rock-solid.'],
|
||||
// 页面 <title> / meta(六语)
|
||||
'meta.title': { zh: '穿山甲 · Pangolin — 极速 · 稳定 · 省心', en: 'Pangolin — Fast · Stable · Effortless', ja: 'Pangolin — 高速 · 安定 · 快適', ko: 'Pangolin — 빠름 · 안정 · 간편', ru: 'Pangolin — Быстро · Стабильно · Без забот', es: 'Pangolin — Rápido · Estable · Sin complicaciones' },
|
||||
'meta.desc': { zh: '轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。', en: 'A lightweight, friendly cross-platform network accelerator. One tap, smart routing, rock-solid.', ja: '軽快で使いやすい、クロスプラットフォームのネットワークアクセラレーター。ワンタップ接続、スマートルーティング、安定した通信。', ko: '가볍고 친근한 크로스플랫폼 네트워크 가속 앱. 원 탭 연결, 스마트 라우팅, 흔들림 없는 안정성.', ru: 'Лёгкий и удобный кроссплатформенный ускоритель сети. Одно касание, умная маршрутизация, надёжное соединение.', es: 'Un acelerador de red multiplataforma, ligero y fácil de usar. Un toque, enrutamiento inteligente, total estabilidad.' },
|
||||
};
|
||||
|
||||
export type T = (key: string) => string;
|
||||
|
||||
/** 返回当前语言的取词函数;缺键回退到中文,再回退到键名本身(便于发现遗漏)。 */
|
||||
/** 返回当前语言的取词函数;缺键回退英文,再回退键名本身(便于发现遗漏)。 */
|
||||
export function createT(lang: Lang): T {
|
||||
const idx = lang === 'zh' ? 0 : 1;
|
||||
return (key: string) => {
|
||||
const pair = STRINGS[key];
|
||||
if (!pair) return key;
|
||||
return pair[idx] ?? pair[0] ?? key;
|
||||
const m = STRINGS[key];
|
||||
if (!m) return key;
|
||||
return m[lang] ?? m.en ?? key;
|
||||
};
|
||||
}
|
||||
|
||||
/** 价格表(月 / 年),与 ui_kits/website/site.js 的 PRICES 同步,对齐 design/CLAUDE.md §7。 */
|
||||
export const PRICES: Record<'free' | 'pro' | 'team', [string, string]> = {
|
||||
free: ['¥0', '¥0'],
|
||||
pro: ['¥25', '¥20'],
|
||||
team: ['¥99', '¥79'],
|
||||
/** 价格表(月 / 年·按月),按语言分币种:zh=人民币、其余=美元。年付约 8 折。
|
||||
* 对齐 design/CLAUDE.md §7;收款走客户端内兑换激活码,网页价仅为展示。
|
||||
* ja/ko/ru/es 暂复用 en 的美元价,币种本地化为后续单独工作。 */
|
||||
export const PRICES: Record<Lang, Record<'free' | 'pro' | 'team', [string, string]>> = {
|
||||
zh: {
|
||||
free: ['¥0', '¥0'],
|
||||
pro: ['¥25', '¥20'],
|
||||
team: ['¥99', '¥79'],
|
||||
},
|
||||
en: {
|
||||
free: ['$0', '$0'],
|
||||
pro: ['$3.99', '$3.19'],
|
||||
team: ['$13.99', '$11.19'],
|
||||
},
|
||||
ja: {
|
||||
free: ['$0', '$0'],
|
||||
pro: ['$3.99', '$3.19'],
|
||||
team: ['$13.99', '$11.19'],
|
||||
},
|
||||
ko: {
|
||||
free: ['$0', '$0'],
|
||||
pro: ['$3.99', '$3.19'],
|
||||
team: ['$13.99', '$11.19'],
|
||||
},
|
||||
ru: {
|
||||
free: ['$0', '$0'],
|
||||
pro: ['$3.99', '$3.19'],
|
||||
team: ['$13.99', '$11.19'],
|
||||
},
|
||||
es: {
|
||||
free: ['$0', '$0'],
|
||||
pro: ['$3.99', '$3.19'],
|
||||
team: ['$13.99', '$11.19'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
/**
|
||||
* Doc.astro — 文档正文页布局(/docs/* 与 /zh/docs/*)。
|
||||
* 复用官网 Header/Footer 与全站样式(tokens.gen / website / site-extra),
|
||||
* 中间是 .doc-page > .doc-article 可读正文容器。构建期单显一种语言。
|
||||
*/
|
||||
import '@fontsource/sora/500.css';
|
||||
import '@fontsource/sora/600.css';
|
||||
import '@fontsource/sora/700.css';
|
||||
import '@fontsource/manrope/400.css';
|
||||
import '@fontsource/manrope/500.css';
|
||||
import '@fontsource/manrope/600.css';
|
||||
import '@fontsource/manrope/700.css';
|
||||
import '@fontsource/noto-sans-sc/400.css';
|
||||
import '@fontsource/noto-sans-sc/500.css';
|
||||
import '@fontsource/noto-sans-sc/700.css';
|
||||
import '@fontsource/jetbrains-mono/400.css';
|
||||
import '@fontsource/jetbrains-mono/500.css';
|
||||
|
||||
import '../styles/tokens.gen.css';
|
||||
import '../styles/website.css';
|
||||
import '../styles/site-extra.css';
|
||||
|
||||
import { createT, type Lang } from '../i18n/strings';
|
||||
import Header from '../components/Header.jsx';
|
||||
import Footer from '../components/Footer.astro';
|
||||
|
||||
interface Props { lang: Lang; title: string; desc?: string }
|
||||
const { lang, title, desc } = Astro.props;
|
||||
const t = createT(lang);
|
||||
|
||||
const HTML_LANG: Record<Lang, string> = { zh: 'zh-CN', en: 'en', ja: 'ja', ko: 'ko', ru: 'ru', es: 'es' };
|
||||
|
||||
const headerT = {
|
||||
product: t('nav.product'),
|
||||
pricing: t('nav.pricing'),
|
||||
download: t('nav.download'),
|
||||
docs: t('nav.docs'),
|
||||
blog: t('nav.blog'),
|
||||
login: t('nav.login'),
|
||||
center: t('nav.center'),
|
||||
brand: t('nav.brand'),
|
||||
mcenter: t('menu.center'),
|
||||
mswitch: t('menu.switch'),
|
||||
mlogout: t('menu.logout'),
|
||||
get: t('nav.get'),
|
||||
suBtn: t('su.btn'),
|
||||
};
|
||||
|
||||
// 导航锚点回主页(文档页无同页锚点):把 #x 改为主页前缀。
|
||||
const home = lang === 'zh' ? '/zh/' : '/';
|
||||
const backHref = `${home}#docs`;
|
||||
const metaTitle = `${title} · ${t('nav.brand')}`;
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang={HTML_LANG[lang]}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{metaTitle}</title>
|
||||
{desc && <meta name="description" content={desc} />}
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="theme-color" content="#B96A3D" />
|
||||
</head>
|
||||
<body>
|
||||
<Header client:load lang={lang} t={headerT} />
|
||||
<main class="doc-page">
|
||||
<div class="wrap">
|
||||
<article class="doc-article">
|
||||
<slot />
|
||||
<a class="doc-back" href={backHref}>← {t('nav.docs')}</a>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
<Footer t={t} />
|
||||
</body>
|
||||
</html>
|
||||
@@ -35,7 +35,7 @@ import WhySignup from '../components/WhySignup.astro';
|
||||
import Pricing from '../components/Pricing.astro';
|
||||
import Download from '../components/Download.astro';
|
||||
import Docs from '../components/Docs.astro';
|
||||
import Blog from '../components/Blog.astro';
|
||||
// Blog 区暂隐藏(占位内容未就绪);有真实文章后恢复 import + <Blog/> + 导航/页脚链接。
|
||||
import CtaBand from '../components/CtaBand.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
|
||||
@@ -43,6 +43,12 @@ interface Props { lang: Lang }
|
||||
const { lang } = Astro.props;
|
||||
const t = createT(lang);
|
||||
|
||||
// 语言 → <html lang> / og:locale / 路由前缀(默认 en = /,其余 /<lang>/)。
|
||||
const HTML_LANG: Record<Lang, string> = { zh: 'zh-CN', en: 'en', ja: 'ja', ko: 'ko', ru: 'ru', es: 'es' };
|
||||
const OG_LOCALE: Record<Lang, string> = { zh: 'zh_CN', en: 'en_US', ja: 'ja_JP', ko: 'ko_KR', ru: 'ru_RU', es: 'es_ES' };
|
||||
const ALL_LANGS: Lang[] = ['en', 'zh', 'ja', 'ko', 'ru', 'es'];
|
||||
const langPath = (l: Lang) => (l === 'en' ? '/' : `/${l}/`);
|
||||
|
||||
const headerT = {
|
||||
product: t('nav.product'),
|
||||
pricing: t('nav.pricing'),
|
||||
@@ -50,27 +56,33 @@ const headerT = {
|
||||
docs: t('nav.docs'),
|
||||
blog: t('nav.blog'),
|
||||
login: t('nav.login'),
|
||||
center: t('nav.center'),
|
||||
brand: t('nav.brand'),
|
||||
mcenter: t('menu.center'),
|
||||
mswitch: t('menu.switch'),
|
||||
mlogout: t('menu.logout'),
|
||||
get: t('nav.get'),
|
||||
suBtn: t('su.btn'),
|
||||
};
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang={lang === 'zh' ? 'zh-CN' : 'en'}>
|
||||
<html lang={HTML_LANG[lang]}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{t('meta.title')}</title>
|
||||
<meta name="description" content={t('meta.desc')} />
|
||||
<meta name="robots" content="index,follow" />
|
||||
<link rel="canonical" href={new URL(lang === 'zh' ? '/' : '/en/', Astro.site).href} />
|
||||
<link rel="alternate" hreflang="zh-CN" href={new URL('/', Astro.site).href} />
|
||||
<link rel="alternate" hreflang="en" href={new URL('/en/', Astro.site).href} />
|
||||
<link rel="canonical" href={new URL(langPath(lang), Astro.site).href} />
|
||||
{ALL_LANGS.map((l) => (
|
||||
<link rel="alternate" hreflang={HTML_LANG[l]} href={new URL(langPath(l), Astro.site).href} />
|
||||
))}
|
||||
<link rel="alternate" hreflang="x-default" href={new URL('/', Astro.site).href} />
|
||||
<meta name="theme-color" content="#B96A3D" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content={t('meta.title')} />
|
||||
<meta property="og:description" content={t('meta.desc')} />
|
||||
<meta property="og:locale" content={lang === 'zh' ? 'zh_CN' : 'en_US'} />
|
||||
<meta property="og:locale" content={OG_LOCALE[lang]} />
|
||||
</head>
|
||||
<body>
|
||||
<AnnouncementBar client:idle text={t('ann.txt')} cta={t('ann.cta')} />
|
||||
@@ -81,10 +93,9 @@ const headerT = {
|
||||
<Hero t={t} />
|
||||
<Features t={t} />
|
||||
<WhySignup t={t} />
|
||||
<Pricing t={t} />
|
||||
<Pricing t={t} lang={lang} />
|
||||
<Download t={t} />
|
||||
<Docs t={t} />
|
||||
<Blog t={t} />
|
||||
<Docs t={t} lang={lang} />
|
||||
<CtaBand t={t} />
|
||||
<Footer t={t} />
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import Doc from '../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="en" title="FAQ" desc="Common questions about accounts, plans, devices and activation codes.">
|
||||
<div class="doc-eyebrow">Docs</div>
|
||||
<h1>Frequently asked questions</h1>
|
||||
<p class="doc-lede">Short answers to the questions we hear most about accounts, plans, devices and codes.</p>
|
||||
|
||||
<h2>How do I redeem an activation code?</h2>
|
||||
<p>Open the app, go to the account or subscription screen, choose <strong>Redeem code</strong>, paste the code and confirm. Your plan upgrades immediately — no restart needed. Codes are obtained through external channels; there is no checkout on the website or in the app.</p>
|
||||
|
||||
<h2>Which platforms are supported?</h2>
|
||||
<p>Windows, macOS, Android and iOS. One account works across all of them, and your plan and settings follow you between devices.</p>
|
||||
|
||||
<h2>What are the free plan limits?</h2>
|
||||
<p>The free plan gives you 10 minutes of connection time per day on a single basic route, and asks you to watch a short ad before each session. Core encryption and our no-logs promise are included on every plan, free or paid. During the 7-day trial the time limit is lifted.</p>
|
||||
|
||||
<h2>How many devices can I use?</h2>
|
||||
<p>Up to 5 devices on one account at the same time on the Pro plan. Sign in with the same email on each device to keep everything in sync.</p>
|
||||
|
||||
<h2>I forgot my password — what now?</h2>
|
||||
<p>Accounts sign in by email verification code, so there is no fixed password to forget. Just request a fresh code at login and enter it to get back in.</p>
|
||||
|
||||
<h2>Why is there no payment button on the site?</h2>
|
||||
<p>For risk and privacy reasons Pangolin never processes payments in the web or app. You get an activation code through an external channel and redeem it in the client — that keeps the payment flow entirely off our platform.</p>
|
||||
</Doc>
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
import Doc from '../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="en" title="Privacy policy" desc="Exactly what we collect, what we never collect, and how it is stored.">
|
||||
<div class="doc-eyebrow">Docs</div>
|
||||
<h1>Privacy policy</h1>
|
||||
<p class="doc-lede">Privacy protection is the baseline of this product, not a marketing line. Here is exactly what we do and don't collect, in plain language.</p>
|
||||
|
||||
<h2>What we collect</h2>
|
||||
<p>We keep only the operational minimum needed to run your account and the service:</p>
|
||||
<ul>
|
||||
<li><strong>Account email</strong> — used to sign in, deliver verification codes and tie your plan to you.</li>
|
||||
<li><strong>Device identifier</strong> — an anonymous ID used to enforce the device limit and sync your plan across devices.</li>
|
||||
<li><strong>Usage statistics</strong> — coarse figures such as connection time and data volume, used for billing limits and capacity planning.</li>
|
||||
</ul>
|
||||
|
||||
<h2>What we never collect</h2>
|
||||
<p>We do not log the content of your traffic, the sites or apps you reach, DNS queries, or any browsing history. There is no per-request connection log tied to what you do online. Because we never gather this data, there is nothing of that kind to disclose or lose.</p>
|
||||
|
||||
<h2>How it is stored</h2>
|
||||
<p>The limited data above is stored on our own infrastructure, encrypted in transit, and retained only as long as it is needed to operate your account and the service. Payments happen entirely through external channels, so no payment card details ever touch our systems.</p>
|
||||
|
||||
<div class="doc-card">
|
||||
<h3>Questions?</h3>
|
||||
<p>Reach out through any of the channels listed in the footer and we'll help clarify how your data is handled.</p>
|
||||
</div>
|
||||
</Doc>
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import Doc from '../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="en" title="Protocol & security" desc="Our data plane runs on sing-box with the REALITY transport, plus a strict no-logs architecture.">
|
||||
<div class="doc-eyebrow">Docs</div>
|
||||
<h1>Protocol & security</h1>
|
||||
<p class="doc-lede">How we move your traffic quickly while keeping it private — the transport, the encryption, and the no-logs architecture behind it.</p>
|
||||
|
||||
<h2>Data plane: sing-box + REALITY</h2>
|
||||
<p>Our data plane is built on <strong>sing-box</strong> and uses the <strong>REALITY</strong> transport. REALITY performs a genuine TLS handshake against a real destination, so accelerated traffic blends in with ordinary encrypted web traffic instead of standing out. The result is a connection that stays fast and reliable on demanding networks.</p>
|
||||
|
||||
<h2>End-to-end encryption</h2>
|
||||
<p>Every session is encrypted from your device to the node. Encryption is the baseline for all traffic on every plan — it is not an add-on. Keys are negotiated per session, and the client configuration is rendered and delivered by our control plane rather than assembled on the device.</p>
|
||||
|
||||
<h2>No-logs architecture</h2>
|
||||
<p>We do not record what you browse. Nodes forward traffic without keeping content or connection logs, and the system is designed so there is simply nothing sensitive to hand over. What we do keep is the operational minimum needed to run the service — see the <a href="/docs/privacy/">Privacy policy</a> for the exact list.</p>
|
||||
|
||||
<div class="doc-card">
|
||||
<h3>Kill switch</h3>
|
||||
<p>If the tunnel ever drops, the client blocks traffic instantly so your real IP is never exposed while the connection re-establishes.</p>
|
||||
</div>
|
||||
</Doc>
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import Doc from '../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="en" title="Quickstart" desc="Sign up, download and make your first connection in three minutes.">
|
||||
<div class="doc-eyebrow">Docs</div>
|
||||
<h1>Quickstart</h1>
|
||||
<p class="doc-lede">Get from zero to your first fast, stable connection in about three minutes — three steps, no configuration.</p>
|
||||
|
||||
<h2>1. Create an account</h2>
|
||||
<p>Enter your email on the homepage or in the app and confirm the verification code we send you. Registration is free and needs nothing but an email — no payment details, ever. New accounts include a 7-day free trial with full access.</p>
|
||||
|
||||
<h2>2. Download the client</h2>
|
||||
<p>Grab the app for your platform from the <a href="/#download">Download</a> section — Windows, macOS, Android and iOS are supported. One account syncs across every device, up to 5 at once.</p>
|
||||
|
||||
<h2>3. Log in and connect</h2>
|
||||
<p>Open the app, sign in with the same email, and tap the connect button. The app smart-picks the fastest route for you; there is nothing to configure. When you see <strong>Connected</strong>, you're on an accelerated line.</p>
|
||||
|
||||
<div class="doc-card">
|
||||
<h3>Upgrading later</h3>
|
||||
<p>For privacy and risk reasons we never take payment inside the web or app. When you want more, obtain an activation code through an external channel and redeem it in the client to unlock unlimited data and top-speed routes.</p>
|
||||
</div>
|
||||
</Doc>
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
// en 路由(/en/)
|
||||
// es 路由(/es/)
|
||||
import Site from '../../layouts/Site.astro';
|
||||
---
|
||||
<Site lang="en" />
|
||||
<Site lang="es" />
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
// zh 路由(/)
|
||||
// 默认语种 = 英文(/ → en)。其它语言:/zh/ /ja/ /ko/ /ru/ /es/。
|
||||
import Site from '../layouts/Site.astro';
|
||||
---
|
||||
<Site lang="zh" />
|
||||
<Site lang="en" />
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
// ja 路由(/ja/)
|
||||
import Site from '../../layouts/Site.astro';
|
||||
---
|
||||
<Site lang="ja" />
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
// ko 路由(/ko/)
|
||||
import Site from '../../layouts/Site.astro';
|
||||
---
|
||||
<Site lang="ko" />
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
// ru 路由(/ru/)
|
||||
import Site from '../../layouts/Site.astro';
|
||||
---
|
||||
<Site lang="ru" />
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import Doc from '../../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="zh" title="常见问题" desc="关于账户、套餐、设备与激活码的常见疑问。">
|
||||
<div class="doc-eyebrow">文档</div>
|
||||
<h1>常见问题</h1>
|
||||
<p class="doc-lede">关于账户、套餐、设备与兑换码,最常被问到的几个问题,简明作答。</p>
|
||||
|
||||
<h2>如何兑换激活码?</h2>
|
||||
<p>打开 App,进入账户或订阅页,选择<strong>兑换激活码</strong>,粘贴激活码并确认,套餐即刻升级,无需重启。激活码通过外部渠道获取;网页与 App 内均不设收银台。</p>
|
||||
|
||||
<h2>支持哪些平台?</h2>
|
||||
<p>Windows、macOS、Android 与 iOS。一个账户全平台通用,套餐与设置在各设备间同步。</p>
|
||||
|
||||
<h2>免费版有哪些限制?</h2>
|
||||
<p>免费版每天可连接 10 分钟,仅含 1 个基础节点,且每次连接前需观看一段短广告。核心加密与无日志承诺在所有套餐(含免费版)中一视同仁。7 天试用期内不受时长限制。</p>
|
||||
|
||||
<h2>可以用几台设备?</h2>
|
||||
<p>专业版一个账户最多 5 台设备同时在线。各设备用同一邮箱登录即可保持同步。</p>
|
||||
|
||||
<h2>忘记密码了怎么办?</h2>
|
||||
<p>账户采用邮箱验证码登录,没有固定密码需要记忆。登录时重新获取一次验证码、输入即可进入。</p>
|
||||
|
||||
<h2>为什么网页上没有支付按钮?</h2>
|
||||
<p>出于风控与隐私考虑,穿山甲不在网页或 App 内直接收款。你通过外部渠道获取激活码、在客户端内兑换 —— 资金流全程不经过我们的平台。</p>
|
||||
</Doc>
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
import Doc from '../../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="zh" title="隐私政策" desc="我们收集什么、绝不收集什么,以及如何存储。">
|
||||
<div class="doc-eyebrow">文档</div>
|
||||
<h1>隐私政策</h1>
|
||||
<p class="doc-lede">隐私保护是这款产品的底线,而非营销话术。以下用大白话说清我们收集与绝不收集的内容。</p>
|
||||
|
||||
<h2>我们收集什么</h2>
|
||||
<p>我们只保留运行账户与服务所必需的最小信息:</p>
|
||||
<ul>
|
||||
<li><strong>账户邮箱</strong> —— 用于登录、发送验证码,以及将套餐与你绑定。</li>
|
||||
<li><strong>设备标识</strong> —— 一个匿名 ID,用于限制设备数量、在多设备间同步套餐。</li>
|
||||
<li><strong>用量统计</strong> —— 连接时长、流量等粗粒度数据,用于计费限额与容量规划。</li>
|
||||
</ul>
|
||||
|
||||
<h2>我们绝不收集什么</h2>
|
||||
<p>我们不记录你的流量内容、访问的网站或 App、DNS 查询,也不保留任何浏览历史;不存在与你上网行为绑定的逐条连接日志。因为我们从一开始就不采集这类数据,所以也没有这类数据可供交出或泄露。</p>
|
||||
|
||||
<h2>如何存储</h2>
|
||||
<p>上述有限数据存放在我们自有的基础设施上,传输过程加密,且仅在运行账户与服务所需的期限内保留。收款全部经外部渠道完成,任何银行卡信息都不会触及我们的系统。</p>
|
||||
|
||||
<div class="doc-card">
|
||||
<h3>还有疑问?</h3>
|
||||
<p>通过页脚列出的任一渠道联系我们,我们会进一步说明你的数据是如何被处理的。</p>
|
||||
</div>
|
||||
</Doc>
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import Doc from '../../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="zh" title="协议与安全" desc="数据面基于 sing-box + REALITY,配合严格无日志架构。">
|
||||
<div class="doc-eyebrow">文档</div>
|
||||
<h1>协议与安全</h1>
|
||||
<p class="doc-lede">我们如何在保持极速的同时守护隐私 —— 传输方式、加密机制,以及背后的无日志架构。</p>
|
||||
|
||||
<h2>数据面:sing-box + REALITY</h2>
|
||||
<p>我们的数据面基于 <strong>sing-box</strong>,传输采用 <strong>REALITY</strong>。REALITY 会与真实站点完成一次真正的 TLS 握手,使加速流量与普通加密网页流量融为一体、不易被区分,从而在苛刻网络下依然快速稳定。</p>
|
||||
|
||||
<h2>端到端加密</h2>
|
||||
<p>每一次会话都从你的设备到节点全程加密。加密是所有套餐、所有流量的底线,而非附加项。密钥按会话协商,客户端配置由控制面渲染下发,而非在设备本地拼装。</p>
|
||||
|
||||
<h2>无日志架构</h2>
|
||||
<p>我们不记录你浏览了什么。节点只做流量转发,不保留内容或连接日志;整套系统的设计初衷,就是让敏感数据「压根不存在、无从交出」。我们仅保留运行服务所必需的最小运营数据 —— 具体清单见<a href="/zh/docs/privacy/">隐私政策</a>。</p>
|
||||
|
||||
<div class="doc-card">
|
||||
<h3>Kill Switch</h3>
|
||||
<p>一旦隧道中断,客户端立即阻断网络,在连接重建期间杜绝真实 IP 泄露。</p>
|
||||
</div>
|
||||
</Doc>
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import Doc from '../../../layouts/Doc.astro';
|
||||
---
|
||||
<Doc lang="zh" title="快速开始" desc="三分钟完成注册、下载与首次连接。">
|
||||
<div class="doc-eyebrow">文档</div>
|
||||
<h1>快速开始</h1>
|
||||
<p class="doc-lede">三步走,约三分钟即可完成第一次极速、稳定的连接,全程无需任何配置。</p>
|
||||
|
||||
<h2>1. 注册账户</h2>
|
||||
<p>在主页或 App 内填写邮箱,输入收到的验证码即可完成注册。注册免费,只要一个邮箱,无需任何付款信息。新账户还附赠 7 天免费试用,功能不设限。</p>
|
||||
|
||||
<h2>2. 下载客户端</h2>
|
||||
<p>在<a href="/zh/#download">下载</a>区选择对应平台的安装包 —— 支持 Windows、macOS、Android 与 iOS。一个账户多端同步,最多 5 台设备同时在线。</p>
|
||||
|
||||
<h2>3. 登录并连接</h2>
|
||||
<p>打开 App,用同一邮箱登录,点一下连接按钮即可。客户端会智能挑选最快线路,无需手动设置。当界面显示<strong>已连接</strong>,你就已经在加速线路上了。</p>
|
||||
|
||||
<div class="doc-card">
|
||||
<h3>之后如何升级</h3>
|
||||
<p>出于风控与隐私考虑,我们不在网页或 App 内直接收款。想要更多时,通过外部渠道获取激活码,在客户端内兑换即可解锁无限流量与极速线路。</p>
|
||||
</div>
|
||||
</Doc>
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
// zh 路由(/zh/)。默认语种已改英文(/ → en),中文移到此。
|
||||
import Site from '../../layouts/Site.astro';
|
||||
---
|
||||
<Site lang="zh" />
|
||||
@@ -40,6 +40,144 @@
|
||||
}
|
||||
.langseg a.on { background: var(--accent); color: var(--fg-on-accent); }
|
||||
|
||||
/* 6 语用原生下拉(横排段控会挤)。圆角矩形 + 柔和边框(原纯 --border 太生硬)。 */
|
||||
.langsel {
|
||||
border: 1px solid color-mix(in oklab, var(--border), transparent 55%);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 5px 10px;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
color: var(--fg2);
|
||||
background: var(--bg-subtle);
|
||||
box-shadow: 0 1px 2px rgba(45, 30, 20, 0.05);
|
||||
transition: border-color 140ms ease, box-shadow 140ms ease;
|
||||
}
|
||||
.langsel:hover {
|
||||
border-color: color-mix(in oklab, var(--accent), transparent 40%);
|
||||
box-shadow: 0 1px 4px rgba(45, 30, 20, 0.08);
|
||||
}
|
||||
|
||||
/* 登录态用户下拉菜单:复用 .langwrap/.langsel/.langmenu 视觉,追加用户名截断
|
||||
与菜单内 <button>(切换用户 / 退出登录需 JS,非纯链接)的等价样式。 */
|
||||
.usermenu .userbtn {
|
||||
max-width: 168px;
|
||||
}
|
||||
.usermenu .uname {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.usermenu .langmenu button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--fg1);
|
||||
background: transparent;
|
||||
padding: 8px 11px;
|
||||
border-radius: var(--radius-sm);
|
||||
white-space: nowrap;
|
||||
transition: background var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.usermenu .langmenu button:hover {
|
||||
background: var(--bg-subtle);
|
||||
}
|
||||
|
||||
/* 文档正文页(/docs/*):承载标题层级 / 段落 / 列表 / 卡片的可读排版。 */
|
||||
.doc-page {
|
||||
padding: 56px 0 88px;
|
||||
}
|
||||
.doc-article {
|
||||
max-width: 760px;
|
||||
}
|
||||
.doc-article .doc-eyebrow {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
}
|
||||
.doc-article h1 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: 38px;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.15;
|
||||
margin: 12px 0 0;
|
||||
}
|
||||
.doc-article .doc-lede {
|
||||
font-size: 17px;
|
||||
color: var(--fg2);
|
||||
line-height: 1.6;
|
||||
margin: 14px 0 0;
|
||||
}
|
||||
.doc-article h2 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: 22px;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
.doc-article h3 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 600;
|
||||
font-size: 17px;
|
||||
margin: 26px 0 0;
|
||||
}
|
||||
.doc-article p {
|
||||
font-size: 15.5px;
|
||||
color: var(--fg2);
|
||||
line-height: 1.7;
|
||||
margin: 12px 0 0;
|
||||
}
|
||||
.doc-article ul,
|
||||
.doc-article ol {
|
||||
margin: 12px 0 0;
|
||||
padding-left: 22px;
|
||||
color: var(--fg2);
|
||||
}
|
||||
.doc-article li {
|
||||
font-size: 15.5px;
|
||||
line-height: 1.7;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
.doc-article a {
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
.doc-article strong {
|
||||
color: var(--fg1);
|
||||
font-weight: 700;
|
||||
}
|
||||
.doc-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: 22px 24px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
margin: 26px 0 0;
|
||||
}
|
||||
.doc-card h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.doc-back {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 44px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* 视觉隐藏(无障碍用,当前未强依赖) */
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* AUTO-GENERATED — 勿手改。源: design/colors_and_type.css。生成器: web/website/scripts/build-tokens.mjs。仅移除第三方 Google Fonts @import。 */
|
||||
/* AUTO-GENERATED — 勿手改。源: design/prototype/tokens.css。生成器: web/website/scripts/build-tokens.mjs。仅移除第三方 Google Fonts @import。 */
|
||||
/* =============================================================
|
||||
穿山甲 VPN · Pangolin VPN — Design Tokens
|
||||
colors_and_type.css
|
||||
|
||||
@@ -42,6 +42,17 @@ img,svg{display:block}
|
||||
.linklogin{font-size:14.5px;font-weight:600;color:var(--fg1);cursor:pointer}
|
||||
.linklogin:hover{color:var(--accent)}
|
||||
|
||||
/* ---------- language dropdown ---------- */
|
||||
.langwrap{position:relative;display:inline-block}
|
||||
.langsel{display:inline-flex;align-items:center;gap:6px;border:1.5px solid var(--border-strong);border-radius:var(--radius-full);padding:8px 14px;background:var(--surface);color:var(--fg1);font-family:var(--font-sans);font-size:14px;font-weight:600;cursor:pointer;transition:border-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out)}
|
||||
.langsel:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.langsel .caret{width:11px;height:11px;transition:transform 140ms var(--ease-out)}
|
||||
.langsel[aria-expanded="true"] .caret{transform:rotate(180deg)}
|
||||
.langmenu{position:absolute;top:calc(100% + 6px);right:0;min-width:160px;display:flex;flex-direction:column;gap:1px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);padding:5px;z-index:60}
|
||||
.langmenu a{text-align:left;text-decoration:none;font-family:var(--font-sans);font-size:13px;font-weight:500;color:var(--fg1);background:transparent;padding:8px 11px;border-radius:var(--radius-sm);white-space:nowrap;transition:background var(--dur-fast) var(--ease-out)}
|
||||
.langmenu a:hover{background:var(--bg-subtle)}
|
||||
.langmenu a[aria-selected="true"]{color:var(--accent);background:var(--accent-subtle);font-weight:700}
|
||||
|
||||
/* mobile nav */
|
||||
.menu-btn{display:none;border:none;background:transparent;cursor:pointer;padding:7px;border-radius:var(--radius-sm);color:var(--fg1)}
|
||||
.menu-btn svg{width:22px;height:22px}
|
||||
@@ -189,6 +200,8 @@ section{padding:88px 0}
|
||||
.dl .pv{font-size:12px;color:var(--fg3);margin:3px 0 14px;font-family:var(--font-mono)}
|
||||
.dl .gb{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;color:var(--accent)}
|
||||
.dl .gb svg{width:14px;height:14px}
|
||||
.dl .gb.disabled{color:var(--fg3);cursor:not-allowed;pointer-events:none}
|
||||
.dl .gb.disabled svg{opacity:.5}
|
||||
|
||||
/* ---------- docs ---------- */
|
||||
.docs-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;margin-top:44px}
|
||||
@@ -225,17 +238,17 @@ section{padding:88px 0}
|
||||
.cta-ghost:hover{border-color:#fff}
|
||||
|
||||
/* ---------- footer ---------- */
|
||||
.ftr{background:var(--sand-950);color:#cfc6b8;padding:64px 0 32px}
|
||||
.ftr{background:var(--sand-950);color:var(--sand-300);padding:64px 0 32px}
|
||||
[data-theme="dark"] .ftr{background:#000}
|
||||
.ftr .top{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:36px}
|
||||
.ftr .nm{font-family:var(--font-display);font-weight:700;font-size:18px;color:#fff;display:flex;align-items:center;gap:10px}
|
||||
.ftr .tag{font-size:13.5px;line-height:1.6;margin:14px 0 0;max-width:280px;color:#a89e8e}
|
||||
.ftr h4{font-family:var(--font-sans);font-size:12px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#8a8070;margin:0 0 14px}
|
||||
.ftr .tag{font-size:13.5px;line-height:1.6;margin:14px 0 0;max-width:280px;color:var(--sand-400)}
|
||||
.ftr h4{font-family:var(--font-sans);font-size:12px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--sand-500);margin:0 0 14px}
|
||||
.ftr ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:10px}
|
||||
.ftr ul a{font-size:14px;color:#cfc6b8}
|
||||
.ftr ul a{font-size:14px;color:var(--sand-300)}
|
||||
.ftr ul a:hover{color:#fff}
|
||||
.ftr .mono{font-family:var(--font-mono);font-size:12.5px}
|
||||
.ftr .bot{display:flex;align-items:center;justify-content:space-between;margin-top:46px;padding-top:24px;border-top:1px solid rgba(255,255,255,.1);font-size:12.5px;color:#8a8070;flex-wrap:wrap;gap:12px}
|
||||
.ftr .bot{display:flex;align-items:center;justify-content:space-between;margin-top:46px;padding-top:24px;border-top:1px solid rgba(255,255,255,.1);font-size:12.5px;color:var(--sand-500);flex-wrap:wrap;gap:12px}
|
||||
|
||||
/* ---------- responsive ---------- */
|
||||
@media (max-width:980px){
|
||||
|
||||
Reference in New Issue
Block a user