# PURO Portal i18n + Pricing Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Ship bilingual (zh/en) support across 5 portal pages (Landing, Docs, Login-narrative, Register-narrative, **new** Pricing) by mounting a dark-tech `PuroLocaleSwitcher` in each top nav and extracting all hard-coded Chinese into i18n keys with English translations.
**Architecture:**
- Reuse the existing `vue-i18n` infrastructure (`setLocale()`, `availableLocales`, `sub2api_locale` localStorage key). Only add portal-specific namespaces: `landing.*`, `docs.*`, `pricing.*`, `auth.narrative.*`.
- The current admin `LocaleSwitcher.vue` uses Tailwind gray/dark palette that clashes with `.puro-page` dark-tech theme — build a new `PuroLocaleSwitcher.vue` styled with puro.css tokens (`--cyan`, `--bg-0`, `--border`, `--font-mono`), reusing the same `setLocale()` core.
- Pricing page is ported from `docs/design-drafts/v2/Pricing.html` as a fidelity Vue component, i18n-native from commit one (no "extract later" debt).
**Tech Stack:** Vue 3 + TS (`
```
- [ ] **Step 2: Verify component typechecks**
Run: `pnpm run typecheck` (from `frontend/`)
Expected: PASS, no new errors.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/puro/PuroLocaleSwitcher.vue
git commit -m "feat(i18n): add PuroLocaleSwitcher for portal pages"
```
---
## Task 2: Mount switcher in `LandingView` nav
**Files:**
- Modify: `frontend/src/views/landing/LandingView.vue` (template nav block + script import; NO i18n text extraction in this task — that happens in Task 5)
- [ ] **Step 1: Import the switcher**
In `
```
- [ ] **Step 2: Typecheck**
Run: `pnpm run typecheck`
Expected: PASS.
- [ ] **Step 3: Commit**
```bash
git add frontend/src/components/puro/PricingCalculator.vue
git commit -m "feat(pricing): add PricingCalculator subcomponent"
```
---
## Task 9: Build `PricingView` (i18n-native) + add route
**Files:**
- Create: `frontend/src/views/pricing/PricingView.vue`
- Modify: `frontend/src/router/index.ts` (add `/pricing` route)
- Modify: `frontend/src/i18n/locales/zh.ts` (add `pricing.*`)
- Modify: `frontend/src/i18n/locales/en.ts` (add `pricing.*`)
**Source:** `docs/design-drafts/v2/Pricing.html` — port verbatim, extract Chinese strings to keys.
**Decisions locked:**
- Header subkicker: ZH `// preview · 最终定价以开售为准` / EN `// preview · final pricing TBD at launch`
- Calculator header pill: ZH `// estimated · 以实际计费为准` / EN `// estimated · for reference only`
- Enterprise card → `mailto:contact@puro.im`
- Binding card → `/register` router-link (no `/binding` page)
- Tier CTAs → `/register` router-link
- Final CTA `Docs` link → `/docs`
**SOON chip:** before writing the template, the subagent audits the backend for these features (grep at `backend/`):
1. API Key monthly budget / 402 Payment Required → look for `budget` / `Payment Required`
2. Zero-log mode → look for `zero_log` / `zeroLog`
3. Priority scheduling → look for `priority`
4. RPM limits (60/120/300) → look for rate limiter
5. Subscription failover → look for `failover` / `cooling`
For each feature NOT found: wrap the `
` with an extra chip `
{{ $t('pricing.soonChip') }} ` (chip = small pill, amber/muted). Add chip CSS in same scoped style.
- [ ] **Step 1: Audit backend for advertised features**
Run: `cd /Users/mini/Work/dev/sub2api && grep -ril -E "budget|zero_log|priority_schedul|failover|cooling" backend/ 2>/dev/null | head -20`
Document which features are implemented; the rest get `SOON` chips.
- [ ] **Step 2: Add `pricing` namespace to `zh.ts`** with structure:
```ts
pricing: {
hero: {
kicker: '// pricing · 充多少 · 用多少 · 永不过期',
previewPill: '// preview · 最终定价以开售为准',
title1: '一次充值,',
titleAccent: '全平台',
title2: '通用',
sub: '同一份积分可以用在 Claude / ChatGPT / Gemini 任意池上。我们把你的订阅额度变成真正的 API 余额 —— 相比官方 API 便宜 {discount}。',
subDiscount: '至多 70%',
underline: '余额永不过期 · 支持支付宝 / 微信 / USDT · 无隐藏订阅费'
},
soonChip: 'SOON',
tiers: {
starter: {
flag: 'STARTER',
tierLabel: 'tier · 01',
headline: '先尝尝鲜,跑通接入',
credit: '充 $9.9 → 得 {credit} 积分 {bonus}',
creditAmount: '$12',
creditBonus: '+21%',
discountTag: '相当于官方 API · 0.5 折起',
cta: '充值 →',
features: {
allModels: '可用所有模型 / 所有池',
oneKey: '{count} 个 API Key',
oneKeyCount: '1',
rpm60: '60 RPM 速率限制',
log7: '基础日志(7 天保留)',
noBYOS: '自带订阅接入',
noTeam: '团队 / 多人协作'
}
},
pro: {
flag: '◆ 推荐',
tierLabel: 'tier · 02',
headline: '个人重度用户 · 最划算',
credit: '充 $29.9 → 得 {credit} 积分 {bonus}',
creditAmount: '$45',
creditBonus: '+50%',
discountTag: '相当于官方 API · 3-7 折',
cta: '立即充值 →',
features: {
allModels: '可用所有模型 / 所有池',
threeKeys: '{count} 个 API Key · 独立预算',
threeKeysCount: '3',
rpm120: '120 RPM 速率限制',
log30: '调用日志(30 天保留)',
byos: '自带订阅接入(无限个)',
failover: '多账号 failover 调度'
}
},
scale: {
flag: '⚡ 限时 +100%',
tierLabel: 'tier · 03',
headline: '小团队 / 长跑项目',
credit: '充 $99 → 得 {credit} 积分 {bonus}',
creditAmount: '$198',
creditBonus: '+100%',
discountTag: '相当于官方 API · 2-5 折',
cta: '充值 →',
features: {
proAll: '所有 Pro 能力',
tenKeys: '{count} 个 API Key · 独立预算',
tenKeysCount: '10',
rpm300: '300 RPM 速率限制',
log90: '调用日志(90 天保留)',
priority: '请求优先级加权调度',
community: 'Slack / Discord 群组支持'
}
},
custom: {
flag: 'CUSTOM',
tierLabel: 'tier · 04',
headline: '自定义金额 · 按需充值',
creditPrefix: '得约',
bonusPrefix: '+',
discountTag: '根据金额阶梯自动匹配折扣',
cta: '定制充值 →',
features: {
noExpire: '积分永不过期',
proAll: 'Pro 全部能力',
tier: '阶梯 +21% ~ +100%',
pay: '支付宝 / 微信 / USDT',
slider: '拖动滑块预览赠送'
}
}
},
custom: {
enterprise: {
title: 'Enterprise · 企业定制',
desc: '专属订阅池、SLA、合规审计、私有化部署、发票结算。规模 >$500/月起可申请。',
cta: '联系商务 →'
},
binding: {
title: '已有订阅?直接接入',
desc: '有 Claude Max / ChatGPT Pro?免费注册后绑定,只为 PURO 路由费买单 —— 按次 {price}。',
price: '$0.0008/request',
cta: '接入我的订阅 →'
}
},
calc: {
kicker: '// cost estimator',
previewPill: '// estimated · 以实际计费为准',
title: '算算你能省多少?',
sub: '按你的使用场景,对比 PURO 和官方 API 的月度花费差。数字会根据你选的场景自动更新。',
reqLabel: '日均请求数',
tokLabel: '平均每请求 tokens',
mixLabel: 'Claude 占比',
monthlyTok: '月度 tokens 消耗',
officialCost: '官方 API 价格',
puroCost: 'PURO 价格(含 +50% 赠送)',
savings: '节省',
recLabel: '建议充值',
recStarter: '≈ Starter 档够用',
recPro: '≈ Pro 档 1 个月',
recScale: '≈ Scale 档 · 1 个月'
},
works: {
kicker: '// works everywhere',
title: '一个 key,所有工具通用',
sub: '只要支持自定义 {baseUrl} 或 OpenAI / Anthropic API,都能直接接入 PURO。',
baseUrl: 'base_url'
},
faq: {
kicker: '// frequently asked',
title: '你可能想问的',
noAnswer: '没找到答案?{contact} · 通常 2 小时内回复。',
contact: '发邮件给我们 ↗',
q1: 'PURO 和 API 中转站 / API 代理有什么不同?',
a1: '中转站只是把官方 API 请求转一手,价格取决于你预付多少 balance。PURO 的不同是 —— 我们让你 {bold}。你原本就在付的 $20/月,不再只能在官网聊天里用,而是通过统一 API 喂给 Cursor、Claude Code、任何 SDK。同时我们也提供按量充值的官方 API 备用池,两种模式可以混用。',
a1bold: '把已有的 Claude Pro / ChatGPT Plus 订阅变成 API',
q2: '用订阅跑 API 会不会被封号?',
a2: '我们会自动控制每个订阅的请求节奏,并在触发限流时把请求 failover 到池子里的其他订阅。实际上 PURO 的调用模式比你在官方客户端直接复制粘贴大段对话 {bold}。你绑定多个订阅时,单个账号的 RPM 会被压到足够安全的阈值内。另外所有凭证用 AES-256 加密存储,请求链路不经过第三方。',
a2bold: '更不容易触发风控',
q3: '积分会过期吗?可以退款吗?',
a3: '{bold}你可以攒着慢慢用 —— 包括几个月都不用。首次充值 7 天内未产生任何调用可全额退款,之后按剩余积分 85% 比例退。详见退款政策。',
a3bold: '积分永不过期。',
q4: '支持哪些支付方式?',
a4: '国内:支付宝 · 微信支付。国际:Stripe 信用卡 · USDT (TRC20 / ERC20) · PayPal。企业充值支持 Invoice 对公打款,人民币开票。',
q5: '一个 PURO 账号可以绑定多少个订阅?',
a5Starter: 'Starter 档:不支持绑定自带订阅',
a5Pro: 'Pro 档及以上:无限制,你可以把 10 个 ChatGPT Plus + 3 个 Claude Pro 一起绑上去,统一调度',
a5Enterprise: 'Enterprise:支持跨团队共享池,按组织维度隔离',
q6: '如果某个订阅触发限流了会怎样?',
a6: 'PURO 的调度器会把受限的订阅自动标记为 cooling 状态,暂时从池子里摘除。同一请求会立刻被 failover 到池内其他健康订阅上 —— 调用方通常 {bold}。你可以在 Dashboard 看到每个订阅的当前状态和剩余配额。',
a6bold: '感受不到中断',
q7: '计费精度?超量会怎么办?',
a7: '按实际 token 数 + 模型单价计费,精度到 4 位小数。每个 API Key 可设置独立月度预算,达到后 402 Payment Required,不会继续扣费。账户总余额不足时同样会返回 402,且 Dashboard 有 80% / 95% 两级提醒邮件。',
q8: '数据会被用于训练吗?',
a8: '{bold}所有请求仅用于路由转发,不入库、不留存内容(仅保留元数据如模型、token 数、延迟,用于计费和日志)。Pro 档及以上可选开启"零日志模式",我们连请求 ID 都不记录。',
a8bold: '不会。',
q9: '可以私有化部署吗?',
a9: 'Enterprise 档支持 Docker / K8s 私有化部署,控制面和数据面可以分开。授权按年订阅,包含升级和技术支持。',
q10: '支持哪些模型?会跟进新模型吗?',
a10: '当前覆盖 Claude(Sonnet 4.5 / Opus 4 / Haiku 4.5)、ChatGPT(GPT-5 / GPT-5 Codex / GPT-4.1)、Gemini(2.5 Pro / 2.5 Flash)。每当官方发布新模型,我们通常在 {bold}。完整模型列表见文档。',
a10bold: '24 小时内上线'
},
finalCta: {
kicker: '// ready to start',
title: '5 分钟,拿到你第一个 sk-puro-* key',
subtitle: '绑定你的第一个订阅即可开始。',
ctaPrimary: '免费注册 →',
ctaDocs: '查看文档'
}
}
```
**Note:** The zip's final CTA contains `注册送 $5 测试积分` — DROP this line per Stage 1 decision (no $5 bonus). The subtitle above is pruned.
- [ ] **Step 3: Mirror `pricing` namespace in `en.ts`**
Translation guidelines:
- Hero title: `"Top up once," / "unlimited across" / "all platforms"`
- Hero sub: `"The same credits work across Claude / ChatGPT / Gemini pools. We turn your subscription into real API balance — {discount} cheaper than the official API."`
- Tier CTAs: `"Top up →"` / `"Buy Pro →"` / `"Top up →"` / `"Custom top-up →"`
- Tier flags: `"STARTER"` / `"◆ RECOMMENDED"` / `"⚡ LIMITED +100%"` / `"CUSTOM"`
- FAQ answers: professional/concise tone, keep technical terms English
- [ ] **Step 4: Write `PricingView.vue`**
Structure:
```vue
PURO AI
{{ $t('landing.nav.products') }}
{{ $t('landing.nav.pricing') }}
{{ $t('landing.nav.docs') }}
{{ $t('landing.nav.login') }}
{{ $t('landing.nav.signup') }}
{{ $t('pricing.hero.kicker') }}
{{ $t('pricing.hero.previewPill') }}
{{ $t('pricing.hero.title1') }}{{ $t('pricing.hero.titleAccent') }} {{ $t('pricing.hero.title2') }}
{{ $t('pricing.hero.subDiscount') }}
{{ $t('pricing.hero.underline') }}
{{ $t('pricing.calc.previewPill') }}
```
**Subagent execution note:** This task is large. Full template is ~400 lines of Vue. Subagent should:
1. Open `docs/design-drafts/v2/Pricing.html` in one tab
2. Open the target `PricingView.vue` being written
3. Copy each section (hero → tier grid → custom row → calc → works → faq → final-cta), replacing raw Chinese with `$t(...)` lookups per the schema in Step 2
4. Keep all SVG/HTML structure verbatim
5. Apply the SOON chip to any unimplemented feature per Step 1 audit results
6. Remove the $5 bonus line from final CTA
- [ ] **Step 5: Add `/pricing` route**
Modify `frontend/src/router/index.ts`. Add new route entry (public, no auth guard):
```ts
{
path: '/pricing',
name: 'pricing',
component: () => import('@/views/pricing/PricingView.vue'),
meta: { requiresAuth: false, title: 'Pricing · PURO AI' }
}
```
Add this near the `/docs` route (public portal section).
- [ ] **Step 6: Add `定价 / Pricing` link to LandingView nav**
Modify `LandingView.vue` `.nav-links` block:
```vue
```
(Landing's Pricing link was added as part of Task 3's nav update for Docs; Landing gets it here.)
- [ ] **Step 7: Typecheck + build**
Run: `pnpm run typecheck && pnpm run build`
Expected: PASS.
- [ ] **Step 8: Commit**
```bash
git add frontend/src/views/pricing/ frontend/src/router/index.ts frontend/src/views/landing/LandingView.vue frontend/src/i18n/locales/zh.ts frontend/src/i18n/locales/en.ts
git commit -m "feat(pricing): add PricingView with bilingual i18n + nav link"
```
---
## Task 10: Verification + PR + deploy
**Files:** none changed
- [ ] **Step 1: Run full typecheck + build**
```bash
cd frontend
pnpm run typecheck
pnpm run build
```
Expected: both PASS.
- [ ] **Step 2: Scan for leftover hard-coded Chinese in portal views**
```bash
cd /Users/mini/Work/dev/sub2api
grep -rnP "[\x{4e00}-\x{9fa5}]" frontend/src/views/landing/ frontend/src/views/docs/ frontend/src/views/pricing/ 2>/dev/null | grep -v "^.*://" | grep -vE ""
```
Expected: empty output (only things that should remain are comments, which this grep filters).
- [ ] **Step 3: Start preview and manually verify**
```bash
cd frontend
pnpm run preview
```
Open in browser (http://localhost:4173):
- `/` — Landing: switcher in top-right, click `EN` → all text flips to English, refresh → stays English
- `/pricing` — Pricing: 4 tiers render, calculator sliders work, FAQ accordions open, switcher works
- `/docs` — Docs: tables render, copy-code works, switcher works
- `/login` — narrative panel + form, switcher top-right
- `/register` — narrative panel + form, switcher top-right
For each page: toggle EN ↔ ZH at least twice, confirm no flashes of untranslated Chinese.
- [ ] **Step 4: Stop preview**
Ctrl-C the preview server. Verify no zombie processes:
```bash
pgrep -f "vite.*preview"
```
Expected: no output. If any: `pkill -f "vite.*preview"`.
- [ ] **Step 5: Push branch and open PR**
```bash
git push -u origin feat/portal-i18n-pricing
gh pr create --title "feat: PURO portal i18n (zh/en) + Pricing page" --body "$(cat <<'EOF'
## Summary
- Puro-themed `PuroLocaleSwitcher` mounted in Landing/Docs/Login/Register top nav
- Full i18n extraction for LandingView / DocsView / Login & Register narrative panels (zh + en)
- New `/pricing` page ported from design zip, i18n-native, with preview pricing pill + SOON chips for unshipped features
- Nav adds 定价 / Pricing link on Landing + Docs
## Test plan
- [ ] Typecheck + build pass
- [ ] Toggle EN/ZH on /, /pricing, /docs, /login, /register — all text switches
- [ ] Refresh persists locale (localStorage `sub2api_locale`)
- [ ] Pricing calculator sliders update live; custom tier bonus updates
- [ ] Admin pages (`/dashboard`, `/admin/*`) unaffected — no CSS regressions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```
- [ ] **Step 6: Merge when CI green**
```bash
gh pr merge --squash --delete-branch
```
- [ ] **Step 7: Verify production deploy**
```bash
curl -sSf -o /dev/null -w "%{http_code}\n" https://ai.puro.im/
curl -sSf -o /dev/null -w "%{http_code}\n" https://ai.puro.im/pricing
curl -sSf -o /dev/null -w "%{http_code}\n" https://ai.puro.im/docs
```
Expected: `200 200 200`.
- [ ] **Step 8: Cleanup worktree**
```bash
cd /Users/mini/Work/dev/sub2api
git worktree remove ../sub2api-portal-i18n
```
---
## Appendix: Translation style guide (EN)
- **Register:** tech-product, concise, no marketing fluff
- **Technical terms:** keep English (`OAuth`, `SDK`, `API key`, `RPM`, `base_url`, `tokens`)
- **Brand tone:** PURO speaks to developers first, so answers in FAQ stay factual, not salesy
- **Punctuation:** English full stops / commas (not `,` or `。`)
- **Numbers:** keep format from zh (e.g., `$29.9`, `$198`)
## Appendix: Key guarantees
- All 5 pages continue to render correctly in zh after extraction (regression check at Task 10 Step 3)
- `setLocale()` remains the single source of truth — no custom storage added
- `PuroLocaleSwitcher` reuses `setLocale()` imports; does not duplicate i18n plumbing
- No changes to admin pages, AppHeader, or existing `LocaleSwitcher.vue` (decoupled)