訓練檔結構描述

最後更新:2026-05-29

Your Trainer 是一款 Android 平板的多騎士室內單車應用程式。智慧訓練台控制,本機資料 + 本機控制。一次性購買。

.ytw 訓練檔是純 JSON — 用任何文字編輯器編寫,透過分享面板匯入,並與朋友分享。視覺化編輯器能處理常見情況;當你需要完全掌控時,結構描述就是後備出口。

何時手動編寫

大多數車手永遠不需要這一頁 — 視覺化訓練編輯器和 AI 訓練教練可以涵蓋從 4×8 閾值課表到 microburst 堆疊的一切。當以下情況時才需要結構描述:

最小範例

最短的有效 .ytw 檔案是一個程式含一段間歇。以 .ytw 副檔名儲存,並分享進 Your Trainer。

{
  "programId": "my-sweet-spot",
  "programName": "My Sweet Spot 30",
  "description": "A short sweet-spot workout.",
  "totalDuration": 1800,
  "workoutType": "POWER",
  "primaryLocale": "en",
  "intervals": [
    { "id": "warmup",   "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup",     "intervalType": "WARMUP"   },
    { "id": "work",     "duration": 1200, "targetPowerPercent": 88, "intensityZone": "Z3", "label": "Sweet Spot", "intervalType": "INTERVAL" },
    { "id": "cooldown", "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown",   "intervalType": "COOLDOWN" }
  ]
}

頂層欄位

頂層物件描述一個訓練程式。必填欄位會被標示出來。

欄位類型說明
programId 必填string穩定識別碼。請使用 kebab-case(my-sweet-spot)。這是你資料庫中該訓練的唯一鍵 — 重新匯入具有相同 programId 的檔案會更新既有項目,而不會建立重複項。
programName 必填string車手主要語系下的顯示名稱。
description 必填string顯示在訓練卡片上的一到兩句描述。
totalDuration 必填integer(秒)訓練總長度。App 會在儲存時根據間歇重新計算,所以編寫時即使不一致也沒關係。
intervals 必填array間歇物件重複群組的有序清單。
workoutTypestring訓練家族:POWER(預設)、HR_ZONEROUTE。參見訓練類型
variantstring家族內的子型:STANDARD(預設)或 RAMP_TEST
primaryLocalestring(BCP-47)字串編寫時所使用的語系。預設為 "en"。驅動跨語系回退鏈。
categorystring自由文字分類(例如 "threshold""endurance")。可選。
difficultyinteger(1–5)主觀難度。會顯示在訓練卡片上。
isUserCreatedboolean對於車手在 App 內編寫的訓練為 true;對於匯入或內建的訓練為 false。預設為 false
isFavoriteboolean釘選置頂旗標。車手會在 App 中切換;在分享檔案中通常省略。
routeProfile{ distanceMeters, elevationMeters } 的陣列ROUTE 訓練的完整爬升曲線。對 POWER 和 HR_ZONE 則為 null。
stringsobject(locale → LocaleStrings)每個語系的名稱、描述、間歇標籤與提示的翻譯。

間歇欄位

每個間歇物件描述訓練中的一個區塊。區塊的形態取決於上層的 workoutType — Power 區塊使用功率百分比,HR-Zone 區塊使用區間目標。

欄位類型說明
duration 必填integer(秒)區塊長度(秒)。
targetPowerPercent power 必填integer(FTP 的 %)POWER 訓練的功率目標。與 targetHrZone 互斥。
targetPowerEndPercentinteger(FTP 的 %)可選的漸進結束功率。當有設定時,目標瓦數會在區塊內從 targetPowerPercenttargetPowerEndPercent 線性插值。
targetHrZone hr 必填integer(1–5)HR_ZONE 訓練的心率區間。與 targetPowerPercent 互斥。
intensityZone 必填string視覺區間代碼:Z1Z5。決定地形視覺化上的顏色。參見訓練區間
intervalTypestringWARMUPCOOLDOWNINTERVAL(預設)。熱身和緩和區塊會被排除在僅計工作區塊的摘要之外(例如僅計工作區塊的平均功率、工作部分的區間時間等)。
label 必填string車手主要語系下顯示在區塊上的文字。跨語系變體位於 strings.<locale>.labels
idstring穩定 slug — 用於 strings.<locale>.labels 以及組成提示鍵的鍵值。對於任何隨附翻譯的訓練都建議使用。
autoLabelboolean當標籤是由編輯器預設值產生而非車手手動輸入時為 true。自動標籤會由 Your Trainer 自行在地化,不需要在 strings 中為每個語系設置項目。預設為 false
cadenceTargetinteger(RPM)區塊的可選踏頻目標(例如 60 用於低踏頻爬坡,100 用於高速旋轉訓練)。
cuesCoachingCue 的陣列區塊期間觸發的教練提示。

