Skip to content

Commit b400559

Browse files
committed
Support Chinese
1 parent 2abbc3f commit b400559

23 files changed

Lines changed: 802 additions & 107 deletions
6.1 MB
Binary file not shown.
5.81 MB
Binary file not shown.
8.6 MB
Binary file not shown.
8.05 MB
Binary file not shown.

src/components/games/GameCard.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
import ComponentChips from "./ComponentChips.astro";
3-
import { t, type Locale } from "../../lib/i18n";
3+
import { t, localizedPath, type Locale } from "../../lib/i18n";
44
55
interface Props {
66
title: string;
@@ -15,7 +15,7 @@ interface Props {
1515
const { title, slug, summary, status, components, locale, cover } = Astro.props;
1616
---
1717

18-
<a href={`/games/${slug}`} class="game-card">
18+
<a href={localizedPath(locale, `/games/${slug}`)} class="game-card">
1919
<div class="game-card__cover">
2020
{cover ? (
2121
<img src={cover} alt={title} class="game-card__image" loading="lazy" />

src/components/layout/BaseLayout.astro

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import Header from "./Header.astro";
33
import Footer from "./Footer.astro";
44
import GridBackground from "../ui/GridBackground.astro";
55
import "../../styles/global.css";
6-
import { type Locale, defaultLocale, t } from "../../lib/i18n";
6+
// ZH-specific styles are imported unconditionally — every rule inside is
7+
// scoped under `html[lang="zh"]`, so EN pages never apply any of it, and
8+
// the CJK woff2 files only load when glyphs actually require them.
9+
import "../../styles/zh.css";
10+
import { type Locale, defaultLocale, t, toggleLocalePath } from "../../lib/i18n";
711
812
interface Props {
913
title?: string;
@@ -25,6 +29,10 @@ const siteTitle = t(lang, "site.title");
2529
const pageTitle = title ? `${title} — ${siteTitle}` : siteTitle;
2630
const pageDescription = description ?? t(lang, "site.description");
2731
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
32+
33+
// Compute absolute URLs for each locale variant of this page
34+
const enUrl = new URL(toggleLocalePath(Astro.url.pathname, "en"), Astro.site).href;
35+
const zhUrl = new URL(toggleLocalePath(Astro.url.pathname, "zh"), Astro.site).href;
2836
---
2937

3038
<!doctype html>
@@ -41,6 +49,11 @@ const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
4149
<!-- Canonical -->
4250
<link rel="canonical" href={canonicalUrl.href} />
4351

52+
<!-- Alternate language variants (for SEO) -->
53+
<link rel="alternate" hreflang="en" href={enUrl} />
54+
<link rel="alternate" hreflang="zh" href={zhUrl} />
55+
<link rel="alternate" hreflang="x-default" href={enUrl} />
56+
4457
<!-- Open Graph -->
4558
<meta property="og:type" content="website" />
4659
<meta property="og:title" content={pageTitle} />

src/components/layout/Header.astro

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
import { t, getLocaleFromUrl, localizedPath, type Locale } from "../../lib/i18n";
2+
import { t, getLocaleFromUrl, localizedPath, toggleLocalePath, type Locale } from "../../lib/i18n";
33
44
interface Props {
55
locale?: Locale;
@@ -15,6 +15,8 @@ const navItems = [
1515
];
1616
1717
const currentPath = Astro.url.pathname;
18+
const enHref = toggleLocalePath(currentPath, "en");
19+
const zhHref = toggleLocalePath(currentPath, "zh");
1820
---
1921

2022
<header class="header">
@@ -44,9 +46,17 @@ const currentPath = Astro.url.pathname;
4446
</nav>
4547

4648
<div class="header__locale font-mono">
47-
<span class="header__locale-active">{t(locale, "nav.locale.en")}</span>
49+
{locale === "en" ? (
50+
<span class="header__locale-active">{t(locale, "nav.locale.en")}</span>
51+
) : (
52+
<a href={enHref} class="header__locale-link">{t(locale, "nav.locale.en")}</a>
53+
)}
4854
<span class="header__locale-separator">/</span>
49-
<span class="header__locale-disabled" title="Coming soon">{t(locale, "nav.locale.zh")}</span>
55+
{locale === "zh" ? (
56+
<span class="header__locale-active">{t(locale, "nav.locale.zh")}</span>
57+
) : (
58+
<a href={zhHref} class="header__locale-link">{t(locale, "nav.locale.zh")}</a>
59+
)}
5060
</div>
5161
</div>
5262
</header>
@@ -117,9 +127,15 @@ const currentPath = Astro.url.pathname;
117127
color: var(--c-gray-300);
118128
}
119129

120-
.header__locale-disabled {
121-
color: var(--c-gray-300);
122-
cursor: default;
130+
.header__locale-link {
131+
color: var(--c-gray-400);
132+
text-decoration: none;
133+
transition: color var(--transition-fast);
134+
}
135+
136+
.header__locale-link:hover {
137+
color: var(--c-ink);
138+
opacity: 1;
123139
}
124140

125141
@media (max-width: 640px) {

src/components/research/ResearchCard.astro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
import { type Locale } from "../../lib/i18n";
2+
import { localizedPath, type Locale } from "../../lib/i18n";
33
44
interface Props {
55
title: string;
@@ -11,7 +11,7 @@ interface Props {
1111
locale: Locale;
1212
}
1313
14-
const { title, slug, summary, publishDate, tags, game } = Astro.props;
14+
const { title, slug, summary, publishDate, tags, game, locale } = Astro.props;
1515
1616
const dateStr = publishDate.toLocaleDateString("en-US", {
1717
year: "numeric",
@@ -20,7 +20,7 @@ const dateStr = publishDate.toLocaleDateString("en-US", {
2020
});
2121
---
2222

23-
<a href={`/research/${slug}`} class="research-card">
23+
<a href={localizedPath(locale, `/research/${slug}`)} class="research-card">
2424
<div class="research-card__meta font-mono">
2525
<time datetime={publishDate.toISOString()}>{dateStr}</time>
2626
{game && <span class="research-card__game"{game}</span>}

src/content/games/zh/splendor.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: "璀璨宝石"
3+
status: "released"
4+
summary: "一个宝石交易游戏引擎,配有启发式、强化学习和大语言模型求解器,并提供完整的技术报告。"
5+
repoUrl: "https://github.com/trace-studio/splendor"
6+
cover: "/assets/games/splendor.png"
7+
tags: ["桌游"]
8+
components: ["engine", "heuristic", "rl", "llm", "web-demo", "report"]
9+
locale: "zh"
10+
publishDate: 2025-12-01
11+
order: 1
12+
---
13+
14+
《璀璨宝石》是一款以卡牌为核心的策略游戏,玩家通过收集宝石来购买发展卡牌并吸引贵族赞助者。Trace Studio 的实现包含一个为高速模拟设计的高性能引擎,以及多种 AI 求解器。
15+
16+
## 引擎
17+
18+
引擎支持 2–4 人的完整游戏规则,具有高效的状态表示,便于树搜索。
19+
20+
## 求解器
21+
22+
- **启发式**:贪心宝石收集与卡牌购买策略。
23+
- **强化学习**:经过 1000 万局自我对弈训练的 PPO 智能体。
24+
- **大语言模型**:基于 GPT-4 和 Claude 的智能体,使用结构化提示。
25+
26+
## 结果
27+
28+
强化学习智能体在 2 人对局中对启发式基线的胜率达到 72%。
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: "《璀璨宝石》技术报告"
3+
game: "splendor"
4+
summary: "对桌游《璀璨宝石》的深入分析,包含游戏统计、求解器与策略。"
5+
authors: ["Trace Studio"]
6+
publishDate: 2026-04-20
7+
tags: ["桌游"]
8+
locale: "zh"
9+
draft: false
10+
---
11+
12+
本报告介绍了一种针对《璀璨宝石》的启发式求解器,使用以金币为权重的评估函数来指导决策。
13+
14+
## 方法
15+
16+
启发式将较高的价值分配给金币,因为金币作为万能牌具有灵活性。评估函数考虑以下因素:
17+
18+
1. **卡牌可得性**——玩家当前能够购买的卡牌数量
19+
2. **贵族接近度**——吸引贵族赞助者的距离
20+
3. **引擎价值**——已拥有发展卡牌的折扣价值
21+
22+
```python
23+
def evaluate(state, player):
24+
card_score = sum(affordable_cards(state, player))
25+
noble_score = noble_proximity(state, player) * 2.0
26+
engine_score = discount_value(state, player) * 1.5
27+
return card_score + noble_score + engine_score
28+
```
29+
30+
## 结果
31+
32+
| 对战 | 胜率 | 局数 |
33+
|---------|----------|-------|
34+
| 金币启发式 vs 随机 | 95.2% | 10,000 |
35+
| 金币启发式 vs 贪心 | 68.4% | 10,000 |
36+
| 金币启发式 vs 金币启发式 | 50.1% | 10,000 |
37+
38+
> 以金币为权重的启发式提供了一个强基线,在计算开销很小的同时显著优于朴素策略。
39+
40+
## 结论
41+
42+
金币优先是《璀璨宝石》中一种有效的启发式锚点。该方法可作为后续强化学习和大语言模型实验的基线。

0 commit comments

Comments
 (0)