From 0602fc9a38dcc3ba46e775b82ca91d7c231e7a7f Mon Sep 17 00:00:00 2001
From: smilelsb
Date: Wed, 21 Oct 2026 21:02:08 +0800
Subject: [PATCH 1/2] =?UTF-8?q?feat(i18n):=20add=20Simplified=20Chinese=20?=
=?UTF-8?q?Stage=200=20localization=20/=20=E6=B7=BB=E5=8A=A0=E9=98=B6?=
=?UTF-8?q?=E6=AE=B5=200=20=E7=AE=80=E4=BD=93=E4=B8=AD=E6=96=87=E6=9C=AC?=
=?UTF-8?q?=E5=9C=B0=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add zh-CN UI strings, navigation, glossary entries, and translated Learning Course pages through Stage 0.
添加 zh-CN 界面文案、导航、术语表,以及截至阶段 0 的学习课程译文。
---
src/config/locales.ts | 25 +-
src/config/sidebarConfig.ts | 209 ++++++++--
src/content/docs/zh-cn/index.mdx | 43 ++
.../getting-started/forking-and-cloning.mdx | 25 ++
.../getting-started/required-tools.mdx | 47 +++
.../getting-started/vscode-overview.mdx | 145 +++++++
.../getting-started/website-feature-guide.mdx | 87 +++++
.../docs/zh-cn/learning-course/index.mdx | 109 ++++++
.../stage0/additional-resources.mdx | 32 ++
.../zh-cn/learning-course/stage0/arrays.mdx | 178 +++++++++
.../stage0/classes-methods.mdx | 215 ++++++++++
.../stage0/classes-objects.mdx | 201 ++++++++++
.../learning-course/stage0/conditionals.mdx | 139 +++++++
.../learning-course/stage0/debugging.mdx | 167 ++++++++
.../docs/zh-cn/learning-course/stage0/git.mdx | 57 +++
.../stage0/interfaces-lists.mdx | 245 ++++++++++++
.../stage0/java-fundamentals.mdx | 278 +++++++++++++
.../zh-cn/learning-course/stage0/loops.mdx | 169 ++++++++
.../learning-course/stage0/operators.mdx | 366 ++++++++++++++++++
.../learning-course/stage0/stage-overview.mdx | 23 ++
.../learning-course/stage0/stage-wrap-up.mdx | 17 +
src/content/docs/zh-cn/resources/glossary.mdx | 7 +
src/content/i18n/zh-CN.json | 44 +++
src/data/glossary.zh-cn.yaml | 75 ++++
24 files changed, 2872 insertions(+), 31 deletions(-)
create mode 100644 src/content/docs/zh-cn/index.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/getting-started/required-tools.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/getting-started/vscode-overview.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/getting-started/website-feature-guide.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/index.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/additional-resources.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/arrays.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/classes-methods.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/classes-objects.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/conditionals.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/debugging.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/git.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/interfaces-lists.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/java-fundamentals.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/loops.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/operators.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/stage-overview.mdx
create mode 100644 src/content/docs/zh-cn/learning-course/stage0/stage-wrap-up.mdx
create mode 100644 src/content/docs/zh-cn/resources/glossary.mdx
create mode 100644 src/content/i18n/zh-CN.json
create mode 100644 src/data/glossary.zh-cn.yaml
diff --git a/src/config/locales.ts b/src/config/locales.ts
index 189471de..31f32e32 100644
--- a/src/config/locales.ts
+++ b/src/config/locales.ts
@@ -4,9 +4,12 @@
export const defaultLang = 'en';
// adding a new locale requires adding a directory under src/content/docs/ and
-// adding an entry here. The directory name must match `lang`.
+// adding an entry here. The directory name must match the locale key; `lang`
+// is the BCP-47 tag written to the page. Starlight lowercases route IDs, so
+// region-tagged locales should use a lowercase key such as `zh-cn`.
const configuredLocales: Record = {
root: { label: 'English', lang: defaultLang },
+ 'zh-cn': { label: '简体中文', lang: 'zh-CN' },
};
export const langByLocaleDir: Record = Object.fromEntries(
@@ -61,14 +64,20 @@ function isPageUrl(url: string): boolean {
* Prefixes a root-relative page URL with a locale directory. Anything already
* in the locale, external, or pointing at public/ is returned unchanged.
*
- * `localeDir` doubles as the BCP-47 tag, so `Astro.currentLocale` can be passed
- * straight in.
+ * Accepts either a locale directory (such as `zh-cn`) or its BCP-47 language
+ * tag (such as `zh-CN`), so `Astro.currentLocale` can be passed straight in.
*/
-export function localizeHref(
- href: string,
- localeDir: string | undefined,
-): string {
- if (!localeDir || !(localeDir in langByLocaleDir)) return href;
+export function localizeHref(href: string, locale: string | undefined): string {
+ if (!locale) return href;
+
+ const localeDir =
+ locale in langByLocaleDir
+ ? locale
+ : Object.entries(langByLocaleDir).find(
+ ([, lang]) => lang === locale,
+ )?.[0];
+
+ if (!localeDir) return href;
if (!isPageUrl(href)) return href;
const [path = ''] = href.split(/[?#]/);
diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts
index 08fdee28..5b42bba2 100644
--- a/src/config/sidebarConfig.ts
+++ b/src/config/sidebarConfig.ts
@@ -31,190 +31,259 @@ export type SidebarItem = {
const learningCourse: SidebarTopic = {
label: 'Learning Course',
+ translations: { 'zh-CN': '学习课程' },
id: 'learning-course',
link: '/learning-course/',
icon: 'notes',
items: [
- { label: 'Overview', slug: 'learning-course' },
+ {
+ label: 'Overview',
+ translations: { 'zh-CN': '概览' },
+ slug: 'learning-course',
+ },
{
label: 'Website Feature Guide',
+ translations: { 'zh-CN': '网站功能指南' },
slug: 'learning-course/getting-started/website-feature-guide',
},
{
label: 'Course Setup',
+ translations: { 'zh-CN': '课程准备' },
collapsed: true,
items: [
{
label: 'Required Tools',
+ translations: { 'zh-CN': '开发必备工具' },
slug: 'learning-course/getting-started/required-tools',
},
{
label: 'VS Code Overview',
+ translations: { 'zh-CN': 'VS Code 概述' },
slug: 'learning-course/getting-started/vscode-overview',
},
{
label: 'Forking and Cloning',
+ translations: { 'zh-CN': '派生仓库与克隆仓库' },
slug: 'learning-course/getting-started/forking-and-cloning',
},
],
},
{
label: 'Stage 0',
+ translations: { 'zh-CN': '阶段 0' },
collapsed: true,
items: [
{
label: 'Stage 0 Overview',
+ translations: { 'zh-CN': '阶段 0 概览' },
slug: 'learning-course/stage0/stage-overview',
},
{
label: 'Java Fundamentals',
+ translations: { 'zh-CN': 'Java基础' },
slug: 'learning-course/stage0/java-fundamentals',
},
{
label: 'Debugging',
+ translations: { 'zh-CN': '调试' },
slug: 'learning-course/stage0/debugging',
},
{
label: 'Git - Uploading code',
+ translations: { 'zh-CN': 'Git - 上传代码' },
slug: 'learning-course/stage0/git',
},
{
label: 'Operators',
+ translations: { 'zh-CN': '运算符' },
slug: 'learning-course/stage0/operators',
},
{
label: 'Conditionals',
+ translations: { 'zh-CN': '条件语句' },
slug: 'learning-course/stage0/conditionals',
},
- { label: 'Loops', slug: 'learning-course/stage0/loops' },
+ {
+ label: 'Loops',
+ translations: { 'zh-CN': '循环' },
+ slug: 'learning-course/stage0/loops',
+ },
{
label: 'Classes and Objects',
+ translations: { 'zh-CN': '类与对象' },
slug: 'learning-course/stage0/classes-objects',
},
{
label: 'Methods and Mutable State',
+ translations: {
+ 'zh-CN': '方法与可变状态',
+ },
slug: 'learning-course/stage0/classes-methods',
},
{
label: 'Arrays and For-Each Loops',
+ translations: {
+ 'zh-CN': '数组与 for-each 循环',
+ },
slug: 'learning-course/stage0/arrays',
},
{
label: 'Interfaces, Generics, and Lists',
+ translations: {
+ 'zh-CN': '接口、泛型与列表',
+ },
slug: 'learning-course/stage0/interfaces-lists',
},
{
label: 'Additional Resources',
+ translations: { 'zh-CN': '补充资源' },
slug: 'learning-course/stage0/additional-resources',
},
{
label: 'Stage 0 Wrap-Up',
+ translations: { 'zh-CN': '阶段 0 总结' },
slug: 'learning-course/stage0/stage-wrap-up',
},
],
},
{
label: 'Stage 1',
+ translations: { 'zh-CN': '阶段 1' },
collapsed: true,
items: [
{
label: 'Stage 1 Overview',
+ translations: { 'zh-CN': '阶段 1 概览' },
slug: 'learning-course/stage1/stage-overview',
},
{
label: 'Stage 1A: Kitbot Intro',
+ translations: { 'zh-CN': '阶段 1A:Kitbot 入门' },
collapsed: true,
items: [
{
label: 'Stage 1A Overview',
+ translations: { 'zh-CN': '阶段 1A 概览' },
slug: 'learning-course/stage1/stage1a/stage-overview',
},
{
label: 'Getting Started',
+ translations: { 'zh-CN': '入门' },
slug: 'learning-course/stage1/stage1a/getting-started',
},
{
label: 'Kitbot Drivetrain',
+ translations: { 'zh-CN': 'Kitbot 传动系统' },
slug: 'learning-course/stage1/stage1a/kitbot-drivetrain',
},
{
label: 'Drivetrain Simulation',
+ translations: { 'zh-CN': '传动系统仿真' },
slug: 'learning-course/stage1/stage1a/drivetrain-sim',
},
{
label: 'Simple Auto',
+ translations: { 'zh-CN': '简单自动程序' },
slug: 'learning-course/stage1/stage1a/simple-auto',
},
{
label: 'Additional Motors',
+ translations: { 'zh-CN': '额外电机' },
slug: 'learning-course/stage1/stage1a/kitbot-additional-motors',
},
{
label: 'Stage 1A Wrap-Up',
+ translations: { 'zh-CN': '阶段 1A 总结' },
slug: 'learning-course/stage1/stage1a/stage-wrap-up',
},
],
},
{
label: 'Stage 1B: Commands',
+ translations: { 'zh-CN': '阶段 1B:命令' },
collapsed: true,
items: [
{
label: 'Stage 1B Overview',
+ translations: { 'zh-CN': '阶段 1B 概览' },
slug: 'learning-course/stage1/stage1b/stage-overview',
},
{
label: 'The Concepts',
+ translations: { 'zh-CN': '核心概念' },
slug: 'learning-course/stage1/stage1b/command-based-overview',
},
{
label: 'The Body of a Command',
+ translations: { 'zh-CN': '命令的主体' },
slug: 'learning-course/stage1/stage1b/the-command-body',
},
{
label: 'Commands & Mechanisms, Pt. 1',
+ translations: { 'zh-CN': '命令与机构(第一部分)' },
slug: 'learning-course/stage1/stage1b/commands-and-mechanisms',
},
{
label: 'Triggers and Scheduling',
+ translations: { 'zh-CN': '触发器与调度' },
slug: 'learning-course/stage1/stage1b/triggers',
},
{
label: 'Commands & Mechanisms, Pt. 2',
+ translations: { 'zh-CN': '命令与机构(第二部分)' },
slug: 'learning-course/stage1/stage1b/commands-and-mechanisms-pt2',
},
{
label: 'Bonus: Spot the Error',
+ translations: {
+ 'zh-CN': '拓展练习:找出代码错误(第一部分)',
+ },
slug: 'learning-course/stage1/stage1b/spot-the-error',
},
{
label: 'Exercise - Kitbot Rewrite, Pt. 1',
+ translations: {
+ 'zh-CN': '练习:Kitbot 代码重构(第一部分)',
+ },
slug: 'learning-course/stage1/stage1b/command-based-kitbot',
},
{
label: 'Suppliers in Command-Based',
+ translations: {
+ 'zh-CN':
+ '基于命令的编程中的 Supplier(供给器)',
+ },
slug: 'learning-course/stage1/stage1b/suppliers-in-command-based',
},
{
label: 'Exercise - Kitbot Rewrite, Pt. 2',
+ translations: {
+ 'zh-CN': '练习:Kitbot 代码重构(第二部分)',
+ },
slug: 'learning-course/stage1/stage1b/command-based-kitbot-pt2',
},
{
label: 'Bonus: Spot the Error, Pt 2',
+ translations: {
+ 'zh-CN': '附加:找出错误(第二部分)',
+ },
slug: 'learning-course/stage1/stage1b/spot-the-error-pt2',
},
{
label: 'Stage 1B Wrap-Up',
+ translations: { 'zh-CN': '阶段 1B 总结' },
slug: 'learning-course/stage1/stage1b/stage-wrap-up',
},
],
},
{
label: 'Stage 1C: Control and Telemetry',
+ translations: { 'zh-CN': '阶段 1C:控制与遥测' },
collapsed: true,
items: [
{
label: 'Stage 1C Introduction',
+ translations: { 'zh-CN': '阶段 1C 介绍' },
slug: 'learning-course/stage1/stage1c/stage-overview',
},
],
@@ -223,10 +292,12 @@ const learningCourse: SidebarTopic = {
},
{
label: 'Stage 2',
+ translations: { 'zh-CN': '阶段 2' },
collapsed: true,
items: [
{
label: 'Stage 2 Overview',
+ translations: { 'zh-CN': '阶段 2 概览' },
slug: 'learning-course/stage2/stage-overview',
},
],
@@ -236,76 +307,168 @@ const learningCourse: SidebarTopic = {
const educatorsGuide: SidebarTopic = {
label: "Educator's Guide",
+ translations: { 'zh-CN': '教师指南' },
id: 'educators-guide',
link: '/educators-guide/introduction/',
icon: 'open-book',
items: [
- { label: 'Introduction', slug: 'educators-guide/introduction' },
+ {
+ label: 'Introduction',
+ translations: { 'zh-CN': '介绍' },
+ slug: 'educators-guide/introduction',
+ },
{
label: 'The Stages',
+ translations: { 'zh-CN': '各个阶段' },
slug: 'educators-guide/introduction/the-stages',
},
{
label: 'Preparing Yourself',
+ translations: { 'zh-CN': '做好准备' },
slug: 'educators-guide/introduction/preparation',
},
- { label: 'Stage 0', slug: 'educators-guide/stage0' },
+ {
+ label: 'Stage 0',
+ translations: { 'zh-CN': '阶段 0' },
+ slug: 'educators-guide/stage0',
+ },
{
label: 'Stage 1',
+ translations: { 'zh-CN': '阶段 1' },
collapsed: true,
items: [
- { label: 'Overview', slug: 'educators-guide/stage1' },
- { label: 'Stage 1A', slug: 'educators-guide/stage1/stage1a' },
- { label: 'Stage 1B', slug: 'educators-guide/stage1/stage1b' },
- { label: 'Stage 1C', slug: 'educators-guide/stage1/stage1c' },
+ {
+ label: 'Overview',
+ translations: { 'zh-CN': '概览' },
+ slug: 'educators-guide/stage1',
+ },
+ {
+ label: 'Stage 1A',
+ translations: { 'zh-CN': '阶段 1A' },
+ slug: 'educators-guide/stage1/stage1a',
+ },
+ {
+ label: 'Stage 1B',
+ translations: { 'zh-CN': '阶段 1B' },
+ slug: 'educators-guide/stage1/stage1b',
+ },
+ {
+ label: 'Stage 1C',
+ translations: { 'zh-CN': '阶段 1C' },
+ slug: 'educators-guide/stage1/stage1c',
+ },
],
},
- { label: 'Stage 2', slug: 'educators-guide/stage2' },
+ {
+ label: 'Stage 2',
+ translations: { 'zh-CN': '阶段 2' },
+ slug: 'educators-guide/stage2',
+ },
],
};
const bestPractices: SidebarTopic = {
label: 'Best Practices',
+ translations: { 'zh-CN': '最佳实践' },
id: 'best-practices',
link: '/best-practices/overview/',
icon: 'approve-check-circle',
items: [
- { label: 'Overview', slug: 'best-practices/overview' },
- { label: 'Git Usage', slug: 'best-practices/git-usage' },
- { label: 'GitHub Usage', slug: 'best-practices/github-usage' },
- { label: 'Code Formatter', slug: 'best-practices/code-formatter' },
- { label: 'CI Checks', slug: 'best-practices/ci-checks' },
+ {
+ label: 'Overview',
+ translations: { 'zh-CN': '概览' },
+ slug: 'best-practices/overview',
+ },
+ {
+ label: 'Git Usage',
+ translations: { 'zh-CN': 'Git 使用' },
+ slug: 'best-practices/git-usage',
+ },
+ {
+ label: 'GitHub Usage',
+ translations: { 'zh-CN': 'GitHub 使用' },
+ slug: 'best-practices/github-usage',
+ },
+ {
+ label: 'Code Formatter',
+ translations: { 'zh-CN': '代码格式化' },
+ slug: 'best-practices/code-formatter',
+ },
+ {
+ label: 'CI Checks',
+ translations: { 'zh-CN': 'CI 检查' },
+ slug: 'best-practices/ci-checks',
+ },
],
};
const resources: SidebarTopic = {
label: 'Resources',
+ translations: { 'zh-CN': '资源' },
id: 'resources',
link: '/resources/overview/',
icon: 'document',
items: [
- { label: 'Overview', slug: 'resources/overview' },
- { label: 'Examples', slug: 'resources/examples' },
- { label: 'Documentation', slug: 'resources/docs' },
- { label: 'Hardware', slug: 'resources/hardware-intro' },
- { label: 'Glossary', slug: 'resources/glossary' },
+ {
+ label: 'Overview',
+ translations: { 'zh-CN': '概览' },
+ slug: 'resources/overview',
+ },
+ {
+ label: 'Examples',
+ translations: { 'zh-CN': '示例' },
+ slug: 'resources/examples',
+ },
+ {
+ label: 'Documentation',
+ translations: { 'zh-CN': '文档' },
+ slug: 'resources/docs',
+ },
+ {
+ label: 'Hardware',
+ translations: { 'zh-CN': '硬件' },
+ slug: 'resources/hardware-intro',
+ },
+ {
+ label: 'Glossary',
+ translations: { 'zh-CN': '术语表' },
+ slug: 'resources/glossary',
+ },
],
};
const contribution: SidebarTopic = {
label: 'Contribution',
+ translations: { 'zh-CN': '贡献' },
id: 'contribution',
link: '/contribution/',
icon: 'code-branch',
items: [
- { label: 'Overview', slug: 'contribution' },
+ {
+ label: 'Overview',
+ translations: { 'zh-CN': '概览' },
+ slug: 'contribution',
+ },
{
label: 'Methods of Contributing',
+ translations: { 'zh-CN': '贡献方式' },
slug: 'contribution/methodsofcontributing',
},
- { label: 'Style Guide', slug: 'contribution/styleguide' },
- { label: 'Contributors', slug: 'contribution/contributors' },
- { label: 'Roadmap', slug: 'contribution/roadmap' },
+ {
+ label: 'Style Guide',
+ translations: { 'zh-CN': '风格指南' },
+ slug: 'contribution/styleguide',
+ },
+ {
+ label: 'Contributors',
+ translations: { 'zh-CN': '贡献者' },
+ slug: 'contribution/contributors',
+ },
+ {
+ label: 'Roadmap',
+ translations: { 'zh-CN': '路线图' },
+ slug: 'contribution/roadmap',
+ },
],
};
diff --git a/src/content/docs/zh-cn/index.mdx b/src/content/docs/zh-cn/index.mdx
new file mode 100644
index 00000000..3a359997
--- /dev/null
+++ b/src/content/docs/zh-cn/index.mdx
@@ -0,0 +1,43 @@
+---
+title: 欢迎来到 FRCSoftware.org
+description: FRC 编程综合学习指南
+template: splash
+hero:
+ tagline: FRC 编程综合学习指南
+ image:
+ html: '
'
+ actions:
+ - text: 开始学习
+ link: /learning-course/
+---
+
+import HomeCard from '../../../components/HomeCard.astro';
+
+
+
+
+
+
+
+
+
diff --git a/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx b/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx
new file mode 100644
index 00000000..d6fe690c
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx
@@ -0,0 +1,25 @@
+---
+title: 派生仓库与克隆仓库
+description: 介绍如何派生并克隆用于个人工作的 GitHub 仓库
+---
+
+import ContentFigure from '@components/ContentFigure.astro';
+
+## Forking(派生仓库)
+
+开始之前,需要为相关的 repository(仓库)创建自己的 fork(派生仓库)。
+在此之前,需要先按照“必备工具”部分的说明完成准备工作。
+要创建 fork,请访问需要复制的仓库(例如 https://github.com/frcsoftware/Stage0-Template),然后点击右上角的“Fork”按钮。
+这会在自己的 GitHub 账户下创建该仓库的副本,以便在不影响原仓库的情况下进行修改。
+
+
+
+## Cloning(克隆)
+
+要 clone(克隆)刚刚创建的 repository(仓库),请打开 VS Code。
+然后复制刚刚派生的仓库 URL。
+在 VS Code 中打开左上角的 Source Control(源代码管理)侧栏,然后选择“Clone Repository”(克隆仓库)。
+粘贴派生仓库的 URL,并选择用于存放该仓库的本地目录。
+这会在计算机上创建仓库的本地副本,以便处理其中的代码。
+
+
diff --git a/src/content/docs/zh-cn/learning-course/getting-started/required-tools.mdx b/src/content/docs/zh-cn/learning-course/getting-started/required-tools.mdx
new file mode 100644
index 00000000..cace84cb
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/getting-started/required-tools.mdx
@@ -0,0 +1,47 @@
+---
+title: 开发必备工具
+description: 安装完成本课程和参与 FRC 编程所需的全部软件
+---
+
+要为 FRC 机器人编程并跟随本网站学习,需要安装 WPILib 工具和 Git,并注册 GitHub 账户。
+
+## WPILib 工具
+
+首先需要安装 WPILib 工具包。
+WPILib 工具包包含特定版本的 VS Code,队伍可以使用它编写代码,并将代码 deploy(部署)到机器人。
+
+本网站要求使用 2027 赛季 WPILib 的 alpha 版本。
+下载链接请参阅[此处](https://github.com/wpilibsuite/allwpilib/releases)。
+
+
+
+下载 2027 WPILib 工具后,可以按照[此处](https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-2/wpilib-setup.html)的说明完成安装。
+
+WPILib 工具包还包含适用于 data logging(数据日志记录)、Simulation(仿真)、仪表盘等用途的程序。
+后续阶段将使用其中的一部分工具。
+
+### Driver Station(操控站)
+
+如果需要控制实体机器人,可能还需要安装 FRC Game Tools(FRC 比赛工具包)。
+该工具包会安装 Driver Station,这是一款连接并控制机器人的程序。
+安装说明请参阅[此处](https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-2/frc-game-tools.html),但完成本课程并不要求安装它。
+
+## Git
+
+接下来需要安装 Git。
+Git 是 FRC 中常用的版本控制系统,可以帮助队伍在机器人制作和比赛赛季期间跟踪代码变更。
+队伍还可以使用 GitHub 提供的 Pull Requests(合并请求)、Issues(议题)和项目看板等功能管理机器人代码。
+Git 的安装说明请参阅[此处](https://git-scm.com/install/)。
+
+## 创建 GitHub 账户
+
+GitHub 是托管 Git repositories(仓库)并支持代码协作的网站。
+GitHub 不仅免费且易于使用,还是行业标准,因此在 FRC 中得到广泛应用。
+
+本网站的许多课程都需要使用 GitHub 账户。
+可以在[此处](https://github.com/signup)注册账户。
+还建议申请 [GitHub Student Developer Pack(GitHub 学生开发者包)](https://education.github.com/pack),以获取各种资源。
diff --git a/src/content/docs/zh-cn/learning-course/getting-started/vscode-overview.mdx b/src/content/docs/zh-cn/learning-course/getting-started/vscode-overview.mdx
new file mode 100644
index 00000000..199ebba6
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/getting-started/vscode-overview.mdx
@@ -0,0 +1,145 @@
+---
+title: VS Code 概述
+description: 介绍使用 WPILib 时所需的 VS Code 程序及其界面
+---
+
+## 什么是 VS Code
+
+Visual Studio Code(VS Code)是 Microsoft 开发的一款免费开源代码编辑器。
+它支持 Windows、macOS 和 Linux,并可通过 Extension Marketplace(扩展市场)中的扩展支持几乎所有编程语言。
+它还集成了 Git,可以直接在编辑器的 Source Control(源代码管理)面板中进行暂存、commit(提交)、push(推送)、pull(拉取)和查看 diff(差异)等操作。
+
+在之前的页面中,你安装了包含 WPILib VS Code 的 WPILib 工具。
+在 FRC 中,应当使用这个版本的 VS Code。
+WPILib VS Code 会随其他 WPILib 工具一起安装,并作为单独的应用程序运行。
+如果之前安装过普通版 VS Code,请确保使用 WPILib 专用版本为机器人编程。
+
+WPILib VS Code 与普通版 VS Code 使用不同的图标。
+如下图所示,WPILib VS Code 使用 WPILib 标志,而普通版 VS Code 使用蓝色标志。
+
+
+
+
+
+## 欢迎界面
+
+首次打开 WPILib VS Code 时,会显示包含多个选项的欢迎界面。
+在这里选择 `File`,即可打开文件菜单。
+菜单中有许多选项,其中最常用的是:
+
+1. `New file`
+2. `Open file`
+3. `Open folder`
+
+
+
+
+
+
+
+## 浏览界面
+
+VS Code 的布局可以划分为几个主要区域:
+
+1. Activity Bar(活动栏)
+ 位于最左侧,由一列垂直图标组成,用于切换主要视图,包括
+ Explorer(资源管理器)、Search(搜索)、Source Control(源代码管理)、Run &
+ Debug(运行和调试)以及 Extensions(扩展)。
+2. Side Bar(侧栏)
+ 位于活动栏旁边,用于显示当前所选视图的详细内容,例如 Explorer 中的文件树。
+3. Panel(面板)位于屏幕底部。 它包含
+ Terminal(终端)、Problems(问题)、Output(输出)和 Debug
+ Console(调试控制台)四个主要视图。
+4. Editor(编辑器)位于中央区域。
+ 编辑器是主要工作区域,可以在其中查看和编辑文件。
+5. Command Palette(命令面板)可通过 `Ctrl+Shift+P` /
+ `Cmd+Shift+P` 打开,也可通过顶部工具栏中的 WPILib 按钮打开,用于搜索和运行 VS
+ Code 中的任何命令。
+
+
+
+### WPILib Command Palette(WPILib 命令面板)
+
+WPILib VS Code 的一项重要功能是提供 FRC 专用命令,例如:Build(构建),用于编译机器人代码并检查设备上的错误;Deploy(部署),用于构建代码并通过与机器人的网络连接将代码发送到 Systemcore;以及 Simulate(仿真),用于通过内置仿真工具在计算机上测试代码。
+这些命令都可以通过 Command Palette(命令面板)运行。
+
+
+
+## 保存和打开文件
+
+### 打开文件
+
+- 打开单个文件:`File > Open File...` 或 `Ctrl+O` / `Cmd+O`
+- 打开文件夹或项目:`File > Open Folder...` 或 `Ctrl+K Ctrl+O` / `Cmd+K Cmd+O`
+- 快速打开文件:按 `Ctrl+P` / `Cmd+P`,然后开始输入文件名
+- 重新打开最近关闭的文件:`Ctrl+Shift+T` / `Cmd+Shift+T`
+
+### 保存文件
+
+- 保存当前文件:`Ctrl+S` / `Cmd+S`
+- 另存为新的名称或位置:`Ctrl+Shift+S` / `Cmd+Shift+S`
+- 保存所有已打开的文件:`Ctrl+K S` / `Cmd+K S`
+
+
+
+文件标签页上的圆点表示存在**未保存的更改**。
+保存文件后,圆点会消失并变为“X”关闭图标。
+
+
+
+## 键盘快捷键
+
+VS Code 提供了许多实用的键盘快捷键。
+
+- [Windows 键盘快捷键](https://code.visualstudio.com/shortcuts/keyboard-shortcuts-windows.pdf)
+- [MacOS 键盘快捷键](https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf)
+
+其中最常用的快捷键包括:
+
+- `Ctrl+P` / `Cmd+P`:打开 Quick Open(快速打开),可以按名称打开任意文件
+- `Ctrl+Shift+P` / `Cmd+Shift+P`:打开 Command Palette(命令面板)
+- `` Ctrl+` ``:快速打开或关闭集成终端
+- `Ctrl+B` / `Cmd+B`:切换侧栏的显示状态
+- `Ctrl+Tab`:在已打开的编辑器之间切换
+
+
diff --git a/src/content/docs/zh-cn/learning-course/getting-started/website-feature-guide.mdx b/src/content/docs/zh-cn/learning-course/getting-started/website-feature-guide.mdx
new file mode 100644
index 00000000..bdbf6207
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/getting-started/website-feature-guide.mdx
@@ -0,0 +1,87 @@
+---
+title: 网站功能指南
+description: 了解 FRCSoftware 的各项功能,以便高效浏览内容
+---
+
+import { REV_CTRE_CHOOSER_KEY } from '@data/tabsSyncKeys.ts';
+import { TabItem, Tabs } from '@astrojs/starlight/components';
+
+在开始学习之前,需要先了解网站中的一些功能,否则可能难以高效地完成课程。
+下面通过示例介绍这些功能,帮助你在浏览网站前快速掌握它们。
+
+## 全站功能
+
+- [链接](/learning-course/)以紫色文字显示。
+- 点击图片可将其放大,并在适用时显示说明文字。
+ 按 `Esc` 键可再次缩小图片。
+- 网站提供与课程内容相关的[术语表](/resources/glossary/)。
+ 术语表中的词语在网站中出现时会带有下划线。
+ 将鼠标悬停在这些带下划线的词语上,即可快速查看定义(例如 PDH、SPARK MAX)。
+- 部分信息隐藏在下拉框中。
+ 请尽量查看其中的大部分内容!
+
+
+
+
+
+FRCSoftware 主要介绍针对不同电机编写代码的两种方式。
+在后续阶段中,将使用标签页来区分这些差异。
+要查看基于 REV 电机的代码,请点击 REV 标签页。
+要查看基于 CTRE 电机的代码,请点击 CTRE 标签页。
+
+
+ 这里将显示 CTRE 代码
+ 这里将显示 REV 代码
+
+
+### 提示框
+
+
+
+
+
+
+
+
+
+
+
+## 学习课程
+
+### 幻灯片
+
+
+ 点击两侧的箭头浏览幻灯片。
+
+ 说明文字下方的圆点表示当前所在的幻灯片。
+也可以点击这些圆点切换幻灯片。
+
+
+
+
+
+
diff --git a/src/content/docs/zh-cn/learning-course/index.mdx b/src/content/docs/zh-cn/learning-course/index.mdx
new file mode 100644
index 00000000..d01975c1
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/index.mdx
@@ -0,0 +1,109 @@
+---
+title: FRCSoftware 学习课程
+description: 学习 FRC 编程的引导式课程
+---
+
+import CourseSection from '@components/CourseSection.astro';
+
+欢迎参加 FRCSoftware 学习课程!
+本课程旨在帮助不同技能水平的学生学习如何为 FRC 机器人编程。
+课程从学习编写 Java 开始,逐步进阶到为复杂度不断提高的机器人编程。
+我们会先以 FRC Kitbot 为例学习机器人的单个机构,再学习更复杂的机器人。
+
+FRC 软件一直在不断变化!
+如果你有反馈或建议,欢迎通过 [GitHub](https://github.com/frcsoftware/frcsoftware.org)
+或 [Discord](https://discord.com/invite/uUugPrPZFs) 与我们交流。
+
+
+
+
+
+ 课程准备介绍如何下载完成本学习课程所需的工具,以及如何浏览和使用这些工具。如果你刚开始学习 FRC 编程,可以从这里入手。
+
+ - 必备工具:下载本学习课程所需的工具。
+ - VS Code 概述:进一步了解本课程使用的代码编辑器 VS Code。
+ - 派生仓库与克隆仓库:学习如何使用 Git 进行 fork(派生仓库)和 clone(克隆)。这些操作对于获取课程中使用的模板十分重要。
+
+
+
+
+
+
+
+ 阶段 0 介绍为简单 FRC 机器人 Kitbot 编程所需的 Java
+ 基础知识,其中包括:
+
+ - Java基础
+ - Operators(运算符)
+ - Conditionals(条件语句)
+ - Loops(循环)
+ - Classes and Objects(类与对象)
+ - Methods and Mutable State(方法与可变状态)
+ - Arrays and For-Each Loops(数组与 for-each 循环)
+ - Interfaces, Generics, and Lists(接口、泛型与列表)
+
+
+
+
+
+
+
+ 阶段 1 将开始为 Kitbot 编程。Kitbot 是一种常用于学习 FRC 基础技能的入门机器人。
+
+
+
+
+ -
+ 阶段 1A:使用 `Robot` 类为 Kitbot 编程,学习 FRC 编程的基础技能。
+
+ -
+ 阶段 1B:在 1A 编写的代码中加入 Command-Based Programming(基于命令的编程)文件结构,学习如何为 FRC 机器人编程。
+
+ -
+ {' '}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/additional-resources.mdx b/src/content/docs/zh-cn/learning-course/stage0/additional-resources.mdx
new file mode 100644
index 00000000..d20c956a
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/additional-resources.mdx
@@ -0,0 +1,32 @@
+---
+title: Additional Resources(补充资源)
+description: 用于继续学习的可选资源
+---
+
+你已经完成阶段 0。
+现在已经掌握了为第一台 FRC 机器人编程所需的 Java。
+
+正如[阶段 0 概览](/learning-course/stage0/stage-overview/)中所说,阶段 0 只涵盖基础知识,所以还有更多 Java 可以学习。
+本页列出用于继续学习 Java 的**可选**资源。
+这一页可以先跳过,以后再回来查看。
+
+## Java 课程
+
+- [Codecademy](https://www.codecademy.com/learn/learn-java):在浏览器里交互式学习 Java,并完成配套练习。
+- FIRST 的 Java 培训:FIRST 官方的 Java 编程培训。
+ - [第 1 部分](https://training.firstinspires.org/courses/java-programming-part-1)
+ - [第 2 部分](https://training.firstinspires.org/courses/java-programming-part-2)
+- [Mooc fi 的 Java 课程](https://java-programming.mooc.fi/):赫尔辛基大学的免费 Java 课程,包含大量编程练习。
+- [Java 的 Learn Java](https://dev.java/learn/):dev.java 上的 Java 入门学习路径。
+- [Oracle 的 Java 教程](https://docs.oracle.com/javase/tutorial/java/):Oracle 官方的 Java 语言和标准库教程。
+- [7237 Unibots,Java Basics](https://www.youtube.com/playlist?list=PLbyD6UONF3m7Tvp6mTluAtD8vSv2MeO8o):7237 队制作的 Java 基础视频系列。
+
+## Java 参考资料
+
+- [W3Schools](https://www.w3schools.com/java/):按主题查阅 Java 语法和短示例。
+- [GeeksForGeeks](https://www.geeksforgeeks.org/java/java/):Java 概念、语法和示例文章。
+
+## 练习
+
+- [Coding Bat](https://codingbat.com/java):针对基础逻辑的小型 Java 练习题。
+- [CodeWars](https://www.codewars.com/):按难度分级的编程挑战。
diff --git a/src/content/docs/zh-cn/learning-course/stage0/arrays.mdx b/src/content/docs/zh-cn/learning-course/stage0/arrays.mdx
new file mode 100644
index 00000000..2482e2f3
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/arrays.mdx
@@ -0,0 +1,178 @@
+---
+title: Arrays and For-Each Loops(数组与 for-each 循环)
+description: 介绍 Java 中的数组和 for-each 循环
+codeRegionSources:
+ default: stage0/snippets/src/Arrays.java
+---
+
+在[上一课](/learning-course/stage0/classes-methods/)中,我们定义了 `Point` 类,用来表示机器人在赛场上的位置。
+本节课我们将学习 arrays(数组)。数组可以把多个相同类型的值存放在一起。
+同时学习 for-each loop(for-each 循环)。它用来遍历这些值,相比你已经学过的 `for` 循环,代码通常会更加简洁。
+
+## 为什么使用数组?
+
+假设 drivetrain(底盘驱动系统)有四台电机,你想记录每台电机设定的速度。
+如果没有数组,就需要为每台电机单独创建一个变量:
+
+{/* rli:ignore */}
+
+```java
+double motor1Speed = 0.5;
+double motor2Speed = 0.5;
+double motor3Speed = 0.5;
+double motor4Speed = 0.5;
+```
+
+只有四台电机时,这样写可以完成任务,但无法继续扩展。
+如果机器人有八台电机,就需要八个变量,而且任何要对“所有电机速度”进行操作的代码,都必须为每个变量重复写一次。
+**数组**把多个相同类型的值放在同一个变量里,从而解决这个问题。
+
+## 声明和创建数组
+
+数组类型的写法是元素类型后面加上方括号,例如 `double[]` 代表存放 `double` 类型数据的数组。
+创建数组最简单的方式是使用 array literal(数组字面量),也就是放在花括号中,用逗号分隔的一组元素:
+
+```java #motorSpeedsLiteral
+
+```
+
+这行代码创建了包含四个 `double` 数值的数组,并把它保存在变量 `motorSpeeds` 中。
+数组一旦创建,长度就固定不变,所以只要 `motorSpeeds` 存在,它就始终正好保存着四个元素。
+不过,数组中的元素的值可以随时更改。
+
+
+
+## 索引与长度
+
+数组中的每个值称为 element(元素)。访问特定元素时使用它的 index(索引),也就是该元素在数组中的位置。
+在 Java 中,数组索引从 `0` 开始,而不是从 `1` 开始。
+因此,在 `motorSpeeds` 中,第一台电机的速度位于索引 `0`,最后一台电机的速度位于索引 `3`。
+
+在数组变量名后面使用方括号来读取和写入元素。
+下面更新第一台电机的速度,并把它打印输出:
+
+```java #setSpeed
+
+```
+
+数组还有一个属性 `length`,它表示数组包含的元素数量。
+和 `String.length()` 方法不同,数组的 `length` 是一个成员变量(field),所以访问时不加圆括号:
+
+```java #speedsLength
+
+```
+
+
+
+## 对象数组
+
+数组的元素类型不限于 `double` 这样的基本类型,它可以是任何类型,包括你自己定义的类,例如 `Point`。
+把一条固定的自动路径定义成 waypoints(路径点)数组,数组中的元素顺序就是机器人应当依次经过它们的顺序:
+
+```java #pathArray
+
+```
+
+和 `motorSpeeds` 一样,`path` 的大小是固定的。这里是三个,因为创建时放入了三个路径点。
+这很适合自动路径:路线事先已经规划好,所以我们确切知道它有多少个路径点。
+
+
+
+
+
+## 遍历数组
+
+数组的用处在于可以遍历其中的元素,而不必逐个手写。
+前面已经学习过基于索引的 `for` 循环。下面用它把 `motorSpeeds` 中每个电机的速度加起来:
+
+```java #indexLoopSpeeds
+
+```
+
+循环条件 `i < motorSpeeds.length` 让这段代码可以适配任意长度的数组:
+无论 `motorSpeeds` 有4个元素还是40个元素,它都会在最后一个有效索引之后停止。
+
+## for-each 循环
+
+通常,这种循环只需要“对每个元素执行一段操作”,并不需要使用索引。
+Java 的 for-each 循环可以完成这个操作,不需要再单独管理索引变量:
+
+{/* rli:ignore */}
+
+```java
+for (ElementType element : array) {
+ // code that uses element
+}
+```
+
+冒号左边整体是「类型 + 局部变量名」。类型必须与数组的元素类型一致;类型后面的名称是一个新的局部变量,每次循环保存一个元素。冒号右边是待遍历的数组或集合。
+我们使用 for-each 循环来打印 `path` 中的每个路径点:
+
+```java #forEachPath
+
+```
+
+这比对应的基于索引的 for 循环更易于阅读和理解,因为不需要一直写 `path[i]`。
+`waypoint` 会按顺序依次变成 `path` 中的每个 `Point` 对象,每个元素对应一次循环。
+
+但有些场景必须用带索引的普通 for 循环,因为需要的不只是“当前元素”。
+例如,要计算路径的总距离,需要每个路径点以及它上一个路径点,而for-each 循环无法表达这一点:
+
+```java #pathLength
+
+```
+
+这里,`path[i - 1]` 是上一个路径点,`path[i]` 是当前路径点。`i` 从 `1` 开始,因为索引 `0` 没有前一个路径点可以比较。
+for-each 循环无法访问前一个元素,所以这里更适合使用基于索引的 `for` 循环。
+
+## 数组与 for-each 循环练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/classes-methods.mdx b/src/content/docs/zh-cn/learning-course/stage0/classes-methods.mdx
new file mode 100644
index 00000000..bad616b1
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/classes-methods.mdx
@@ -0,0 +1,215 @@
+---
+title: Methods and Mutable State(方法与可变状态)
+description: 编写定义类行为的方法,以及状态会随时间变化的类
+codeRegionSources:
+ point: stage0/snippets/src/classes-methods/Point.java
+ tracker: stage0/snippets/src/classes-methods/RobotTracker.java
+ usage: stage0/snippets/src/classes-methods/Usage.java
+---
+
+在[上一课](/learning-course/stage0/classes-objects/)中,我们编写了带有成员变量(field)、constructor(构造函数)和 getter(获取器)的 `Point` 类。
+它可以保存一对坐标,但不能对坐标进行任何计算。
+本节课我们将编写 methods(方法),用来定义类的行为,然后再实现一个状态会随时间变化的类。
+
+## 编写方法
+
+methods(方法)是定义在类内部的一段代码,用来定义这个类的行为。
+我们已经见过两个方法示例:`String` 类中的 `length()`,以及我们在 `Point` 上编写的 getter 方法。
+方法可以有参数,也就是调用方法时传入的值。
+
+方法执行结束后,可以向调用者返回值,这称为返回值。
+想要返回值,需要在方法体需要使用 `return` 关键字,后面跟上要返回的数据。
+一旦 `return` 语句被执行,方法会立刻停止运行,并把该值返回给调用者。
+例如,`String` 类中的 `length()` 方法会返回字符串的长度,并且它没有参数。
+
+方法的一般语法格式为:
+
+{/* rli:ignore */}
+
+```java
+public ReturnType methodName(ParameterType1 param1, ParameterType2 param2, ...) {
+ // method body
+}
+```
+
+一个方法可以拥有任意数量的参数,也可以没有参数,参数也可以是任何类型。
+`ReturnType` 代表方法返回值的类型,例如 `length()` 的返回类型是 `int`。
+方法不是必须返回数据;如果不需要返回任何内容,
+就使用返回类型 `void`。`void` 是一个关键字,表示方法不返回任何值。
+`void` 类型的方法也可以单独写 `return` 后面不带返回值,用来提前终止方法。
+不过这种写法并不常用,大多数情况下直接省略即可 —— 方法执行到末尾就会自动结束。
+
+接下来我们给 `Point` 类添加一些行为,先从 `plus` 方法开始。
+`plus` 方法接收另一个 `Point` 对象作为参数,
+并返回一个全新的 `Point` 对象,这个对象是两个 `Point` 对象之和。
+
+```java {point}#plus
+
+```
+
+这里的 `other` 只是我们给这个方法参数取的名字,代表我们要相加的另一个 `Point` 对象。
+`this` 仍然指调用 `plus` 方法的那个 `Point` 对象,与构造函数中相同。
+因此,`this.x` 和 `this.y` 是调用 `plus` 的那个 `Point` 的坐标,
+`other.x` 和 `other.y` 是传入的 `Point` 的坐标。
+
+在这个方法中,我们使用 `new` 关键字创建一个新的 `Point` 对象。
+因为前面定义的构造函数接收两个参数,
+所以把 `this.x + other.x` 和 `this.y + other.y` 作为实参传给构造函数。
+因此,新的 `Point` 对象会拥有两个 `Point` 对象坐标之和。
+
+`minus` 方法的逻辑与之类似,但它是减去坐标,而不是相加:
+
+```java {point}#minus
+
+```
+
+`minus` 方法得到的结果并不是场地上的一个位置。
+它是一个 vector(向量),也就是带有方向和长度的箭头,例如“向右 3 米、向上 4 米”。
+用这个 `Point` 减去 `other`,得到的是从 `other` 指向这个 `Point` 的向量。
+`Point` 既可以保存位置,也可以保存向量,因为两者都只用 `x` 和 `y` 两个数值描述。
+
+最后定义 `norm` 方法,它返回这个 `Point` 到原点的距离。
+想象一个直角三角形,一个角的顶点在原点,对角的顶点就是当前的这个 `Point`。
+两条直角边的长度分别是 `x` 和 `y`,所以根据勾股定理,斜边长度是 `x² + y²` 的平方根。
+对于点 (3, 4),结果就是 9 + 16 的平方根,也就是 5。
+向量的长度称为它的 norm(范数),这个方法也因此得名。
+
+```java {point}#norm
+
+```
+
+这个方法定义了一个 local variable(局部变量) `sumOfSquares`,用于储存 `x` 和 `y` 这两个成员变量(field)的平方和。
+局部变量只在当前的方法内部访问,
+一般用来保存中间计算结果。
+这个方法还使用 `Math.sqrt()` 方法。
+它是 `Math` 类中的 static(静态)方法,
+作用是返回传入参数的平方根。
+
+
+
+`Point` 现在可以完成这些数学运算了。
+下面是完整的类:
+
+```java {point}#pointFull
+
+```
+
+下面是使用它的示例:
+
+```java {usage}#createAndUse
+
+```
+
+`a.plus(b)` 的含义是“在对象 `a` 上调用 `plus` 方法,并把 `b` 作为实参传入”。
+在 `plus` 方法内部,`a` 就对应关键字 `this`,`b` 对应参数 `other`。
+
+## Mutable State(可变状态)
+
+我们定义的 `Point` 类是 immutable(不可变) 的。这意味着 `Point` 对象一旦被创建,
+它的 `x` 和 `y` 值就永远不能改变,因为这些成员变量(field)用了 `final` 修饰。
+不可变类型有很多好处,因为它让类更容易理解:不需要担心成员变量(field)的值会在意外的时候被改变。
+
+不过,有时我们需要一个状态会随时间变化的类。接下来我们定义一个 `RobotTracker` 类,用来跟踪机器人在场地上的当前位置。
+位置从某个地方开始,并随着机器人移动而更新,因此它不能是 `final`:
+
+```java {tracker}#mutableField
+
+```
+
+注意,成员变量(field)的类型不一定非得是 primitive type(基本类型),例如 `double`。
+就像 `x` 和 `y` 是 `double` 类型的成员变量(field)一样,`position` 也是一个成员变量(field),它的类型是我们自己写的 `Point` 类。
+任何类型,无论是 Java 内置的,还是我们自己定义的,都可以用作成员变量(field)的类型。
+
+我们仍然使用 `private`,防止其他类直接修改这个成员变量(field),
+因此改变位置的唯一方式是通过我们定义的方法。
+这很重要,因为 `move`、`distanceTo` 和 `reset` 可以依赖 `position` 始终有效,
+而不用担心另一个类把它设成意外的值。
+如果其他代码可以直接修改 `position` 的值,`RobotTracker` 就无法保证自己的行为。
+
+构造函数的工作方式与之前相同,把 `position` 初始化为给定的起始位置值:
+
+```java {tracker}#robotTrackerConstructor
+
+```
+
+只要每个构造函数接收的参数组合不同,一个类就可以有多个构造函数。
+当存在合理的默认值时,这个特性非常好用:在这里不带参数的 `RobotTracker` 会默认从原点开始。
+我们不用重复写 `this.position = Point.ORIGIN`,而是使用 `this(...)` 的方法来调用另一个构造函数:
+
+```java {tracker}#constructorChaining
+
+```
+
+调用 `move` 方法时,传入一个 `delta`,也就是位置的变化量,这个方法会把它加到当前位置上。
+这就是 mutation(可变修改),方法对 `this.position` 重新赋值,改变了这个对象的状态。
+
+```java {tracker}#stateMutation
+
+```
+
+注意,`move` 方法的返回类型是 `void`。
+这是因为该方法实际上不需要返回任何内容。
+它改变的是自己的内部状态。
+
+`distanceTo` 方法用来计算从当前位置到目标的直线距离。
+它使用一个局部变量 `diff` 保存两点之间的向量,然后再取这个向量的长度:
+
+```java {tracker}#localVariableChain
+
+```
+
+因为 `position` 是私有(private)的,所以我们还需要一个 getter 方法来读取机器人的当前位置:
+
+```java {tracker}#getPosition
+
+```
+
+`reset` 方法把位置设回原点。
+我们不用写 `new Point(0, 0)`,而是复用 `Point.ORIGIN` 常量。
+两种写法都可以,因为 `new Point(0, 0)` 和 `Point.ORIGIN` 的坐标相同。
+但复用 `ORIGIN` 可以避免创建一个多余的对象,也让代码的意图更清楚:
+我们明确指的是原点本身,而不是一个碰巧位于 (0, 0) 的点:
+
+```java {tracker}#useStaticConstant
+
+```
+
+下面是完整的 `RobotTracker` 类:
+
+```java {tracker}#robotTrackerFull
+
+```
+
+下面是使用它的示例:
+
+```java {usage}#trackerUsage
+
+```
+
+
+
+## 方法与可变状态练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/classes-objects.mdx b/src/content/docs/zh-cn/learning-course/stage0/classes-objects.mdx
new file mode 100644
index 00000000..0a7bc8fe
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/classes-objects.mdx
@@ -0,0 +1,201 @@
+---
+title: Classes and Objects(类与对象)
+description: 介绍 Java 的类和对象,以及如何为它们添加字段和构造函数
+codeRegionSources:
+ point: stage0/snippets/src/classes-methods/Point.java
+ usage: stage0/snippets/src/classes-methods/Usage.java
+---
+
+在[前面的课程](/learning-course/stage0/java-fundamentals/)中,我们讨论了 Java 的不同数据类型,
+例如 `int`、`double` 和 `String`。
+本节课我们将学习**类**。类可以用来创建我们自己的数据类型,以及如何用类创建**对象**。
+我们还会定义类的两个组成部分:fields(字段)和 constructors(构造函数)。
+
+## 什么是对象
+
+在开始编写代码之前,需要先弄清楚对象是什么。
+到目前为止,我们编写的代码都是从运行程序的计算机这一方来描述的。
+例如,`System.out.println("hello");` 读起来像一条指令:“让计算机输出 hello。”
+
+类让我们从另一个角度编写代码,这种方式有时称为 object-oriented programming(面向对象编程)。
+程序不再由一个执行者完成所有事情,而是由许多对象组成。每个对象都有自己的**状态**和**行为**,
+类似于戏剧中的角色。
+角色有服装、名字等属性,也有唱歌、说台词等能力。
+在代码中,对象的属性保存在**字段**中,对象的能力由**方法**定义。
+
+角色的有些属性在整场戏剧中保持不变,例如名字;
+另一些则会随场景变化,例如服装,或者他们站在舞台上的位置。
+字段也是如此:有些设定一次后就不再改变,我们称之为 immutable(不可变);
+另一些则会随时间变化,我们称之为 mutable(可变)。
+字段应该可变还是不可变,是设计类时需要做出的选择。
+在本节课和下一课中,我们都会看到这两类例子。
+
+在 FRC 代码中,对象通常比戏剧角色更抽象。
+`Point` 表示一个位置,`Motor` 表示一台实体电机,等等。
+对象也可以只是把相关数据和行为组织在一起的一种方式,
+所以它并不一定对应现实中的某个物体。
+
+## 类型的种类
+
+Java 有两类类型:primitive types(基本类型)和 reference types(引用类型):
+
+- **基本类型**是语言内置的最基础类型,例如 `int`、`double` 和 `boolean`。
+- **引用类型**(也称为对象类型)是由程序员定义的更复杂类型。
+ 许多引用类型内置在 Java Development Kit(Java 开发工具包,JDK)中,例如 `String`。
+ 另一些来自外部库,但你也可以定义自己的对象类型。
+
+类是定义对象类型的一种方式。
+**类**是创建对象的模板,
+用字段和方法定义对象的状态和行为。
+**对象**是类的实例。
+例如,`"a"` 和 `"b"` 都是 `String` 类的对象。
+
+
+
+## 定义自己的类
+
+现在我们已经知道类是什么,接下来定义自己的类。
+第一个示例是一个简单的 `Point` 类,表示二维空间中的一个点。
+它可以帮助我们编写代码,让机器人知道自己在场地上的位置。
+
+在 Java 中,每个类都定义在一个 `.java` 文件中,并以 `public class` 关键字开头。
+`public` 关键字表示这个类可以在程序的任何地方访问,包括其他文件。
+在本节课后面,我们会再次看到 `public`。那时它用于字段和构造函数,而不是整个类。
+`class` 关键字告诉 Java 我们正在定义一个新类,后面跟着我们给它的名称 `Point`。
+
+创建一个名为 `Point.java` 的新文件,并这样开始:
+
+{/* rli:ignore */}
+
+```java
+public class Point {
+
+}
+```
+
+接下来添加三个字段。
+**字段**是属于类的变量,用于保存对象状态的一部分。
+每个 `Point` 对象都有自己的一份字段副本,因此两个不同的 `Point` 对象可以有不同的坐标。
+首先添加两个 `double` 字段 `x` 和 `y`,表示点的坐标。
+每个 `Point` 对象的 `x` 和 `y` 都可以不同。
+
+```java {point}#finalFields
+
+```
+
+注意,我们使用 `final` 关键字使这些字段**不可变**。
+这意味着它们一旦创建,值就不能再改变。
+通常情况下,除非有明确的理由,否则应该把所有字段都设为 `final`。
+在下一课中,我们会看到这样的理由。
+我们还使用 `private` 关键字使这些字段成为**私有**字段。
+这意味着只有同一个类中的方法才能访问它们。
+
+到目前为止,`x` 和 `y` 字段都属于某一个具体的 `Point` 对象。
+不过,有时我们想要一个由该类的所有对象共享的值,而不是每个对象各有一份。
+接下来添加一个静态常量字段 `ORIGIN`,表示原点 (0, 0)。
+
+```java {point}#staticConstant
+
+```
+
+`static` 关键字用于创建**静态**字段。
+静态字段由类的所有实例共享。
+在这里,`ORIGIN` 字段由所有 `Point` 对象共享,表示原点 (0, 0)。
+静态字段和方法通过类名访问,而不是通过类的实例访问,
+因此 `ORIGIN` 字段通过 `Point.ORIGIN` 访问。
+`ORIGIN` 字段也是 `public` 的,所以程序的任何地方都可以访问它。
+因为它既是 `static` 又是 `final`,所以 `ORIGIN` 是一个 constant(常量)。
+这也是它的名字使用大写下划线命名法的原因,[Java基础](/learning-course/stage0/java-fundamentals/#名称)中介绍过这种写法。
+
+
+
+## Constructors(构造函数)
+
+目前还没有任何代码给 `x` 和 `y` 赋上值。
+这是**构造函数**要完成的工作:使用 `new` 关键字创建对象时执行的一段特殊代码。
+它通常用来给对象的字段设置初始值。
+
+构造函数是一种**方法**。方法是属于类的、有名字的代码块,用来定义类行为的一部分。
+我们将在[下一课](/learning-course/stage0/classes-methods/)中自己编写方法。
+目前只需要构造函数,以及用来读取字段值的简单方法。
+
+定义构造函数时,先写 `public` 关键字,
+然后写类名,再在圆括号中写参数列表。
+**参数**是调用构造函数时传入的值。
+
+```java {point}#constructor
+
+```
+
+这样就可以用 `new` 关键字创建新的 `Point` 对象。
+例如,点 (1, 1) 可以用 `new Point(1, 1)` 创建。
+`new Point (1, 1)` 是 `Point` 类的一个对象。
+
+这里需要注意 `this` 关键字。
+`this` 用来表示当前对象。
+它用于访问对象的字段和方法。
+在这个示例中,我们用 `this` 访问当前对象的 `x` 和 `y` 字段,
+并把它们设为参数 `x` 和 `y` 的值。
+这里必须使用 `this`,因为参数名和字段名相同。
+所以 `this.x` 指字段 `x`,而 `x` 指参数 `x`。
+`this` 并不总是必需的:如果方法参数的名称与它们要设置的字段不同,
+Java 已经能够区分它们,就可以省略 `this`。
+
+## Getter(获取器)
+
+接下来我们要定义一些 getter 方法。getter 方法的作用很简单:获取并返回一个成员变量(field)的值。它的返回值类型和对应的成员变量一致,并且不带参数。
+由于 `x` 和 `y` 这两个成员变量是私有(private)的,我们必须编写 getter 方法,才能让其他类访问到它们:
+
+```java {point}#getters
+
+```
+
+getter 方法是最简单的一种方法。
+方法名前面的 `double` 是它的**返回类型**,也就是方法返回给调用者的值的类型。
+`return` 关键字负责把这个值返回回去。
+可以看到,getter 方法按照惯例以 `get` 开头,后面跟着成员变量(field)的名称。
+
+## 创建和使用对象
+
+有了构造函数和 getter 方法之后,`Point` 就是一个可以实际使用的类型:
+
+```java {usage}#createPoint
+
+```
+
+我们使用 `new` 关键字和构造函数创建新的 `Point` 对象。
+这会执行刚才编写的构造函数,并返回一个新的对象。
+然后使用点运算符(`.`)调用对象的方法:
+`a.getX()` 的意思是“在对象 `a` 上调用 `getX` 方法”。
+在这次调用中,`a` 就是 `this` 所指的对象。
+任何方法调用都使用同样的形式:`object.method(arguments)`。
+
+也可以直接在类上访问 `ORIGIN` 常量,而不创建实例:
+
+```java {usage}#accessStaticConstant
+
+```
+
+## 类与对象练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/conditionals.mdx b/src/content/docs/zh-cn/learning-course/stage0/conditionals.mdx
new file mode 100644
index 00000000..fa1807bd
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/conditionals.mdx
@@ -0,0 +1,139 @@
+---
+title: Conditionals(条件语句)
+description: 介绍 Java 中的 if、else 和 else if 语句
+codeRegionSources:
+ default: stage0/snippets/src/Conditionals.java
+---
+
+在[上一课](/learning-course/stage0/operators/)中,我们用比较运算符和逻辑运算符得到 `true` 或 `false`。
+本节课我们将利用这些值,通过**条件语句**实现程序判断:只有当条件成立(`true`)时,才执行对应的代码块。
+
+## 为什么需要条件语句?
+
+日常生活中我们一直在做决定,例如“如果饿了,就去吃点零食”。
+机器人也会做决定,例如“如果机器人离墙很远,就向前行驶”,或者“如果比赛计时到 0,就停止行驶”。
+到目前为止,我们写的每一行代码每次都会运行。
+条件语句让我们可以根据传感器读数或比赛剩余时间这样的值,选择运行哪一段代码。
+
+我们会介绍三种条件语句:`if`、`else` 和 `else if`。
+
+
+
+## `if` 语句
+
+**`if` 语句**只在条件为 true 时运行一段代码:
+
+```java #ifSyntax
+
+```
+
+`if` 语句有三个部分:
+
+- `if` 关键字
+- 圆括号里的条件,结果为 true 或 false,例如 `6 > 2`
+- **代码块**,也就是花括号 `{` 和 `}` 之间的代码
+
+如果条件为 true,代码块就会运行。
+如果为 false,代码块会被跳过,程序继续执行它后面的代码。
+
+下面的示例只有当机器人离墙壁超过 10 厘米时,才让机器人向前行驶:
+
+```java #ifExample
+
+```
+
+`distance` 是 60,所以条件 `distance > 10` 为 true。
+代码块会运行,打印 `Driving at full speed`,并把传动系统设置为全速。
+如果 `distance` 是 5,条件就为 false,不会打印任何内容。
+
+
+
+## 加上 `else`
+
+单独的 `if` 语句在条件为 false 时不会执行任何操作。
+但很多时候,我们希望在这种情况下执行其他动作,比如机器人靠近墙壁时停下。
+**`else`** 代码块只在 `if` 语句的条件为 false 时运行:
+
+```java #ifElseSyntax
+
+```
+
+`else` 本身不带判断条件。
+它负责处理所有 `if` 没有匹配到的情况。
+这两段代码块**只会执行其中一个,不会同时运行**。
+
+现在机器人就可以实现:离墙较远时前进,靠近墙壁时停下。
+
+```java #elseExample
+
+```
+
+不运行代码,判断它会输出什么?
+
+
+
+## 加上 `else if`
+
+`if` 和 `else` 只能在两种情况里做选择,但有时我们需要更多分支。
+**`else if`** 可以增加新的判断条件,只有它前面所有条件都 false 时,才会检查这个条件:
+
+```java #elseIfSyntax
+
+```
+
+Java 会**从上往下依次判断条件**,执行第一个结果为 true 所对应的代码块。
+只要某一段代码块执行了,后面所有条件都会直接跳过,哪怕后面的条件同样成立。
+如果所有条件都不成立,就不会执行任何代码块。
+
+我们用 `else if` 实现让机器人在靠近墙壁时减速:
+
+```java #elseIfExample
+
+```
+
+`distance` 为 30,所以第一个条件 `distance > 50` 为 false,Java 继续判断 `else if`。
+`distance > 10` 为 true,所以机器人以半速行驶。
+
+条件的判断顺序在这里非常关键。
+`distance` 为 60 时,它同时满足大于 50 和大于 10,但只会执行第一个匹配到的代码块,所以机器人全速行驶。
+如果我们先检查 `distance > 10`,那么即便机器人离墙很远,也只会以半速行驶。
+
+## 综合运用
+
+刚才的例子里,一旦机器人距离墙壁小于等于 10 厘米,程序就不会执行任何分支,电机会保持在上一次设定的转速继续运转。
+我们可以在末尾加上一个 `else` 来解决这个问题,它用来处理剩下所有的情况:
+
+```java #conditionalExample
+
+```
+
+不运行代码,判断它会输出什么?
+注意,这一题有点容易踩坑。
+
+
+
+你可以在 `if` 和末尾的 `else` 之间按需串联任意多个 `else if`,并且最后的这个 `else` 也可以省略。
+
+## 条件语句练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/debugging.mdx b/src/content/docs/zh-cn/learning-course/stage0/debugging.mdx
new file mode 100644
index 00000000..8f79e38d
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/debugging.mdx
@@ -0,0 +1,167 @@
+---
+title: Debugging(调试)
+description: 介绍语法错误、运行时错误和逻辑错误,以及如何找到并修复它们
+codeRegionSources:
+ default: stage0/snippets/src/Debugging.java
+---
+
+在[上一课](/learning-course/stage0/java-fundamentals/)中,你写出了第一段 Java 代码,过程中可能也遇到了一些错误。
+如果第一次就全部成功,那要么是你非常幸运,要么是还没有充分测试。
+找出并修复这些错误称为 debugging(调试),以后你会经常做这件事。
+这一课介绍你会遇到的三种错误,以及如何分别追查它们。
+
+
+
+## 错误类型
+
+错误主要有三种,每种出现的时间不同:
+
+
+
+
+ | 名称 |
+ 发生节点 |
+ 现象 |
+
+
+
+
+ | Syntax error(语法错误) |
+ 编译时 |
+ 编译器会显示错误信息,代码也无法完成构建 |
+
+
+ | Runtime error(运行时错误) |
+ 运行时 |
+ 程序会崩溃并显示错误信息。在机器人上,代码随后会重新启动 |
+
+
+ | Logic error(逻辑错误) |
+ 运行时 |
+ 代码能够运行,但不会按你的意图工作 |
+
+
+
+
+## Syntax Errors(语法错误)
+
+代码运行之前,一个称为 compiler(编译器)的程序会把它翻译成计算机能够理解的指令。
+就像英语有语法规则一样,Java 也有 syntax(语法)规则,用来确保编译器能理解你写的代码。
+如果代码违反了其中一条规则,编译器不会构建代码,而是给出一个 syntax error(语法错误)。
+这发生在编译器翻译代码的时候,代码实际上还没有运行,所以我们说它发生在 compile time(编译时)。
+
+VS Code 会在它认为写错的代码下方显示红线,就像 Google Docs 标出拼写错误一样。
+例如,把 `System` 拼错就是语法错误:
+
+
+
+漏掉语句的一部分也是语法错误,例如变量声明里缺少等号:
+
+
+
+把鼠标悬停在红线上,VS Code 会告诉你更多关于这个错误的信息。
+例如,如果变量名拼错了,悬停时会显示 `numer cannot be resolved to a variable`。
+这表示编译器正在寻找名为 `numer` 的变量,但找不到。
+严格来说,这不是语法错误,因为代码符合 Java 的语法规则,但编译器仍会在编译时发现它,并用同样的方式显示出来。
+
+
+
+
+
+构建代码时,同样的错误信息也会出现在终端中。
+真正的错误不一定就在编译器指出的那一行。
+某一行缺少分号或花括号,可能会让后面几行才报错,所以在动手修改之前,也要看一看错误附近的几行。
+
+## Runtime Errors(运行时错误)
+
+编译器抓不住所有错误。
+有些代码符合 Java 的全部规则,问题只有在代码运行时才会出现。
+这些称为 runtime errors(运行时错误)。
+一种常见的运行时错误是除以零:
+
+{/* rli:ignore */}
+
+```java
+int answer = 10 / 0;
+System.out.println(answer);
+```
+
+这段代码能够通过编译,但运行时会崩溃,并出现类似这样的错误:
+
+{/* rli:ignore */}
+
+```
+Exception in thread "main" java.lang.ArithmeticException: / by zero
+ at Main.main(Main.java:5)
+```
+
+这条信息称为 stack trace(堆栈跟踪),因为它会把错误追溯到程序中发生问题的位置。
+第一行说明出了什么问题。
+`ArithmeticException` 是数学错误,`/ by zero` 说明具体是哪一种。
+第二行说明发生在哪里,`(Main.java:5)` 表示 `Main.java` 的第 5 行。
+要修复它,就修改代码,让它永远不会除以零。
+
+## Logic Errors(逻辑错误)
+
+有时代码能够通过编译,运行时也不会崩溃,但没有做出你想要的结果。
+这是 logic error(逻辑错误)。
+计算机不知道你的本意,它只按代码所说的去做,即使那是错的。
+
+下面这段代码本应计算一个宽为 2、高为 5 的矩形面积。
+不运行它的话,你能找出错误吗?
+
+```java #logicError1
+
+```
+
+
+
+逻辑错误没有错误信息,所以必须自己找。
+通读代码,检查它实际写的内容是否和你的本意一致。
+在程序的不同位置用 `System.out.println()` 打印数值也会有帮助。
+第一个打印出你没预料到的值的位置,大致就是出错的地方。
+真实的逻辑错误通常比这个例子更难发现。
+
+## 避免和修复错误的建议
+
+你不可能写出完全没有错误的代码,但可以让错误更少出现,也更容易找到:
+
+- 使用能说明变量保存内容的变量名。
+- 把复杂的代码拆成更小、更简单的部分。
+- 让代码易于阅读。
+- 每写完一小段代码就测试一次。
+ 不要等全部写完再一次测试。
+
+遇到错误时,要仔细阅读错误信息。
+如果卡住了,就在网上搜索这条错误信息。
+很可能已经有人遇到过同样的问题。
+
+VS Code 也有一些无障碍功能,可以让代码更容易阅读和调试。
+例如,有些人觉得换一种字体(比如 Comic Sans)更容易发现错误。
diff --git a/src/content/docs/zh-cn/learning-course/stage0/git.mdx b/src/content/docs/zh-cn/learning-course/stage0/git.mdx
new file mode 100644
index 00000000..6398d23e
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/git.mdx
@@ -0,0 +1,57 @@
+---
+title: Git - Uploading code(上传代码)
+description: Git 入门
+---
+
+在前面的课程中,你已经编写了第一段 Java 代码,学习了一些调试方法,并完成了练习。
+现在需要把这些修改 push(推送)到 GitHub。
+
+Git 是一种版本控制系统。它通过命令记录代码的修改。
+本课程会用到两个命令:commit(提交)和 push(推送)。
+
+
+
+## Commit(提交)
+
+commit(提交)是仓库的一份已保存状态。
+每完成一项任务,例如做完一个练习,都应该做一次 commit。
+每次 commit 都要写一段说明,这段说明称为 message(提交说明)。
+提交说明应当能说清这次改了什么,并且不要太长。
+例如,完成 Print.java 练习后,提交说明可以写成 "Completed Print.java"。
+
+## Push(推送)与 Pull(拉取)
+
+push(推送)会把你电脑上的 commit 上传到远程仓库,也就是你 GitHub 账号上的仓库。
+上传到 GitHub 时,要先 commit,再 push。
+
+pull(拉取)会把远程仓库中的修改下载到本地仓库,也就是你电脑上的仓库。
+
+VS Code 里的 Git 不直接使用 push 这个名称,而是使用 "Sync Changes"(同步更改)。它会在 push 你的修改的同时,pull 远程的新修改。
+
+## 上传代码
+
+完成练习后,先保存代码,然后打开 VS Code 左侧的 Source Control(源代码管理)。
+
+
+
+这时会打开一个新的侧栏。
+在这里可以看到哪些文件被修改了,以及具体改了什么。
+也可以在这里把修改 commit 并 push 到 GitHub。
+
+在本阶段,每完成一个练习,建议按图中的方式把修改 commit 并 push 到你的仓库。
+
+
+
+在本课程的其他阶段,建议在完成一项任务后,或准备停下来时,再 push 代码。
+这样可以避免进度丢失。
diff --git a/src/content/docs/zh-cn/learning-course/stage0/interfaces-lists.mdx b/src/content/docs/zh-cn/learning-course/stage0/interfaces-lists.mdx
new file mode 100644
index 00000000..efeaf072
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/interfaces-lists.mdx
@@ -0,0 +1,245 @@
+---
+title: Interfaces, Generics, and Lists(接口、泛型与列表)
+description: 介绍 Java 中的接口、泛型方法,以及 List 接口
+codeRegionSources:
+ sensor: stage0/snippets/src/interfaces-lists/DistanceSensor.java
+ ultrasonic: stage0/snippets/src/interfaces-lists/UltrasonicSensor.java
+ lidar: stage0/snippets/src/interfaces-lists/LidarSensor.java
+ tracker: stage0/snippets/src/interfaces-lists/RobotHistoryTracker.java
+ usage: stage0/snippets/src/interfaces-lists/InterfacesListsUsage.java
+ pair: stage0/snippets/src/interfaces-lists/Pair.java
+---
+
+import { FileTree } from '@astrojs/starlight/components';
+
+在前面的课程中,我们编写了 `Point` 和 `RobotTracker` 这样的类,用来表示程序中的事物。
+本节课我们将学习 interfaces(接口)。接口用来描述一个类能做什么,而不规定具体实现方式。
+还要学习 generics(泛型),让同一段代码可以用于不止一种类型。
+以及 List 接口,它比数组更灵活。
+
+## 为什么使用接口?
+
+假设机器人需要在 autonomous(自动模式)期间测量自己与墙壁的距离。
+某一年,队伍可能使用超声波传感器;下一年,可能换成激光测距传感器。
+两者都测量距离,但它们是不同的硬件,由不同的代码控制。
+
+如果机器人的其余代码只能配合某一个具体的传感器类工作,更换硬件就意味着重写所有使用它的代码。
+接口可以描述传感器能做什么,而不指明它具体是哪一种传感器,这样就解决了这个问题:
+
+```java {sensor}#distanceSensorInterface
+
+```
+
+接口看起来和类很像,但它的方法没有方法体,只有一个以分号结尾的 signature(方法签名)。
+它相当于是一份约定:任何 implements(实现) `DistanceSensor` 的类都必须提供 `getDistanceMeters()` 方法。
+
+下面是两个各自以不同方式实现这份约定的传感器类:
+
+```java {ultrasonic}#ultrasonicSensorClass
+
+```
+
+```java {lidar}#lidarSensorClass
+
+```
+
+这里使用 `implements` 关键字告诉 Java:`UltrasonicSensor` 和 `LidarSensor` 实现了 `DistanceSensor`。
+如果我们写了 `implements DistanceSensor`,却没有添加 `getDistanceMeters()` 方法,编译器就会报错。
+我们还使用 `@Override` 注解告诉编译器,我们正在 overriding(重写) `getDistanceMeters()` 方法。
+重写方法是改变父类或接口中已有方法行为的一种方式。
+
+
+
+因为这两个类都实现了 `DistanceSensor`,只依赖 `DistanceSensor` 的代码就可以使用其中任意一个传感器,
+例如这个用来判断我们是否离物体太近的方法:
+
+```java {usage}#isTooClose
+
+```
+
+这里,`isTooClose` 的形参类型是 `DistanceSensor`,所以传入 `UltrasonicSensor` 还是 `LidarSensor` 都没有关系:
+
+```java {usage}#distanceSensorCall
+
+```
+
+如果队伍在下个赛季更换传感器,这个方法完全不需要修改,因为它只依赖 `DistanceSensor` 接口。
+
+## 泛型方法
+
+接口让一段代码可以用于几种相关的类型。
+泛型更进一步,让一个方法可以用于任何类型。
+下面这个方法返回数组的最后一个元素,无论数组里是什么类型:
+
+```java {usage}#genericLast
+
+```
+
+返回类型前面的 `` 引入一个 type parameter(类型参数),它是一个占位符,直到方法被调用时才确定具体类型。
+在方法内部,`T` 像真实类型一样使用:参数的类型是 `T[]`,返回类型是 `T`。
+
+我们可以用完全不同的数组类型调用 `last`,两种情况都可以正常执行:
+
+```java {usage}#genericLastCall
+
+```
+
+调用 `last(path)` 时,Java 把 `T` 填成 `Point`;调用 `last(sensors)` 时,Java 把 `T` 填成 `DistanceSensor`。
+我们不需要为每种类型单独写一个方法。
+
+类或接口也可以是泛型的。
+例如,如果想从一个方法返回两个值,可以编写一个 `Pair` 类:
+
+```java {pair}#pairClass
+
+```
+
+这样我们就可以创建存放任意两种类型的 `Pair` 对象:
+
+```java {usage}#pairCall
+
+```
+
+## 包与导入
+
+到目前为止,我们编写的每个类都没有制定包,所以各个类之间可以直接使用,完全不需要 `import` 语句。
+不过,大多数真实项目会把许多类组织到不同目录中,
+因此我们需要一种方式,告诉 Java 去哪里找到这些类,以及它们依赖的类。
+Java Development Kit(Java 开发工具包,JDK)内置的类、WPILib 等第三方库中的类,
+以及你自己的机器人项目中的类,都被组织成 packages(包):包就是一组相关类的命名集合。
+
+包名与类所在的目录对应,用 `.` 代替斜杠。
+`ArrayList` 位于 JDK 的 `java/util/` 目录中,所以它的包是 `java.util`,完整名称是 `java.util.ArrayList`。
+机器人代码遵循同样的规则,通常位于 `first.robot` 包中。开始本课程的阶段 1 时就会看到:
+
+{/* prettier-ignore */}
+
+- src/main/java
+ - first
+ - robot
+ - Robot.java
+ - subsystems
+ - Drivetrain.java
+ - Main.java
+
+
+包中的每个文件都在第一行声明自己属于哪个包。
+`Drivetrain.java` 位于 `first/robot/subsystems/`,所以它这样开头:
+
+{/* rli: ignore */}
+
+```java
+package first.robot.subsystems;
+```
+
+同一个包中的类可以直接互相使用。
+要使用另一个包中的类,需要在 package 声明下面写一条 `import` 语句,并写明要使用的具体类:
+
+```java {usage}#importList
+
+```
+
+这会从 `java.util` 包导入 `ArrayList` 和 `List`,接下来就会用到它们。
+课程后面,你也会用同样的方式从 WPILib 包导入类,例如 `org.wpilib.math.geometry.Translation2d`。
+
+
+
+## `List` 接口与 `ArrayList`
+
+在[上一课](/learning-course/stage0/arrays/)中,我们使用数组把多个值存放在一起。
+数组一旦创建,大小就固定了。当你确切知道需要多少个元素时,例如一条固定的自动路径,这种方式很合适。
+但有些场景下,你事先不知道需要多少元素。例如,你想在整场比赛期间,每当机器人移动就记录一次它的位置。
+
+`List` 是一个接口,和 `DistanceSensor` 一样。它描述一个可以增大和缩小的 collection(集合)。
+`ArrayList` 是实现 `List` 的类:
+
+```java {usage}#historyList
+
+```
+
+就像 `DistanceSensor sensor = new UltrasonicSensor();` 一样,声明的类型(`List`)是接口,而我们创建的对象(`new ArrayList<>()`)是它的一种具体实现。
+`` 告诉 Java,这个 `List` 保存的是 `Point`。`List` 本身是泛型的,和 `Pair` 一样。
+
+`List` 没有固定长度。你可以随时向它添加元素,它会自动扩容来容纳新数据:
+
+```java {usage}#historyAdd
+
+```
+
+
+
+如果一开始就知道每一个元素,例如上一课里那条固定的自动路径,`List.of` 可以用一行代码创建包含这些元素的 `List`:
+
+```java {usage}#listOf
+
+```
+
+`List.of` 返回一个 `List`,但不会告诉你是哪个类实现了它。
+它返回的对象并不是 `ArrayList`。
+这里它是 `ImmutableCollections$ListN`。这是 JDK 内部的一个类,类名不需要自己输入。
+这样是可以的,因为我们只通过 `List` 接口使用它,就像 `isTooClose` 只使用 `DistanceSensor` 接口一样。
+
+### `List` 的可变性
+
+在[前面的课程](/learning-course/stage0/classes-methods/)中,我们看到有些类是 mutable(可变)的,也就是创建之后状态还可以改变,有些类则是 immutable(不可变)的。
+列表也是如此。
+因为 `List` 是接口,某个列表能不能改变,取决于实现它的是哪个类:
+
+- `ArrayList` 是 mutable(可变)的,所以可以随时添加、删除和替换元素。
+- `List.of` 创建的列表是 immutable(不可变)的。
+ 它的元素在创建时就固定了,对它调用 `add` 或 `remove` 会抛出 `UnsupportedOperationException`。
+
+编译器无法区分这两者,因为它们都被声明为 `List`。
+只有程序运行、`add` 抛出异常时,才会出现这个错误。
+
+不能把 immutable(不可变)列表变成 mutable(可变)列表,但可以基于它创建一份可变副本。
+把已有的列表传给 `new ArrayList<>(...)`,就能生成一个初始元素和原列表完全一致的新 `ArrayList`:
+
+```java {usage}#copyList
+
+```
+
+`editablePath` 是和 `autoPath` 两个相互独立的对象,往 `editablePath` 里新增路径点,不会改变 `autoPath`。
+
+## 给 `RobotTracker` 增加历史记录功能
+
+[上一课](/learning-course/stage0/classes-methods/)中的 `RobotTracker` 会跟踪机器人的当前位置,但无法记录走过的轨迹。
+。现在我们借助 `List`,让它记录机器人到访过的每一个坐标,而不只是当前位置:
+
+```java {tracker}#robotHistoryTrackerClass
+
+```
+
+
+
+`getHistory()` 按顺序返回 `move` 曾经移动到的每个位置组成的 `List`。
+我们可以用 for-each 循环遍历它,方式和上一课遍历数组相同:
+
+```java {usage}#forEachHistory
+
+```
+
+即使每次调用 `move` 时 `history` 的元素都会增加,for-each 循环也不需要事先知道将要访问多少个位置。它会依次访问其中的每一个位置。
+
+## 接口、泛型与列表练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/java-fundamentals.mdx b/src/content/docs/zh-cn/learning-course/stage0/java-fundamentals.mdx
new file mode 100644
index 00000000..e40518d4
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/java-fundamentals.mdx
@@ -0,0 +1,278 @@
+---
+title: Java基础
+description: 介绍 Java 语法、变量、打印语句和注释
+codeRegionSources:
+ default: stage0/snippets/src/JavaFundamentals.java
+---
+
+欢迎来到阶段 0 的第一课!
+我们会先学习编写 Java 代码的规则,然后学习如何用变量保存信息、用打印语句显示信息,以及用注释说明代码。
+
+## Syntax(语法)
+
+Syntax(语法)是一门编程语言中所有代码都必须遵守的规则。
+可以把它理解成语法规则,就像英文句子以大写字母开头、以句号结尾一样,Java
+代码也有类似的规则,并且 Java 对这些规则要求严格,违反规则的代码完全无法运行。
+这类错误称为 syntax errors(语法错误),也是你最常遇到的错误。
+每当本课程介绍一种新语法时,都要仔细留意。
+
+## Variables(变量)
+
+Variable(变量)是程序中有名字的容器,用来保存一条信息。
+变量可以保存温度、电机速度,或储球仓里的球数。
+
+**声明**一个变量,也就是创建它,需要五个部分:
+
+1. 数据类型
+2. 变量名
+3. 等号
+4. 变量值
+5. 分号
+
+下面逐一说明。
+
+### Data Types(数据类型)
+
+变量的 data type(数据类型)告诉 Java 这个变量保存哪种值,以及你可以对它做什么。
+与 Python 等某些编程语言不同,Java 要求每个变量都必须有类型。
+编译器会检查这些类型,所以如果你试图把 `"fast"` 存进一个 `int` 变量,代码还没运行就会报错。
+
+FRC 编程中常用的一些数据类型包括:
+
+- `int`:整数,可以是正数或负数,没有小数部分。
+ 示例:`12`
+- `double`:可以带小数的正数或负数。
+ 示例:`34.1`
+- `boolean`:值为 `true` 或 `false`。
+- `String`:一串字符,用双引号(`"`)括起来。
+ 示例:`"Hello World"`
+
+
+
+### 名称
+
+变量名可以包含字母、数字和下划线(`_`),但不能以数字开头。
+除此之外,变量的命名由你决定,只要它能说明这个变量保存的是什么。
+有描述性的名字会让你自己和帮助你的人更容易看懂代码。
+
+Java 代码常用两种命名风格:
+
+- Camel case(驼峰命名法)
+ :除第一个单词外,其余每个单词的首字母大写,例如
+ `frontLeftDrive`、`currentTemperature` 和 `targetPosition`。
+ 大多数变量使用驼峰命名法。
+- Upper snake case(大写下划线命名法)
+ :每个字母都大写,单词之间用下划线分隔,例如 `BACK_LEFT_DRIVE` 和
+ `GEAR_RATIO`。 这种写法用于 constants(常量)
+ ,也就是整个程序共用的固定值,例如齿轮比。
+
+
+
+给变量起名字是编程里有趣的部分之一,Java 也完全允许你把变量命名为 `bingo`。
+不过阅读你代码的队友就不会那么高兴了,因为 `bingo` 看不出这个变量保存的是什么。
+以拾取机构的电机为例,下面列出一些不合适和合适的变量名:
+
+
+
+
+ | 不合适的变量名 |
+ 合适的变量名 |
+
+
+
+
+ |
+ `f` 避免使用过短或只有一个字母的变量名。
+ 这样的名称没有描述性,无法清楚说明变量的用途。
+ |
+ `frontLeftMotor` 清楚说明了变量的用途,并且易于阅读。 |
+
+
+ | `bingo` 避免使用随意的单词。这样很难知道变量的用途。 |
+ `backLeftMotor` 变量名易于阅读,并且具有描述性。 |
+
+
+ | `motor` 机器人上有许多电机。这样无法说明这台电机的用途。 |
+
+ `intakeMotor` 加入机构名称后,就能清楚表明这台电机属于拾取机构。
+ |
+
+
+ |
+ `intakeMotorForTheTopIntakeRoller`
+ 描述过多,名称也太长,因此难以阅读。
+ |
+ `intakeTopMotor` 变量名更短,但仍能清楚说明用途。 |
+
+
+
+
+### 分号
+
+在 Java 中,分号(`;`)用来标记一条 statement(语句)的结束。语句就是一条指令,就像句号用来结束一个句子。
+Java 不关心 whitespace(空白),例如空格、换行和缩进;空白多一点或少一点,并不会改变代码的作用。
+Java 用分号来区分各条语句,所以漏写分号通常会导致错误。
+
+### 合在一起
+
+下面是四个变量,每个都使用不同的数据类型:
+
+```java #variables
+
+```
+
+## Print Statements(打印语句)
+
+
+
+**打印语句**会在终端中显示信息。
+它可以把信息显示给正在运行程序的人,也可以用来检查代码在做什么,例如电机被要求以多快的速度旋转。
+Java 中的打印语句如下所示:
+
+```java #printLiteral
+
+```
+
+这会把文本 `hello!` 打印到终端。
+`"hello!"` 两侧的引号告诉 Java 把它当作文本,按原样处理。
+如果没有引号,Java 会把 `hello!` 当作代码,并且报错,因为它不是一个有效的变量名或值。
+
+
+
+要打印变量的值,去掉引号,使用变量名即可:
+
+```java #printVariable
+
+```
+
+### 组合文字和值
+
+当程序打印很多信息时,给每个值加上标签会更清楚。
+可以用 `+` 运算符把一个 `String` 和一个值组合起来,这称为 **字符串拼接**:
+
+```java #stringConcatenation1
+
+```
+
+这会打印 `Left Motor Speed 1.0`。
+因为 `speed` 是 `double`,即使我们写成 `1`,打印出来也会带小数点。
+注意 `"Left Motor Speed "` 末尾的空格。
+如果没有这个空格,打印结果会是 `Left Motor Speed1.0`。
+
+也可以在一条打印语句里组合多个值。
+不运行代码的话,下面这行会打印出什么?
+
+```java #stringConcatenation2
+
+```
+
+
+
+## Comments(注释)
+
+Comment(注释)
+是写在代码里的笔记,是写给人看的,不是写给计算机的。
+注释用来说明为什么要写这段代码,既给你的队友看,也给几周后的你自己看。
+编译器会忽略注释,因此也可以把代码变成注释,让它不再运行。
+
+Java 中的注释有两种:单行注释和多行注释。
+
+### 单行注释
+
+**单行注释**以 `//` 开头,这一行里它后面的所有内容都属于注释:
+
+```java #singleLineComment
+
+```
+
+注释也可以写在一行代码的末尾:
+
+```java #inlineComment
+
+```
+
+两个示例做的是同一件事。
+把注释放在代码前面,还是写在同一行,由你决定。
+
+### 多行注释
+
+**多行注释**以 `/*` 开始,以 `*/` 结束。两者之间的所有内容都属于注释,即使跨了好几行也一样。
+多行注释适合写较长的说明,或者把一大段代码变成注释:
+
+```java #multiLineComment
+
+```
+
+### 编写有用的注释
+
+好的注释解释代码存在的原因,而不是重复代码本身在做什么。
+这条注释是有用的,因为它说明了代码的目的:
+
+```java #goodComment
+
+```
+
+这条注释没有用,因为它只是重复了代码已经表达的内容:
+
+```java #badComment
+
+```
+
+不必为每一行代码都写注释。
+但如果某些内容可能会让别人,或者以后的你感到困惑,就留下一条注释!
+
+## 运行代码
+
+在这个阶段,你会根据每个练习中的说明编写代码。
+写完之后,需要运行代码,确认它能正常工作。
+要运行代码,点击 VS Code 右上角的三角形箭头按钮。
+
+显示出来的信息会出现在终端中,终端位于 Panel(面板)里。
+
+
+
+
+
+## Java 基础练习
+
+
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/loops.mdx b/src/content/docs/zh-cn/learning-course/stage0/loops.mdx
new file mode 100644
index 00000000..cc4a5cb4
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/loops.mdx
@@ -0,0 +1,169 @@
+---
+title: Loops(循环)
+description: 介绍 Java 中的 while 循环和 for 循环
+codeRegionSources:
+ default: stage0/snippets/src/Loops.java
+---
+
+在[上一课](/learning-course/stage0/conditionals/)中,我们用条件语句决定一段代码是否执行。
+本节课我们将学习**循环**。循环可以让同一段代码块执行不止一次。
+
+## 为什么需要循环?
+
+程序经常需要把同一件事连续执行多次。
+日常生活中,如果知道成绩即将公布,你可能会反复查看,直到成绩出来。
+机器人也是这样:它可能需要一直向前行驶,直到离墙壁足够近;也可能需要对料斗里的每一个球都执行一次发射流程。
+如果每重复一次就再写一行相同的代码,既麻烦,也无法应对事先不知道要重复多少次的情况。
+
+条件语句和循环都可以用来控制哪段代码应该执行。
+条件语句的代码块**最多执行一次**,循环的代码块可以执行很多次。
+本节介绍两种主要的循环:`while` 循环和 `for` 循环。
+
+
+
+## `while` 循环
+
+**`while` 循环**会反复执行它的代码块,只要条件成立就会继续:
+
+```java #whileSyntax
+
+```
+
+`while` 循环和[上一课](/learning-course/stage0/conditionals/)的 `if` 语句很像。
+它同样包含 `while` 关键字、圆括号里结果为 true 或 false 的条件,以及花括号里的代码块。
+
+它们的区别在于代码块执行完之后:
+`if` 语句会继续执行后面的代码,而 `while` 循环会再次进行条件的判断。
+如果条件仍然成立,代码块会再执行一次,如此重复,直到条件不成立为止。
+如果第一次判断时条件就是 false,代码块将一次都不会执行。
+与 `if` 语句不同,`while` 循环不能带有 `else` 语句。
+
+下面这个循环会依次输出从 0 到 5 的数字:
+
+```java #whileExample
+
+```
+
+`i` 从 0 开始。
+每执行一次循环,代码都会输出 `i` 的值,然后用 `i++` 给它加 1,然后再次判断 `i < 6`是否成立。
+`i++` 就是 [Operators(运算符)](/learning-course/stage0/operators/)那一节内容里的自增运算符。
+一旦 `i` 的值达到 6,条件就不成立,循环结束,所以最后输出的数字是 5。
+
+每执行一次循环的代码块,称为一次 iteration(迭代)。这个循环会执行六次迭代。
+
+### 循环和条件语句一起用
+
+循环的代码块里可以包含任何代码,包括 `if` 语句。
+下面这个循环会让机器人先向前行驶几次,然后停下来:
+
+```java #whileExample2
+
+```
+
+不运行代码,判断它会输出什么?
+
+
+
+## `for` 循环
+
+很多循环都遵循同一种结构:先创建计数变量,然后在条件里判断它,并在每次迭代结束时更新它。
+写成 `while` 循环就是:
+
+```java #ForExample1
+
+```
+
+这个循环依赖三部分:`int i = 0`、`i < 6` 和 `i++`。
+**`for` 循环**把这三部分放在一起,写在循环开头:
+
+```java #ForExample2
+
+```
+
+`for` 后面的圆括号里有三个部分,用分号隔开:
+
+- Initialization(初始化)(`int i =
+ 0`)在循环开始前执行一次,通常用来创建计数变量。
+- Condition(条件)(`i < 6`)在每次迭代前判断,条件成立时循环继续。
+- Update(更新)(`i++`)在每次迭代结束时执行。
+
+这两个循环完成的是同一件事,但 `for` 循环更容易阅读和理解。
+计数从哪里开始、循环何时停止、计数如何变化,都写在同一行里,而不是散落在循环各处。
+
+
+
+不运行代码,判断这个循环会依次输出什么?
+
+```java #forExample
+
+```
+
+
+
+## 小心无限循环
+
+只有条件变成 false,循环才会停止。
+如果循环内部没有任何代码改变这个条件,条件就会一直成立,循环将永远不会停止。
+这称为 infinite loop(无限循环)。
+
+在机器人的控制系统中,无限循环很危险。
+传动系统可能会一直行驶,机械臂也可能一直运动,直到撞上其他物体。
+下面这个循环的本意是让机器人连续向前行驶 7 次:
+
+{/* rli:ignore */}
+
+```java
+int loopCount = 0;
+while (loopCount < 7) {
+ drivetrain.setThrottle(1); // sets drive motors to full speed
+}
+```
+
+但是循环内部没有修改 `loopCount` 的值。
+所以它的值一直为 0,`loopCount < 7` 一直成立,机器人永远不会停止行驶。
+
+要修正这个问题,就需要在循环内部更新 `loopCount` 的值,让条件最终不成立:
+
+{/* rli:ignore */}
+
+```java
+int loopCount = 0;
+while (loopCount < 7) {
+ drivetrain.setThrottle(1); // sets drive motors to full speed
+ loopCount++;
+}
+```
+
+现在 `loopCount` 的值每次迭代结束时都会被加 1,循环会在 7 次迭代后停止。
+
+
+
+## 循环练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/operators.mdx b/src/content/docs/zh-cn/learning-course/stage0/operators.mdx
new file mode 100644
index 00000000..edee224b
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/operators.mdx
@@ -0,0 +1,366 @@
+---
+title: Operators(运算符)
+description: 介绍 Java 中的算术运算符、赋值运算符、比较运算符和逻辑运算符
+codeRegionSources:
+ default: stage0/snippets/src/Operators.java
+---
+
+在 [Java基础](/learning-course/stage0/java-fundamentals/) 这一课中,我们把值存在变量里。
+这一课学习 **运算符**,也就是像 `+` 和 `>` 这样的符号,用来根据已有的值计算出新的值。
+机器人代码里到处都是运算符。
+数储球仓里的球要用 `+`,检查射球机构是否达到速度要用 `>`。
+
+我们会介绍四种运算符:
+
+- Arithmetic operators(算术运算符),用来做数学运算
+- Assignment operators(赋值运算符),用来更新变量的值
+- Comparison operators(比较运算符),用来比较两个值
+- Logical operators(逻辑运算符),用来组合 `true` 和 `false`
+
+## Arithmetic Operators(算术运算符)
+
+**算术运算符**对数字做基本数学运算:
+
+
+
+
+ | 运算符 |
+ 说明 |
+ 示例 |
+ 结果 |
+
+
+
+
+ | `+` |
+ 加法:将两个值相加 |
+ `5 + 2` |
+ `7` |
+
+
+ | `-` |
+ 减法:用一个值减去另一个值 |
+ `5 - 2` |
+ `3` |
+
+
+ | `*` |
+ 乘法:将两个值相乘 |
+ `5 * 2` |
+ `10` |
+
+
+ | `/` |
+ 除法:用一个值除以另一个值 |
+ `10 / 2` |
+ `5` |
+
+
+ | `%` |
+ 余数:返回两个数相除后的余数 |
+ `5 % 2` |
+ `1` |
+
+
+
+
+任何可以使用数字的地方都可以使用运算符,例如给变量赋值时。
+不运行代码的话,下面这些变量分别保存什么值?
+
+```java #variables
+
+```
+
+
+
+
+
+## Assignment Operators(赋值运算符)
+
+**赋值运算符**给变量一个新的值。
+你已经用过最简单的一个,`=`,用来设置变量的值。
+其他赋值运算符是根据变量当前值来更新它的简写:
+
+
+
+
+ | 运算符 |
+ 说明 |
+ 示例 |
+
+
+
+
+ | `=` |
+ 把一个值赋给变量 |
+ `a = 2` |
+
+
+ | `+=` |
+
+ 加法:取变量的当前值,加上指定的量,再把结果赋给这个变量。它与
+ `x = x + y` 相同
+ |
+ `a += 3` |
+
+
+ | `-=` |
+
+ 减法:取变量的当前值,减去指定的量,再把结果赋给这个变量。它与
+ `x = x - y` 相同
+ |
+ `a -= 4` |
+
+
+ | `*=` |
+
+ 乘法:取变量的当前值,乘以指定的量,再把结果赋给这个变量。它与
+ `x = x * y` 相同
+ |
+ `a *= 5` |
+
+
+ | `/=` |
+
+ 除法:取变量的当前值,除以指定的量,再把结果赋给这个变量。它与
+ `x = x / y` 相同
+ |
+ `a /= 6` |
+
+
+
+
+`score += 2` 和 `score = score + 2` 做的是同一件事,但变量名只需要写一次。
+代码更短,少一个容易出错的地方。
+
+这里,`score` 从 10 开始并增加 2,而 `ballsCarried` 从 5 开始并减少 1:
+
+```java #arithmetic
+
+```
+
+加 1 或减 1 非常常见,所以 Java 专门提供了运算符。
+`++` 通过加 1 来 increment(自增)一个变量,`--` 通过减 1 来 decrement(自减)一个变量。
+在本阶段后面的[循环](/learning-course/stage0/loops/)里,你会经常看到 `++`。
+
+
+
+
+ | 运算符 |
+ 说明 |
+ 示例 |
+
+
+
+
+ | `++` |
+ 自增:给变量加 1 |
+ `a++` |
+
+
+ | `--` |
+ 自减:给变量减 1 |
+ `a--` |
+
+
+
+
+机器人每射出一个球,就可以记下这一次,并从储球仓里去掉这个球:
+
+```java #increments
+
+```
+
+## Comparison Operators(比较运算符)
+
+**比较运算符**比较两个值,并返回一个 `boolean`,也就是 `true` 或 `false`。
+程序靠它们做判断,[条件语句](/learning-course/stage0/conditionals/)那一课会用到。
+
+
+
+
+ | 运算符 |
+ 说明 |
+ 示例 |
+ 结果 |
+
+
+
+
+ | `==` |
+ 等于:检查两个值是否相同 |
+ `5 == 2` |
+ `false` |
+
+
+ | `!=` |
+ 不等于:检查两个值是否**不**相同 |
+ `5 != 2` |
+ `true` |
+
+
+ | `>` |
+ 大于:检查一个值是否大于另一个值 |
+ `5 > 2` |
+ `true` |
+
+
+ | `<` |
+ 小于:检查一个值是否小于另一个值 |
+ `5 < 5` |
+ `false` |
+
+
+ | `>=` |
+ 大于或等于:检查一个值是否大于或等于另一个值 |
+ `6 >= 7` |
+ `false` |
+
+
+ | `<=` |
+ 小于或等于:检查一个值是否小于或等于另一个值 |
+ `6 <= 6` |
+ `true` |
+
+
+
+
+它们的用法和数学课里的比较一样。
+这里,机器人已经进了 2 个球,但需要 4 个,所以 `ballsScored > ballsNeeded` 是 `false`:
+
+```java #comparison
+
+```
+
+
+
+### `=` 与 `==`
+
+要检查两个值是否相等,使用双等号 `==`。
+单个等号 `=` 是赋值运算符。
+它设置变量的值,不做比较。
+
+把它们弄混是常见错误。
+下面这行代码无法通过编译。
+Java 会把它理解成试图把数字 6 改成 8,这在java中是做不到的。
+错误信息会说,赋值的左边必须是一个变量:
+
+{/* rli:ignore */}
+
+```java
+System.out.println(6 = 8);
+```
+
+换成 `==` 后,Java 比较的是两个值,并且会打印 `false`,因为 6 不等于 8:
+
+{/* rli:ignore */}
+
+```java
+System.out.println(6 == 8);
+```
+
+## Logical Operators(逻辑运算符)
+
+**逻辑运算符**用来组合 `boolean` 值,这样可以同时根据多个条件做判断,例如“机器人有球,并且已经对准目标”。
+
+
+
+
+
+
+ | 运算符 |
+ 说明 |
+ 示例 |
+ 结果 |
+
+
+
+
+ | `&&` |
+ 与:两个语句都为 true 时结果为 true |
+ `true && true` |
+ `true` |
+
+
+ | `||` |
+ 或:至少一个语句为 true 时结果为 true |
+ `true || false` |
+ `true` |
+
+
+ | `!` |
+ 非:取反。true 变成 false,false 变成 true |
+ `!true` |
+ `false` |
+
+
+
+
+这里,机器人有球,但还没有对准目标。
+不运行代码的话,每一行会打印什么?
+
+```java #logical
+
+```
+
+
+
+## 改变变量与使用它的值
+
+运算符本身会算出一个新的值,但不会改变任何变量。
+只有用 `=` 或其他赋值运算符把结果赋回去,变量才会改变。
+
+这里,`totalPoints` 被设为 `autoPoints + 10` 的结果,所以它的值变成 12:
+
+```java #math1
+
+```
+
+在这个示例里,`gamePieces * 2` 是在打印语句内部计算的:
+
+```java #math2
+
+```
+
+这会打印 12,但之后 `gamePieces` 仍然是 6。
+`gamePieces * 2` 的结果只用于打印,从未赋回给 `gamePieces`。
+可以在下一行再打印一次 `gamePieces` 来确认。
+
+## 运算符练习
+
+
diff --git a/src/content/docs/zh-cn/learning-course/stage0/stage-overview.mdx b/src/content/docs/zh-cn/learning-course/stage0/stage-overview.mdx
new file mode 100644
index 00000000..1066c07f
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/stage-overview.mdx
@@ -0,0 +1,23 @@
+---
+title: 阶段 0 概览
+description: 介绍阶段 0 涵盖的主题
+---
+
+学习 Java 是学习 FRC 编程最重要的一步。
+使用 Java,可以编写代码来运行电机、调节电机的速度,以及完成更多操作。
+关于 Java 要学的内容很多,但阶段 0 只涵盖编写机器人代码所需的基础知识。
+
+阶段 0 的每一课都会介绍一个不同的核心主题,并且会在前面课程的基础上继续展开。
+如果你是编程新手,请**按顺序学习这些课程**。
+
+本阶段涵盖以下主题:
+
+- [Java基础](/learning-course/stage0/java-fundamentals/)
+- [Debugging(调试)](/learning-course/stage0/debugging/)
+- [Operators(运算符)](/learning-course/stage0/operators/)
+- [Conditionals(条件语句)](/learning-course/stage0/conditionals/)
+- [Loops(循环)](/learning-course/stage0/loops/)
+- [Classes and Objects(类与对象)](/learning-course/stage0/classes-objects/)
+- [Methods and Mutable State(方法与可变状态)](/learning-course/stage0/classes-methods/)
+- [Arrays and For-Each Loops(数组与 for-each 循环)](/learning-course/stage0/arrays/)
+- [Interfaces, Generics, and Lists(接口、泛型与列表)](/learning-course/stage0/interfaces-lists/)
diff --git a/src/content/docs/zh-cn/learning-course/stage0/stage-wrap-up.mdx b/src/content/docs/zh-cn/learning-course/stage0/stage-wrap-up.mdx
new file mode 100644
index 00000000..42093438
--- /dev/null
+++ b/src/content/docs/zh-cn/learning-course/stage0/stage-wrap-up.mdx
@@ -0,0 +1,17 @@
+---
+title: 阶段 0 总结
+description: 总结阶段 0
+---
+
+恭喜你完成阶段 0!
+能够动手编写代码,是实现机器人各项动作的基础。
+完成本阶段学习后,你已经掌握了后续编写机器人程序所需要的 Java 基础能力。
+如果你想要复习自己写过的练习,阶段 0 全部练习的答案在 [Stage 0 Solution 仓库](https://github.com/frcsoftware/Stage0-Solution)中。
+
+在阶段 0 中,你已经学习了:
+
+- Java 基础语法,以及如何找到并修复错误
+- 进行数学运算和比较数值
+- 用条件语句和循环控制代码执行逻辑
+- 类、对象和接口,以及把代码拆分到不同文件
+- 用数组和列表存储大量数据
diff --git a/src/content/docs/zh-cn/resources/glossary.mdx b/src/content/docs/zh-cn/resources/glossary.mdx
new file mode 100644
index 00000000..85ddfc81
--- /dev/null
+++ b/src/content/docs/zh-cn/resources/glossary.mdx
@@ -0,0 +1,7 @@
+---
+title: 术语表
+---
+
+import GlossaryList from '@components/GlossaryList.astro';
+
+
diff --git a/src/content/i18n/zh-CN.json b/src/content/i18n/zh-CN.json
new file mode 100644
index 00000000..ece160f1
--- /dev/null
+++ b/src/content/i18n/zh-CN.json
@@ -0,0 +1,44 @@
+{
+ "skipLink.label": "跳到正文",
+ "search.label": "搜索",
+ "search.ctrlKey": "Ctrl",
+ "search.cancelLabel": "取消",
+ "search.devWarning": "搜索仅在生产构建中可用。\n请先构建并预览网站,再在本地测试搜索。",
+ "themeSelect.accessibleLabel": "选择主题",
+ "themeSelect.dark": "深色",
+ "themeSelect.light": "浅色",
+ "themeSelect.auto": "自动",
+ "languageSelect.accessibleLabel": "选择语言",
+ "menuButton.accessibleLabel": "菜单",
+ "sidebarNav.accessibleLabel": "主导航",
+ "tableOfContents.onThisPage": "本页内容",
+ "tableOfContents.overview": "概述",
+ "i18n.untranslatedContent": "此内容尚无简体中文译本。",
+ "page.previousLink": "上一页",
+ "page.nextLink": "下一页",
+ "404.text": "页面未找到。请检查网址,或使用搜索栏查找。",
+ "aside.note": "Note(备注)",
+ "aside.tip": "Tip(提示)",
+ "aside.caution": "Warning(警告)",
+ "aside.danger": "Danger(危险)",
+ "aside.answer": "Answer(答案)",
+ "aside.example": "Example(示例)",
+ "aside.exercise": "Exercise(练习)",
+ "aside.hint": "Hint(提示)",
+ "aside.video": "Video(视频)",
+ "aside.wip": "Work in Progress(正在完善)",
+ "header.menuToggle": "切换导航菜单",
+ "banner.development": "本网站仍在开发中。如果发现任何错误,请{link}报告。",
+ "banner.developmentLinkText": "在这里",
+ "courseSection.underConstruction": "(正在建设中)",
+ "glossary.definitionNotFound": "未找到定义",
+ "footer.github": "在 GitHub 上打开 FRCSoftware.org",
+ "footer.discord": "加入 FRCSoftware.org Discord",
+ "card.copyLink": "复制链接到剪贴板",
+ "slides.previous": "上一张幻灯片",
+ "slides.next": "下一张幻灯片",
+ "slides.goTo": "转到第 {number} 张幻灯片",
+ "slides.closeLightbox": "关闭灯箱",
+ "video.unsupported": "你的浏览器不支持 video 标签。",
+ "video.youTubeTitle": "YouTube 视频"
+}
diff --git a/src/data/glossary.zh-cn.yaml b/src/data/glossary.zh-cn.yaml
new file mode 100644
index 00000000..bf623574
--- /dev/null
+++ b/src/data/glossary.zh-cn.yaml
@@ -0,0 +1,75 @@
+# 简体中文术语和缩写表
+#
+# 键必须与 glossary.yaml 中的英文规范术语一致。
+# `term` 是中文正文中匹配和显示的术语;省略时沿用英文术语。
+# `definition` 是中文定义。未填写的条目会回退到英文。
+
+# 电子器件
+PDH:
+ definition: 配电中心(Power Distribution Hub)
+
+SPARK MAX:
+ definition: 用于控制 REV 电机的电机控制器
+
+Talon FX:
+ definition: 用于控制 CTRE 电机的电机控制器
+
+Systemcore:
+ definition: 运行机器人代码的主处理器,提供多种 I/O 接口
+
+CAN:
+ definition: >-
+ 控制器局域网(Controller Area Network):通常使用黄绿线连接电机控制器和传感器;
+ 可以采用多种拓扑结构进行布线,而不必将每根线缆都单独接回 Systemcore
+
+PWM:
+ definition: >-
+ 脉宽调制(Pulse Width Modulation):一种用于与电机控制器和传感器通信的规范,
+ 设备需要连接回 Systemcore
+
+Main Breaker:
+ term: 主断路器
+ definition: 机器人的电源开关
+
+Limit Switch:
+ term: 限位开关
+ definition: >-
+ 一种在受到物理接触或磁力触发时动作的传感器,可用于在信号上升沿或下降沿触发操作,
+ 也可用于检查机构的状态
+
+Throughbore Encoder:
+ term: 通孔编码器
+ definition: 一种允许轴穿过其中心,并记录角位置的编码器
+
+Magnetic Encoder:
+ term: 磁编码器
+ definition: 一种使用接收器和磁体测量位置与运动状态的编码器
+
+# 软件
+Repository:
+ term: 仓库
+ definition: >-
+ 用于存放代码及相关资源的位置,常用于 Git 等版本控制系统。
+ 仓库本质上是包含文件和子文件夹的文件夹,也可托管在 GitHub 等平台上,
+ 以便开展协作和管理版本
+
+CI:
+ definition: >-
+ 持续集成(Continuous Integration):一种软件开发实践。
+ 开发者频繁地将代码变更合并到共享仓库中,并由此触发自动构建和测试,
+ 以确保代码库保持稳定
+
+WPILib:
+ definition: >-
+ WPILib 是为队伍提供的标准软件库和工具集,用于编写、测试和调试
+ FIRST® Robotics Competition 与 FIRST® Tech Challenge 机器人的代码
+
+AprilTag:
+ definition: >-
+ 一种类似二维码的基准标记。WPILib 和厂商提供的视觉工具可以检测 AprilTag,
+ 从而确定相机相对于标记的位置和朝向
+
+GitHub:
+ definition: >-
+ GitHub 类似于用于代码的 Google Drive。
+ 它可以托管 Git 仓库,并通过拉取请求和议题等功能改善项目协作和版本跟踪
From fc6ecab365e0776ee33fd75fa8e30136c00e5ffa Mon Sep 17 00:00:00 2001
From: smilelsb
Date: Thu, 22 Oct 2026 10:06:27 +0800
Subject: [PATCH 2/2] =?UTF-8?q?fix(i18n):=20keep=20the=20Stage=200=20templ?=
=?UTF-8?q?ate=20link=20intact=20/=20=E4=BF=AE=E6=AD=A3=E9=98=B6=E6=AE=B5?=
=?UTF-8?q?=200=20=E6=A8=A1=E6=9D=BF=E9=93=BE=E6=8E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fullwidth punctuation after the bare URL was parsed as part of the address, so the link check received a 404.
---
.../learning-course/getting-started/forking-and-cloning.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx b/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx
index d6fe690c..359de16b 100644
--- a/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx
+++ b/src/content/docs/zh-cn/learning-course/getting-started/forking-and-cloning.mdx
@@ -9,7 +9,7 @@ import ContentFigure from '@components/ContentFigure.astro';
开始之前,需要为相关的 repository(仓库)创建自己的 fork(派生仓库)。
在此之前,需要先按照“必备工具”部分的说明完成准备工作。
-要创建 fork,请访问需要复制的仓库(例如 https://github.com/frcsoftware/Stage0-Template),然后点击右上角的“Fork”按钮。
+要创建 fork,请访问需要复制的仓库(例如 [Stage 0 Template](https://github.com/frcsoftware/Stage0-Template)),然后点击右上角的“Fork”按钮。
这会在自己的 GitHub 账户下创建该仓库的副本,以便在不影响原仓库的情况下进行修改。