教練提示

教練提示是在騎乘期間出現在駕駛艙上的短文字覆蓋。每個提示有一個相對於其上層間歇的偏移、要顯示的文字,以及保留在螢幕上的時長。

欄位類型說明
offsetSec 必填integer(秒)從上層間歇起始算起,提示觸發的秒數。
text 必填string訓練主要語系下的提示文字。跨語系變體位於 strings.<locale>.cues,以 <intervalId>:<cueIndex> 為鍵。
durationSecinteger(秒)提示停留在螢幕上的時長。預設為 5

範例間歇含三個提示(鍵的組成使用上層間歇的 id + 提示在陣列中的索引):

{
  "id": "work",
  "duration": 600,
  "targetPowerPercent": 95,
  "intensityZone": "Z4",
  "label": "Threshold",
  "intervalType": "INTERVAL",
  "cues": [
    { "offsetSec": 0,   "text": "Settle in — find your rhythm." },
    { "offsetSec": 300, "text": "Halfway. Stay smooth.",  "durationSec": 8 },
    { "offsetSec": 540, "text": "One minute. Hold form." }
  ]
}

在地化字串與回退鏈

strings 區塊承載訓練中每個對車手可見字串的各語系翻譯。每個語系項目都有相同的形態:

"strings": {
  "en": {
    "name": "Sweet Spot 30",
    "description": "A short sweet-spot workout.",
    "labels": { "warmup": "Warmup", "work": "Sweet Spot", "cooldown": "Cooldown" },
    "cues":   { "work:0": "Settle in", "work:1": "Halfway" }
  },
  "de": {
    "name": "Sweet Spot 30",
    "description": "Ein kurzes Sweet-Spot-Training.",
    "labels": { "warmup": "Aufwärmen", "work": "Sweet Spot", "cooldown": "Ausrollen" },
    "cues":   { "work:0": "Locker einrollen", "work:1": "Halbzeit" }
  }
}

提示鍵遵循 <intervalId>:<cueIndex> 的模式 — 因此 work 間歇上的第一個提示鍵為 work:0

對於每個車手可見的字串,App 會以這個順序挑選最佳的語系匹配:

  1. strings[<rider-locale>] — 車手自己的語系。
  2. strings[primaryLocale] — 作者的語系。
  3. strings["en"] — 通用回退。
  4. 頂層欄位(programName、間歇 label、提示 text)。

從車手自身語系以外的任何語系顯示的字串,會在訓練卡片和駕駛艙上以斜體呈現,讓車手知道哪些字串尚未翻譯。

See also: AI 提示技能 — the AI Coach can generate strings blocks for additional locales when prompted.

重複群組

對於重複性結構,重複群組讓你編寫一次單元,並告訴 App 要播放幾次。重複群組會在匯入時展開為個別區塊,因此車手在騎乘期間能在即將到來的間歇條中看到每個區塊。

{
  "intervals": [
    { "id": "warmup", "duration": 600, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
    {
      "repeat": 4,
      "intervals": [
        { "id": "on",  "duration": 480, "targetPowerPercent": 95, "intensityZone": "Z4", "label": "Threshold" },
        { "id": "off", "duration": 240, "targetPowerPercent": 55, "intensityZone": "Z1", "label": "Recovery" }
      ]
    },
    { "id": "cooldown", "duration": 600, "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
  ]
}

上述範例會展開為 1 段熱身 + 4×(閾值 + 恢復)+ 1 段緩和 = 10 個區塊。重複群組可以巢狀,但為了可讀性,平面結構較佳。

訓練類型與變體

workoutType 欄位選擇主要範式;variant 選擇其中的子型。

workoutType是什麼所需的間歇形態
POWER(預設)以 FTP 為基準的間歇。訓練台在 ERG 模式下騎乘目標瓦數,或在 SIM 模式下跟隨阻力曲線。每段間歇有 targetPowerPercent(漸進間歇還可選 targetPowerEndPercent)。
HR_ZONE心率驅動的間歇。訓練台會即時調整瓦數,讓車手的心率維持在目標區間內 — 當心血管負荷是訓練指標時很有用(恢復、基礎、極化訓練)。每段間歇有 targetHrZone(1–5)。在連接 HRM 之前,START 按鈕會顯示 LINK HRM
ROUTE以坡度驅動的模擬騎乘。訓練台會跟隨 routeProfile 的爬升曲線;車手自行選擇踏頻與檔位。間歇通常為空,或只包含一個全長佔位符。實際的騎乘內容位於 routeProfile

variant 欄位選擇子型:

訓練包(.ytwpack)

當你想要一次取得精選的一批訓練時,.ytwpack 格式會將許多 .ytw 檔案與每個訓練包的清單捆綁在一起。.ytwpack 在底層是一個 ZIP 壓縮檔 — 重新命名為 .zip 並解壓即可看到內容,或透過 Your Trainer 的訓練包安裝程式匯入,只需一觸即可安裝每個訓練。

.ytwpack 壓縮檔包含:

訓練包清單承載足夠的資訊以製作安裝決策表,而無需打開每個訓練。頂層欄位:

欄位類型說明
schema_version 必填integer目前永遠為 1
pack_id 必填string穩定的 kebab-case 識別碼(例如 sweet-spot)。
name 必填string顯示在訓練包目錄中的名稱。
description 必填string車手點選安裝之前可見的一到兩句摘要。
version 必填string(SemVer)MAJOR.MINOR.PATCH,可選 -prerelease。Patch 用於內容修正;minor 用於新增訓練;major 用於破壞性結構描述變更。會內嵌在發佈的檔名中(v1.0.2.ytwpack)。
content_hash 必填string對所有 .ytw 檔案位元組(以 slug 排序串接)計算的 sha256:。在未變更內容的重複產生間保持穩定;每當內部的訓練變動就會更新。
generated_at 必填string(ISO 8601)UTC 時間戳。
set 必填stringpowerhr-zone — 這個訓練包所屬的訓練家族。
category 必填string集合內的子分類(例如 sweet-spot)。
workout_count 必填integer訓練包中 .ytw 項目的數量。
total_ride_time_seconds 必填integer訓練包中每個訓練時長的總和。
experience_level 必填string根據內容的難度範圍計算得出 — 為 beginner / intermediate / advanced / mixed 其中之一。線格式為小寫;App 會在顯示時將首字母大寫。
hrm_required 必填boolean若訓練包中有任何訓練使用 HR_ZONE 則為 true。
type_mix 必填object每個分類佔總騎乘時間的百分比(總和為 100)。驅動安裝畫面上 App 內的類型組成圓環圖。
duration_histogram 必填object每個時長區間的訓練數量:0-3030-6060-9090+(分鐘)。驅動安裝畫面的時長圖表。
contents 必填array完整的每訓練項目 — 是資料庫清單項目形態的超集;每項包含 slugnameduration_seconds、摘要指標,以及用於縮圖渲染的 sparkline 陣列。

已下載的 .ytwpack 有兩種安裝路徑:

範例訓練包清單(為了可讀性已截短 contents):

{
  "schema_version": 1,
  "pack_id": "sweet-spot",
  "name": "Sweet Spot",
  "description": "26 sweet-spot sessions across classic intervals, sustained stacks, and over-unders.",
  "version": "1.0.2",
  "content_hash": "sha256:9432a3a76015158dc71ec63…",
  "generated_at": "2025-05-28T00:00:00Z",
  "set": "power",
  "category": "sweet-spot",
  "workout_count": 26,
  "total_ride_time_seconds": 119340,
  "experience_level": "intermediate",
  "hrm_required": false,
  "type_mix":   { "sweet-spot": 100 },
  "duration_histogram": { "0-30": 0, "30-60": 5, "60-90": 12, "90+": 9 },
  "contents": [
    { "slug": "sweet-spot-3x10min-at-88pct-ftp",  "name": "Sweet Spot 3×10min @ 88% FTP",  "duration_seconds": 3300, "tss": 55.8,  "intensity_factor": 0.764, "sparkline": […] },
    { "slug": "sweet-spot-3x15min-at-90pct-ftp",  "name": "Sweet Spot 3×15min @ 90% FTP",  "duration_seconds": 4500, "tss": 81.6,  "intensity_factor": 0.808, "sparkline": […] }
    /* … 24 more workouts … */
  ]
}

資料庫與訓練包清單

兩份清單與可下載的檔案一同發佈。兩者皆為純 JSON;兩者皆由可直接抓取的 JSON Schema 文件描述。

URL列出什麼JSON Schema
library/manifest.json 資料庫中每個精選的 .ytw — 提供瀏覽 / 搜尋 / 篩選客戶端的每訓練中繼資料。也列出可用的 .ytwpack 下載(檔案路徑、版本、內容雜湊、類型組成摘要、圖示 URL)。 /schemas/workout-manifest.json
packs/manifest.json 訓練包目錄端點:每個已發佈的 .ytwpack 及其摘要中繼資料。與資料庫清單的 packs 陣列具有相同的每訓練包項目形態;App 內的訓練包資料庫會在車手手動更新時抓取這個。 /schemas/workout-manifest.json
(每個 .ytwpack 內部) 作為壓縮檔根目錄下 manifest.json 攜帶的每訓練包清單 — 上方表格記錄了其形態。 /schemas/workout-pack-manifest.json

如果你正在打造消費這個資料庫的工具 — 自訂訓練瀏覽器、針對 .zwo 的訓練轉換器、能呈現訓練包的教練儀表板 — 這些是你需要驗證的合約。同樣的每訓練項目形態在兩份清單的 contents / workouts 陣列中都會出現,所以處理其中一個的客戶端也能處理另一個。

完整範例

漸進式間歇

一個 5 分鐘的熱身,透過線性插值從 40 % FTP 漸進到 75 % FTP:

{
  "id": "rampup",
  "duration": 300,
  "targetPowerPercent": 40,
  "targetPowerEndPercent": 75,
  "intensityZone": "Z1",
  "label": "Ramp up",
  "intervalType": "WARMUP"
}

高低交替

三組 2 分鐘 95 % FTP / 1 分鐘 105 % FTP,以重複群組表示:

{
  "repeat": 3,
  "intervals": [
    { "id": "under", "duration": 120, "targetPowerPercent": 95,  "intensityZone": "Z4", "label": "Under" },
    { "id": "over",  "duration": 60,  "targetPowerPercent": 105, "intensityZone": "Z5", "label": "Over"  }
  ]
}

心率區間訓練

30 分鐘 Z2 耐力騎乘,中段加入 3 分鐘 Z4 加速:

{
  "programId": "hr-z2-with-surge",
  "programName": "Z2 with a Z4 surge",
  "description": "Steady Zone 2 with a single 3-minute Zone 4 surge.",
  "totalDuration": 1800,
  "workoutType": "HR_ZONE",
  "primaryLocale": "en",
  "intervals": [
    { "id": "warmup",  "duration": 300,  "targetHrZone": 1, "intensityZone": "Z1", "label": "Warmup", "intervalType": "WARMUP" },
    { "id": "endure1", "duration": 600,  "targetHrZone": 2, "intensityZone": "Z2", "label": "Endurance" },
    { "id": "surge",   "duration": 180,  "targetHrZone": 4, "intensityZone": "Z4", "label": "Surge" },
    { "id": "endure2", "duration": 420,  "targetHrZone": 2, "intensityZone": "Z2", "label": "Endurance" },
    { "id": "cooldown","duration": 300,  "targetHrZone": 1, "intensityZone": "Z1", "label": "Cooldown", "intervalType": "COOLDOWN" }
  ]
}

多語言

最小甜蜜點範例,含 EN + DE + NL strings 區塊。同一個訓練,三種原生體驗:

{
  "programId": "my-sweet-spot",
  "programName": "Sweet Spot 30",
  "description": "A short sweet-spot workout.",
  "totalDuration": 1800,
  "workoutType": "POWER",
  "primaryLocale": "en",
  "intervals": [
    { "id": "warmup",   "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Warmup",     "intervalType": "WARMUP"   },
    { "id": "work",     "duration": 1200, "targetPowerPercent": 88, "intensityZone": "Z3", "label": "Sweet Spot", "intervalType": "INTERVAL",
      "cues": [
        { "offsetSec": 0,   "text": "Settle in" },
        { "offsetSec": 600, "text": "Halfway" }
      ]
    },
    { "id": "cooldown", "duration": 300,  "targetPowerPercent": 50, "intensityZone": "Z1", "label": "Cooldown",   "intervalType": "COOLDOWN" }
  ],
  "strings": {
    "en": {
      "name": "Sweet Spot 30",
      "description": "A short sweet-spot workout.",
      "labels": { "warmup": "Warmup", "work": "Sweet Spot", "cooldown": "Cooldown" },
      "cues":   { "work:0": "Settle in", "work:1": "Halfway" }
    },
    "de": {
      "name": "Sweet Spot 30",
      "description": "Ein kurzes Sweet-Spot-Training.",
      "labels": { "warmup": "Aufwärmen", "work": "Sweet Spot", "cooldown": "Ausrollen" },
      "cues":   { "work:0": "Locker einrollen", "work:1": "Halbzeit" }
    },
    "nl": {
      "name": "Sweet Spot 30",
      "description": "Een korte sweet-spot-training.",
      "labels": { "warmup": "Inrijden", "work": "Sweet Spot", "cooldown": "Uitrijden" },
      "cues":   { "work:0": "Rustig inrijden", "work:1": "Halverwege" }
    }
  }
}

常見陷阱

參考

進行程式化的格式規範查詢(欄位表、範例、約束、術語表)時,正規的機器可讀來源是 Your Trainer MCP — 從任何 MCP 客戶端呼叫 get_format_specget_canonical_examplesget_format_constraintsget_format_glossary。這些工具與驅動本頁的相同知識註冊表共用資料;若工具的回應曾與本頁分歧,則 MCP 為正規,本頁已過時。

JSON Schema(供基於這些格式建構的客戶端使用的機器可讀合約):

← 返回使用手冊與指南