diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 045d6e26b..68dcb858b 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -3369,7 +3369,7 @@ test:track` 43 tracking invariants · headless QA harnesses under ### Interface localization (October 2026) -Shipped locales: **en** (source and fallback), **es** and **ru**. The code lives in +Shipped locales: **en** (source and fallback), **es**, **ru** and **zh-CN**. The code lives in `src/i18n/`. How to mark UI text: [`docs/I18N.md`](./I18N.md); translator workflow: [`docs/TRANSLATORS.md`](./TRANSLATORS.md). @@ -3380,7 +3380,9 @@ Shipped locales: **en** (source and fallback), **es** and **ru**. The code lives value). English has no catalog; every other locale is `src/i18n/locales/.json`, and a missing entry falls back to English. - **Locale choice.** `?lang=` → stored choice (`gev:locale:v1`) → - `navigator.languages` → English. `?lang=` is not stored, and share links drop + `navigator.languages` → English. Any `zh` tag maps to `zh-CN` except + Traditional ones (`Hant`, `TW`, `HK`, `MO`), which fall through until a + Traditional catalog ships. `?lang=` is not stored, and share links drop it, so a recipient keeps their own language. The globe picker in the bottom-right corner (`src/ui/localePicker.js`) stores the choice, drops `?lang=` (keeps it only when storage is blocked) and reloads. It asks first diff --git a/docs/TRANSLATORS.md b/docs/TRANSLATORS.md index 2131ab7a3..538f8c50e 100644 --- a/docs/TRANSLATORS.md +++ b/docs/TRANSLATORS.md @@ -1,6 +1,6 @@ # Translating God's Eye View -Shipped languages: **English** (source and fallback), **Spanish** (`es`) and **Russian** (`ru`). +Shipped languages: **English** (source and fallback), **Spanish** (`es`), **Russian** (`ru`) and **Simplified Chinese** (`zh-CN`). ## How it works @@ -63,4 +63,4 @@ These are contracts, not copy: layer ids and registry keys, status enums and oth ## Language notes -Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/.md`: [Spanish](./translations/es.md), [Russian](./translations/ru.md). Read it before translating, and add a term there when you settle on one. +Each language keeps its glossary, plural forms, units and style in its own file, `docs/translations/.md`: [Spanish](./translations/es.md), [Russian](./translations/ru.md), [Simplified Chinese](./translations/zh-CN.md). Read it before translating, and add a term there when you settle on one. diff --git a/docs/translations/zh-CN.md b/docs/translations/zh-CN.md new file mode 100644 index 000000000..9b779e0b7 --- /dev/null +++ b/docs/translations/zh-CN.md @@ -0,0 +1,59 @@ +# Simplified Chinese (`zh-CN`) + +Catalog: `src/i18n/locales/zh-CN.json`. General rules: [TRANSLATORS.md](../TRANSLATORS.md). + +The catalog started from the Chinese locale by abiyeenzo (#914) and the +Simplified Chinese localization by EDIT-Aeterna (#979); the rest was translated +for the current English and reviewed against both. + +## Style + +- Mainland usage and Simplified characters. Address the user neutrally, without 您/你 where a plain imperative works: 「点击地图」, 「重试」. +- Chinese has no case: an UPPERCASE English HUD label becomes the same short Chinese label. Keep labels as short as the English: 「实时」, 「过期」, 「关」. +- Full-width punctuation in Chinese text: ,。:;?!()“”. A dash is 「——」 without spaces; keep `·` as the HUD separator and `…` for ellipsis. +- One space between Chinese and Latin letters or digits: 「AIS 船舶」, 「24 小时」, 「{count} 个」. No space next to full-width punctuation. +- Units: km, m, km/h, m/s, kn, ft, MW, °, % stay as written; time words are 小时, 分钟, 秒 after a number (「5 分钟前」). +- Product names, provider names and codes stay as they are: Google, Cesium, CelesTrak, NOAA, FIRMS, AIS, ADS-B, GNSS, CCTV, HUD, CRT, NVG, FLIR, WHOT/BHOT, Ironbow, CONUS. + +## Plurals + +`Intl.PluralRules('zh-CN')` has one category, `other`: a `tn()` entry has only that form. Use a measure word next to the number where Chinese needs one: 「{count} 个密钥」, 「{count} 架飞机」, 「{count} 艘船」. + +## Glossary + +| English | 中文 | +| ------------------------------------- | --------------------------- | +| layer / data layer | 图层 / 数据图层 | +| feed | 数据源 | +| source / data source | 来源 / 数据源 | +| LIVE (status) / live data | 实时 / 实时数据 | +| SIMULATED | 模拟 | +| stale / degraded / unavailable | 过期 / 降级 / 不可用 | +| retry | 重试 | +| key (API key) / POWER UP | 密钥(API 密钥)/ 增强 | +| Provider Settings | 服务商设置 | +| globe / map / map source / basemap | 地球 / 地图 / 地图源 / 底图 | +| aircraft / flight / military flights | 飞机 / 航班 / 军用航班 | +| vessel, ship | 船舶 | +| satellite | 卫星 | +| launch (rocket) / space mission | 发射 / 太空任务 | +| contact (tracked object) | 目标 | +| installation, site | 设施 | +| camera / CCTV camera | 摄像头 / CCTV 摄像头 | +| earthquake / active fire | 地震 / 活跃火点 | +| scene / shot | 场景 / 镜头 | +| briefing / panel / dock | 简报 / 面板 / 停靠栏 | +| preset / visual preset | 预设 / 视觉预设 | +| Display / HUD layout | 显示 / HUD 布局 | +| Tactical / Operator / Minimal / Cyber | 战术 / 操作员 / 精简 / 赛博 | +| Scope (circular viewport mask) | 视域 | +| Clean UI | 纯净界面 | +| cockpit | 座舱 | +| heading / altitude / ground speed | 航向 / 高度 / 地速 | +| Street Level / Street View (Google) | 街景 / 街景(Google) | +| Recent Imagery | 近期影像 | +| Global Context / Context (panel) | 全局态势 / 态势 | +| radio (station) | 电台 | +| transit / bikeshare | 公共交通 / 共享单车 | +| Previous / Next | 上一个 / 下一个 | +| compass points (N, NNE, …) | 北, 北东北, … | diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index 264f58440..bbbc39574 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -426,7 +426,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": [] }, @@ -451,7 +452,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -467,7 +469,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": [] }, @@ -495,6 +498,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js", "src/ui/keyGuidance.js", "src/ui/statusText.js" @@ -513,7 +517,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": [] }, @@ -528,7 +533,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": [] }, @@ -546,6 +552,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": [] @@ -570,6 +577,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/layerNames.js" ], "external": [] @@ -603,6 +611,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": ["cesium"] @@ -617,6 +626,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": [] @@ -765,6 +775,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js", "src/ui/keyGuidance.js", "src/ui/layerNames.js", @@ -882,6 +893,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": ["cesium"] @@ -947,6 +959,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": ["cesium"] @@ -992,7 +1005,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1052,7 +1066,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1081,6 +1096,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js", "src/layers/recentImagery/display.js" ], @@ -1109,7 +1125,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1140,6 +1157,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": ["cesium", "satellite.js"] @@ -1171,7 +1189,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1213,6 +1232,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/layers/installations/classLabel.js" ], "external": ["cesium", "@mapbox/vector-tile", "pbf"] @@ -1247,6 +1267,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": ["cesium"] @@ -1295,7 +1316,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium", "pbf", "@mapbox/vector-tile"] }, @@ -1323,7 +1345,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1370,7 +1393,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["hls.js", "cesium"] }, @@ -1435,7 +1459,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium", "pbf", "@mapbox/vector-tile", "mapillary-js"] }, @@ -1462,7 +1487,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium", "@mapbox/vector-tile", "pbf"] }, @@ -1501,7 +1527,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1522,7 +1549,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1577,7 +1605,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -1633,6 +1662,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/statusText.js" ], "external": [] @@ -2486,6 +2516,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js", "src/layers/recentImagery/display.js", "src/ui/keyGuidance.js", @@ -3059,6 +3090,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js", "src/layers/recentImagery/display.js", "src/layers/installations/classLabel.js", @@ -3164,6 +3196,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/i18n/index.js" ], "external": ["cesium"] @@ -3184,7 +3217,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": [] }, @@ -3216,7 +3250,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -3240,7 +3275,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": [] }, @@ -3331,6 +3367,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/unitLabels.js" ], "external": ["cesium"] @@ -3385,6 +3422,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/statusText.js" ], "external": [] @@ -3636,7 +3674,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium", "egm96-universal", "pbf"] }, @@ -3678,7 +3717,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium", "pbf"] }, @@ -3730,7 +3770,8 @@ "src/i18n/index.js", "src/i18n/locale.js", "src/i18n/locales/es.json", - "src/i18n/locales/ru.json" + "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json" ], "external": ["cesium"] }, @@ -3746,6 +3787,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/statusText.js", "src/ui/layerNames.js" ], @@ -3938,6 +3980,7 @@ "src/i18n/locale.js", "src/i18n/locales/es.json", "src/i18n/locales/ru.json", + "src/i18n/locales/zh-CN.json", "src/ui/statusText.js", "src/ui/layerNames.js" ], diff --git a/src/data/bhoteKoshiEvent.js b/src/data/bhoteKoshiEvent.js index 2baaf7031..434aff6d0 100644 --- a/src/data/bhoteKoshiEvent.js +++ b/src/data/bhoteKoshiEvent.js @@ -412,14 +412,18 @@ const IMAGERY_LABEL_NAMES = Object.freeze([ [/^(\d{4}) POST-EVENT$/, msg('{year} POST-EVENT')], ]); -/** Day, short month and year in the active language: 16 OCT 2021, 16 ОКТ. 2021. */ +/** Day, short month and year in the active language: 16 OCT 2021, 16 ОКТ. 2021, 2021年10月16日. */ function imageryDateText(observedAt) { const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(observedAt || '')); if (!match) return ''; const date = new Date(Date.UTC(+match[1], +match[2] - 1, +match[3])); if (Number.isNaN(date.getTime())) return ''; const part = (options) => formatDate(date, { timeZone: 'UTC', ...options }); - return `${part({ day: 'numeric' })} ${part({ month: 'short' })} ${part({ year: 'numeric' })}`.toUpperCase(); + const year = part({ year: 'numeric' }); + // Year-first languages (2021年10月16日) keep their own order and units. + const whole = part({ day: 'numeric', month: 'short', year: 'numeric' }); + if (whole.startsWith(year)) return whole; + return `${part({ day: 'numeric' })} ${part({ month: 'short' })} ${year}`.toUpperCase(); } /** @@ -1029,7 +1033,7 @@ function createPanel(event, handlers) { ${escapeMarkup(t('GEOLOCATIONS · {credit}', { credit: 'GEO GEORGE SHADRACH' }))} -

${escapeMarkup(t('Clouds are preserved from the source imagery.'))}

+

${escapeMarkup(t(' Clouds are preserved from the source imagery.'))}

`; const refs = collectPanelReferences(panel); diff --git a/src/hudLocalityFormat.js b/src/hudLocalityFormat.js index 4a82fdbb3..84278564b 100644 --- a/src/hudLocalityFormat.js +++ b/src/hudLocalityFormat.js @@ -35,7 +35,16 @@ export const LOCALIZED_LOCALITY = Object.freeze({ }), sector: ({ latDeg, lonDeg }) => t('SECTOR {lat} {lon}', { - lat: hudCoordinate(latDeg, t('N'), t('S')), - lon: hudCoordinate(lonDeg, t('E'), t('W')), + // Hemisphere letters, not compass words: 21.33N, never 21.33北. + lat: hudCoordinate( + latDeg, + t('N', { context: 'hemisphere' }), + t('S', { context: 'hemisphere' }), + ), + lon: hudCoordinate( + lonDeg, + t('E', { context: 'hemisphere' }), + t('W', { context: 'hemisphere' }), + ), }), }); diff --git a/src/i18n/i18n.test.mjs b/src/i18n/i18n.test.mjs index a662130a1..70eaf7e86 100644 --- a/src/i18n/i18n.test.mjs +++ b/src/i18n/i18n.test.mjs @@ -35,6 +35,12 @@ test('normalizeLocale folds regional variants and rejects unshipped tags', () => assert.equal(normalizeLocale(' RU '), 'ru'); assert.equal(normalizeLocale('en_GB'), 'en'); assert.equal(normalizeLocale('xx'), null); + // Simplified Chinese by default; Traditional tags wait for zh-TW. + assert.equal(normalizeLocale('zh'), 'zh-CN'); + assert.equal(normalizeLocale('zh-CN'), 'zh-CN'); + assert.equal(normalizeLocale('zh-Hans-SG'), 'zh-CN'); + assert.equal(normalizeLocale('zh-TW'), null); + assert.equal(normalizeLocale('zh-Hant-HK'), null); assert.equal(normalizeLocale(undefined), null); }); diff --git a/src/i18n/index.js b/src/i18n/index.js index ccc54a5e6..d5698aa5b 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -18,8 +18,9 @@ import { } from './locale.js'; import es from './locales/es.json' with { type: 'json' }; import ru from './locales/ru.json' with { type: 'json' }; +import zhCN from './locales/zh-CN.json' with { type: 'json' }; -const CATALOGS = Object.freeze({ es, ru }); +const CATALOGS = Object.freeze({ es, ru, 'zh-CN': zhCN }); let currentLocale = DEFAULT_LOCALE; diff --git a/src/i18n/locale.js b/src/i18n/locale.js index 626303ad7..fab95e5cc 100644 --- a/src/i18n/locale.js +++ b/src/i18n/locale.js @@ -8,25 +8,32 @@ export const LOCALE_STORAGE_KEY = 'gev:locale:v1'; export const DEFAULT_LOCALE = 'en'; /** Shipped locales; order does not matter. Adding one: docs/TRANSLATORS.md. */ -export const LOCALES = Object.freeze(['en', 'es', 'ru']); +export const LOCALES = Object.freeze(['en', 'es', 'ru', 'zh-CN']); /** Each locale named in its own language, for the selector. */ export const LOCALE_NAMES = Object.freeze({ en: 'English', es: 'Español', ru: 'Русский', + 'zh-CN': '简体中文', }); /** Right-to-left locales. */ const RTL_LOCALES = Object.freeze([]); /** - * Map any language tag to a shipped locale: 'ru-RU' and 'RU' → 'ru'. + * Map any language tag to a shipped locale: 'ru-RU' and 'RU' → 'ru', + * 'zh', 'zh-CN' and 'zh-Hans-SG' → 'zh-CN'. * @returns {string|null} */ export function normalizeLocale(candidate) { if (typeof candidate !== 'string') return null; - const primary = candidate.trim().toLowerCase().split(/[-_]/)[0]; + const [primary, ...rest] = candidate.trim().toLowerCase().split(/[-_]/); + // Traditional Chinese readers are not given Simplified: no zh-TW yet. + if (primary === 'zh') + return rest.some((part) => ['hant', 'tw', 'hk', 'mo'].includes(part)) + ? null + : 'zh-CN'; return LOCALES.includes(primary) ? primary : null; } diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index f93f42070..b0d4dbef5 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -1096,7 +1096,7 @@ "FIELD REPORTS · {count}": "INFORMES DE CAMPO · {count}", "GEOLOCATIONS · {credit}": "GEOLOCALIZACIONES · {credit}", "↗ OPEN PUBLIC MAP": "↗ ABRIR MAPA PÚBLICO", - "Clouds are preserved from the source imagery.": "Las nubes provienen de las imágenes de origen.", + " Clouds are preserved from the source imagery.": " Las nubes provienen de las imágenes de origen.", "Show the Nepal context callout without a border or country fill": "Muestra la etiqueta de contexto de Nepal sin frontera ni relleno del país", "Introduce Nepal, draw its border, then reveal the Bhote Koshi incident": "Presenta Nepal, dibuja su frontera y luego revela el incidente del Bhote Koshi", "CITIES": "CIUDADES", @@ -2890,5 +2890,9 @@ "USB work abandoned after a stalled transfer": "Trabajo USB cancelado tras una transferencia bloqueada", "SDR decoder did not start; receiver stopped": "El decodificador SDR no arrancó; receptor detenido", "SDR decoder stopped responding; receiver stopped": "El decodificador SDR dejó de responder; receptor detenido", - "SDR decoder fell behind; receiver stopped": "El decodificador SDR se quedó atrás; receptor detenido" + "SDR decoder fell behind; receiver stopped": "El decodificador SDR se quedó atrás; receptor detenido", + "hemisphere|N": "N", + "hemisphere|S": "S", + "hemisphere|E": "E", + "hemisphere|W": "O" } diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 6a58ab901..cce27c3d4 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -1110,7 +1110,7 @@ "FIELD REPORTS · {count}": "СООБЩЕНИЯ С МЕСТА · {count}", "GEOLOCATIONS · {credit}": "ГЕОЛОКАЦИИ · {credit}", "↗ OPEN PUBLIC MAP": "↗ ОТКРЫТЬ ПУБЛИЧНУЮ КАРТУ", - "Clouds are preserved from the source imagery.": "Облака оставлены такими, как на исходных снимках.", + " Clouds are preserved from the source imagery.": " Облака оставлены такими, как на исходных снимках.", "Show the Nepal context callout without a border or country fill": "Показать выноску с контекстом Непала без границы и заливки страны", "Introduce Nepal, draw its border, then reveal the Bhote Koshi incident": "Представить Непал, нарисовать его границу, затем показать инцидент на Bhote Koshi", "CITIES": "ГОРОДА", @@ -2957,5 +2957,9 @@ "USB work abandoned after a stalled transfer": "Работа с USB прервана: передача данных зависла", "SDR decoder did not start; receiver stopped": "Декодер SDR не запустился; приёмник остановлен", "SDR decoder stopped responding; receiver stopped": "Декодер SDR перестал отвечать; приёмник остановлен", - "SDR decoder fell behind; receiver stopped": "Декодер SDR не успевает; приёмник остановлен" + "SDR decoder fell behind; receiver stopped": "Декодер SDR не успевает; приёмник остановлен", + "hemisphere|N": "С", + "hemisphere|S": "Ю", + "hemisphere|E": "В", + "hemisphere|W": "З" } diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 3a5972058..0a56bc76a 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -7,12 +7,16 @@ }, "Those points are in a line — move one off it to enclose an area.": "这些点都在同一条直线上——请移开其中一个点才能围出面。", "That line has no length — click somewhere further away.": "这条线没有长度——请在更远处点击。", + " · {max}-point limit reached": " · 已达 {max} 个点上限", + "{measure} · double-click or Enter to finish, Backspace undoes, Esc cancels.{full}": "{measure} · 双击或按 Enter 完成,按 Backspace 撤销,按 Esc 取消。{full}", + "drawings on the map": "地图上的绘图", "That shape already has {count} points — finish it or press Backspace.": "该形状已有 {count} 个点——请完成绘制或按 Backspace。", "That point is off the globe — click on the world.": "该点在地球之外——请点击地球上的位置。", "That shape could not be placed.": "该形状无法放置。", "Could not place the shape: {error}": "无法放置形状:{error}", "Board cleared.": "画板已清除。", "{owner} is using the pointer — close it first.": "{owner} 正在使用指针——请先关闭它。", + "{label} · Detailed outline unavailable": "{label} · 无法获取详细轮廓", "Flying to Austin, TX...": "正在飞往德克萨斯州奥斯汀…", "Restoring shared view...": "正在恢复共享视图…", "Configuring viewer...": "正在配置视图…", @@ -20,6 +24,9 @@ "Loading the keyless globe...": "正在加载免密钥地球…", "Google 3D Tiles unavailable ({detail}). Loading the keyless globe...": "Google 3D Tiles 不可用({detail})。正在加载免密钥地球…", "Initializing systems...": "正在初始化系统…", + "Space Missions isolates replay data. Exit the mode to enable {layer}.": "太空任务模式会隔离回放数据。退出该模式后才能启用“{layer}”。", + "Data attribution": "数据来源说明", + "Data provided by:": "数据提供方:", "Close data attribution": "关闭数据来源说明", "Light aircraft": "轻型飞机", "Glider": "滑翔机", @@ -31,18 +38,86 @@ "Fast jet": "喷气战机", "Business jet": "公务机", "Drone": "无人机", + "General emergency": "一般紧急情况", + "Unlawful interference": "非法干扰", + "Radio failure": "无线电失效", + "Downed aircraft": "飞机坠毁", + "Minimum fuel": "最低油量", + "Lifeguard (medical priority)": "救护飞行(医疗优先)", + "Rasuwagadhi witness video": "Rasuwagadhi 目击视频", + "Bhote Koshi valley footage": "Bhote Koshi 河谷画面", + "Flood footage": "洪水画面", + "Compilation and casualty claims": "视频合集与伤亡说法", + "SOURCE GEOCONFIRMED · WORLD-PINNED": "来源已地理核实 · 已标定于地图", + "SOURCE CREDITED · LOCATION UNRESOLVED": "来源已署名 · 位置未确定", + "REFERENCE ONLY · CLAIMS UNVERIFIED": "仅供参考 · 说法未核实", + "SOURCE GEOCONFIRMED": "来源已地理核实", + "2021 historical reference versus 2026 post-event imagery; changes are cumulative and include the July 2025 Rasuwagadhi flood. Schematic downstream progression, not modeled arrival time; not an official hazard model.": "2021 年历史参考影像与 2026 年灾后影像对比;变化为累计结果,包含 2025 年 7 月的 Rasuwagadhi 洪水。下游演进为示意,并非模拟的到达时间;也不是官方灾害模型。", + "Immediate collapse viewpoint": "近距离崩塌观察点", + "Collapse viewpoint": "崩塌观察点", + "Debris-dammed lake": "碎屑堰塞湖", + "Dammed lake": "堰塞湖", + "Second landslide": "第二次滑坡", + "Gyirong border gate": "Gyirong 边境口岸", + "Border gate": "边境口岸", + "Timure evidence cluster": "Timure 证据群", + "Mailung / Upper Trishuli bridge": "Mailung / Upper Trishuli 桥", + "Dandagaun viewpoint restaurant": "Dandagaun 观景餐厅", + "Dandagaun viewpoint": "Dandagaun 观景点", + "Bidur - Trishuli bridge": "Bidur - Trishuli 桥", + "Bidur bridge": "Bidur 桥", + "Devighat / Taadi Khola Bridge": "Devighat / Taadi Khola 桥", + "Devighat bridge": "Devighat 桥", + "Facebook player API did not initialize.": "Facebook 播放器 API 未能初始化。", + "Facebook player API could not load.": "无法加载 Facebook 播放器 API。", + "Facebook player API timed out.": "Facebook 播放器 API 超时。", + "X embed API did not initialize.": "X 嵌入 API 未能初始化。", + "X embed could not load.": "无法加载 X 嵌入内容。", + "X embed timed out.": "X 嵌入内容超时。", "UNAVAILABLE": "不可用", + "TIMEOUT": "超时", + "BLOCKED": "被拦截", "SOURCE": "来源", + "Facebook embed unavailable. The post may be private, removed, region-restricted, or blocked.": "Facebook 嵌入内容不可用。该帖子可能为私密、已删除、受地区限制或被拦截。", + "Preloading {provider} media": "正在预加载 {provider} 媒体", + "LOADING X POST…": "正在加载 X 帖子…", + "The X post is unavailable or cannot be embedded.": "该 X 帖子不可用或无法嵌入。", + "X POST · PROVIDER CONTROLS": "X 帖子 · 平台控件", + "{reason} OPEN ORIGINAL.": "{reason} 请打开原始来源。", + "X embed unavailable": "X 嵌入内容不可用。", + "FACEBOOK · PROVIDER CONTROLS": "FACEBOOK · 平台控件", + "Facebook embed unavailable": "Facebook 嵌入内容不可用。", + "AUTOPLAY BLOCKED · USE PROVIDER CONTROLS": "自动播放被拦截 · 请使用平台控件", + "Embedded {provider} media for {source}": "{source} 的嵌入 {provider} 媒体", + "witness source": "目击来源", + "Witness source": "目击来源", + "{provider} · EMBEDDED SOURCE": "{provider} · 嵌入来源", + "OPEN ORIGINAL ↗": "打开原始来源 ↗", + "{provider} media: {source}": "{provider} 媒体:{source}", + "{provider} · PROVIDER CONTROLS": "{provider} · 平台控件", + "YOUTUBE {state} · OPEN ORIGINAL": "YOUTUBE {state} · 打开原始来源", + "INITIATION": "起始", + "DAMMING": "堰塞", + "SECONDARY FAILURE": "二次溃决", + "BORDER ARRIVAL": "抵达边境", + "PROPAGATION": "演进", + "DOWNSTREAM PASSAGE": "向下游推进", + "CONSEQUENCE": "后果", + "DOWNSTREAM CONSEQUENCE": "下游后果", "EVIDENCE": "证据", + "{year} HISTORICAL REFERENCE": "{year} 年历史参考", + "{year} POST-EVENT": "{year} 年灾后", "{count} GEOLOCATED SOURCE-MAP PLACEMENTS": { "other": "{count} 个地理定位来源点位" }, "NO PREVIEW": "无预览", + "YouTube source": "YouTube 来源", + "Facebook source": "Facebook 来源", "SOURCE LINK": "来源链接", "{publisher} / OPEN": "{publisher} / 打开", "Bhote Koshi flood reconstruction controls": "Bhote Koshi 洪水重构控制", "EVENT RECONSTRUCTION · 26 AUG 2026": "事件重构 · 2026-08-26", - "BHOTE KOSHI OUTBURST FLOOD": "BHOTE KOSHI 冰湖溃决洪水", + "BHOTE KOSHI OUTBURST FLOOD": "BHOTE KOSHI 溃决洪水", "Close event layer": "关闭事件图层", "OBSERVED IMAGERY": "实测影像", "SCHEMATIC CORRIDOR": "示意走廊", @@ -63,12 +138,13 @@ "↗ OPEN ORIGINAL": "↗ 打开原始来源", "⌖ LOWER GORGE": "⌖ 下游峡谷", "FIELD REPORTS · {count}": "现场报道 · {count}", + "GEOLOCATIONS · {credit}": "地理定位 · {credit}", "↗ OPEN PUBLIC MAP": "↗ 打开公共地图", - "Clouds are preserved from the source imagery.": " 云层按源影像原样保留。", + " Clouds are preserved from the source imagery.": "云层按源影像原样保留。", "Rasuwagadhi witness": "Rasuwagadhi 目击", "Open original source for {source}; capture time unverified": "打开 {source} 的原始来源;拍摄时间未核实", "Historical reference and post-event image divider": "历史参考与灾后影像的分割线", - "A historical reference {before} percent, B post-event {after} percent": "历史参考 {before}%,灾后 {after}%", + "A historical reference {before} percent, B post-event {after} percent": "A 历史参考 {before}%,B 灾后 {after}%", "■ RELEASE CAMERA": "■ 释放相机", "Seek Nepal scene clock": "定位尼泊尔场景时钟", "NEPAL SCENE CLOCK": "尼泊尔场景时钟", @@ -86,7 +162,7 @@ "{count} SOURCES": { "other": "{count} 个来源" }, - "OUTSIDE IMAGERY SWIPE": "影像范围外滑动", + "OUTSIDE IMAGERY SWIPE": "不在影像对比范围内", "{scene} / AUTHORED SHOT": "{scene} / 排定镜头", "NEPAL FLOOD INCIDENT": "尼泊尔洪水事件", "Play {shot}": "播放 {shot}", @@ -94,38 +170,103 @@ "Play reconstruction": "播放重构", "Load previous scene shot": "载入上一个场景镜头", "Load next scene shot": "载入下一个场景镜头", + "SCHEMATIC CORRIDOR, NOT MODELED ARRIVAL TIME.": "示意走廊,并非模拟的到达时间。", "Rasuwa · 26 Aug 2026": "Rasuwa · 2026-08-26", "Event reconstruction": "事件重构", + "BHOTE KOSHI FLOOD INCIDENT": "BHOTE KOSHI 洪水事件", + "NEPAL · 26 AUG 2026": "尼泊尔 · 2026-08-26", + "USGS INITIAL REPORT": "USGS 初始报告", + "26 AUG 2026 · 08:37 NPT": "2026-08-26 · 08:37 NPT", + "28.2710° N · 85.5150° E": "北纬 28.2710° · 东经 85.5150°", + "INITIAL M4.4 · RECLASSIFIED M5.2 LANDSLIDE": "初报 M4.4 · 重新归类为 M5.2 滑坡", + "USGS EVENT us7000tbwb · OPEN SOURCE": "USGS 事件 us7000tbwb · 打开来源", + "Open the USGS initial report for the 26 August 2026 Nepal landslide": "打开 USGS 关于 2026 年 8 月 26 日尼泊尔滑坡的初始报告", + "Show the Nepal context callout without a border or country fill": "显示尼泊尔背景标注,不绘制边界和国家填充", + "Introduce Nepal, draw its border, then reveal the Bhote Koshi incident": "介绍尼泊尔,绘制其边界,再展示 Bhote Koshi 事件", + "CITIES": "城市", + "Keep the regional incident context and introduce nearby city labels": "保留区域事件背景,并加入附近城市标注", + "PLACES": "地点", + "Introduce the reconstruction scene key places from trigger to downstream": "依次介绍重构场景的关键地点,从触发点到下游", + "PATH": "路径", + "Animate the sourced cyan river-centerline route to Trishuli Bazaar": "以动画展示有来源依据的青色河道中线路径,直至 Trishuli Bazaar", + "OVERVIEW": "总览", + "Keep the completed flood path and location labels visible": "保持完整的洪水路径和地点标注可见", + "RECORD": "记录", + "Reveal the trigger record while making a slow camera approach": "镜头缓慢推近,同时展示触发事件记录", + "{speed} kn": "{speed} kn", + "That file is not valid JSON.": "该文件不是有效的 JSON。", + "That JSON file is not GeoJSON.": "该 JSON 文件不是 GeoJSON。", + "That FeatureCollection has no features list.": "该 FeatureCollection 没有 features 列表。", + "Every GeoJSON feature must have type Feature.": "每个 GeoJSON 要素的类型都必须是 Feature。", + "That GeoJSON has invalid geometries.": "该 GeoJSON 包含无效的几何图形。", + "That GeoJSON contains an unsupported geometry.": "该 GeoJSON 包含不受支持的几何图形。", + "That GeoJSON has invalid coordinates.": "该 GeoJSON 包含无效坐标。", + "That file creates more than {limit} map objects (the limit).": "该文件生成的地图对象超过上限 {limit} 个。", + "That file has more than {limit} style definitions and image links after cleanup (the limit).": "清理后,该文件的样式定义和图像链接仍超过上限 {limit} 个。", + "That file has more than {limit} features (the limit).": "该文件的要素超过上限 {limit} 个。", + "That file has more than {limit} points (the limit) across {features} features.": "该文件的点数超过上限 {limit} 个(共 {features} 个要素)。", + "That file has more than {limit} points (the limit).": "该文件的点数超过上限 {limit} 个。", + "That KMZ unpacks to more than the import limit.": "该 KMZ 解压后超过导入上限。", + "That KMZ is damaged and could not be unpacked.": "该 KMZ 已损坏,无法解压。", + "That KMZ is not a readable zip archive.": "该 KMZ 不是可读取的 zip 压缩包。", + "That KMZ uses a zip format this import does not read.": "该 KMZ 使用的 zip 格式无法导入。", + "That KMZ is encrypted.": "该 KMZ 已加密。", + "That KMZ uses a compression method this import does not read.": "该 KMZ 使用的压缩方式无法导入。", + "That KML has invalid geometry bounds.": "该 KML 的几何范围无效。", + "That KML has invalid coordinates.": "该 KML 包含无效坐标。", + "That GPX file has no document.": "该 GPX 文件没有 文档。", + "That {format} file is not valid XML.": "该 {format} 文件不是有效的 XML。", + "That file has an unreadable image or exceeds the 16-megapixel image limit.": "该文件包含无法读取的图像,或图像超过 1600 万像素上限。", + "That KML could not be read.": "无法读取该 KML。", + "That file has invalid point coordinates.": "该文件的点坐标无效。", + "Ground heights are not available for that file. Try again when the map has loaded.": "该文件无法获取地面高度。请在地图加载完成后重试。", + "That file took too long to read.": "读取该文件耗时过长。", + "Nothing to import.": "没有可导入的内容。", + "That file is larger than {size} MiB.": "该文件大于 {size} MiB。", + "Only GeoJSON, KML, KMZ and GPX files can be imported.": "只能导入 GeoJSON、KML、KMZ 和 GPX 文件。", + "That file has nothing to draw.": "该文件没有可在地图上绘制的内容。", + "That KMZ holds no KML document.": "该 KMZ 中没有 KML 文档。", + "That {format} file could not be read.": "无法读取该 {format} 文件。", + "That file has {count} map objects; the limit is {limit}.": "该文件有 {count} 个地图对象,上限为 {limit} 个。", + "Ground heights could not be read. Try that file again.": "无法读取地面高度。请重新导入该文件。", + "That file has invalid point graphics.": "该文件的点样式无效。", "Overpass rate-limited": "Overpass 已限流", "Overpass timed out": "Overpass 超时", "Overpass could not complete the query": "Overpass 无法完成查询", "Map tiles temporarily unavailable": "地图瓦片暂时不可用", "Mapped names temporarily unavailable": "地点名称暂时不可用", "Overpass temporarily unavailable": "Overpass 暂时不可用", - "Retrying mapped sites…": "正在重试地图站点…", - "Fetching mapped sites…": "正在获取地图站点…", + "Retrying mapped sites…": "正在重试加载地图标注设施…", + "Fetching mapped sites…": "正在获取地图标注设施…", "{reason} — retrying in {seconds}s": "{reason}——{seconds} 秒后重试", "{reason} — retry pending": "{reason}——等待重试", "Zoom in to search mapped installations": "请放大地图以搜索地图标注的设施", - "Showing cached mapped sites": "正在显示缓存的地图站点", - "Mapped sites not loaded": "地图站点未加载", - "Mapped sites loaded": "已加载地图站点", + "Showing cached mapped sites": "正在显示缓存的地图标注设施", + "Mapped sites not loaded": "地图标注设施未加载", + "Mapped sites loaded": "已加载地图标注设施", + "Military airfield": "军用机场", + "Naval base": "海军基地", + "Firing range": "靶场", + "Military land": "军事用地", "Mapped installation": "地图标注设施", + "Military area": "军事区域", + "flight": "航班", "military flight": "军用航班", + "satellite": "卫星", "feed unavailable": "数据源不可用", "feed stale": "数据源过期", - "observed or mapped nearby context": "附近有观测或地图标注的上下文", + "observed or mapped nearby context": "附近的观测或地图标注态势", "no observed or mapped objects in current feeds": "当前数据源中没有观测或地图标注的对象", "Draw": "绘制", "Directions": "路线导航", "Recent imagery": "近期影像", - "STATION": "电台", + "STATION": "空间站", "Crewed stations and their visiting vehicles": "载人空间站及其访问飞行器", "NAV": "导航", "GNSS navigation — GPS, GLONASS, Galileo": "GNSS 导航:GPS、GLONASS、Galileo", "GEO": "GEO", "Geostationary belt — comms and weather, fixed over the equator": "地球静止轨道带:通信和气象,固定在赤道上空", - "VISUAL": "可见", + "VISUAL": "目视可见", "Brightest naked-eye objects — CelesTrak visual group": "最亮的肉眼可见天体:CelesTrak visual 组", "COMMS": "通信", "Broadband constellation shell — shown only in DENSE mode": "宽带星座:仅在“密集”模式下显示", @@ -168,10 +309,10 @@ "NVG": "NVG", "FLIR": "FLIR", "ANIME": "动漫", - "NOIR": "黑色电影", + "NOIR": "黑白", "SNOW": "雪景", "ARCTIC": "北极", - "ANTARCTIC": "南极洲", + "ANTARCTIC": "南极", "NORTH AMERICA": "北美洲", "SOUTH AMERICA": "南美洲", "EUROPE": "欧洲", @@ -186,16 +327,51 @@ "PARTIAL": "部分", "FALLBACK": "备用", "{state} {layers}": "{state} {layers}", + "view band|STREET": "街道", + "view band|CITY": "城市", + "view band|METRO": "都市圈", + "view band|REGIONAL": "区域", + "view band|GLOBAL": "全球", "SUMMARY": "摘要", "Awaiting telemetry...": "等待遥测数据…", "REC": "REC", "AIS: --": "AIS: --", + "N/A": "无", + "{mode} {band} {locality} | {region} | ALT {alt} | WINDOW {window} | SUN {sun}° | ONA {ona}° | {zone}": "{mode} {band} {locality} | {region} | ALT {alt} | 视窗 {window} | SUN {sun}° | ONA {ona}° | {zone}", + "NEAR {poi} ({city}) {distance}": "邻近 {poi}({city}){distance}", "SECTOR {lat} {lon}": "扇区 {lat} {lon}", + "N": "北", + "S": "南", + "E": "东", + "W": "西", "POWER UP · {count} KEYS WAITING": { - "other": "启动 · {count} 个密钥待配置" + "other": "增强 · {count} 个密钥待配置" }, - "POWERED UP": "已启动", + "POWERED UP": "已增强", + "Provider Settings does not answer proxied requests": "服务商设置不响应经代理的请求", + "Provider Settings is disabled while sharing is enabled": "启用共享时,服务商设置不可用", + "Provider Settings answers only the machine running the server": "服务商设置只响应运行服务器的本机", + "Provider Settings answers only local hostnames": "服务商设置只响应本地主机名", + "Provider Settings requires an exact local Origin": "服务商设置要求完全匹配的本地 Origin", + "Unrecognized Origin refused": "已拒绝无法识别的 Origin", + "Cross-origin requests are refused": "拒绝跨源请求", + "Content-Type must be application/json": "Content-Type 必须为 application/json", + "Method not allowed": "不允许的方法", + "Request too large": "请求过大", "Invalid JSON": "无效的 JSON", + "Body must be a JSON object of {ENV_VAR: value}": "请求体必须是 {ENV_VAR: value} 形式的 JSON 对象", + "No keys provided": "未提供密钥", + "At most {limit} keys per save": "每次最多保存 {limit} 个密钥", + "Unknown key: {name}": "未知密钥:{name}", + "{name} must be a string": "{name} 必须是字符串", + "{name} is empty": "{name} 为空", + "{name} is longer than any real key ({limit} max)": "{name} 比任何真实密钥都长(最多 {limit} 个字符)", + "{name} may only contain printable characters with no spaces": "{name} 只能包含可打印字符,且不能有空格", + "{name} contains a character that is not valid in a key (#, quotes, $, \\, or backtick)": "{name} 包含密钥中不允许的字符(#、引号、$、\\ 或反引号)", + "{name} is configured outside Provider Settings and can only be changed where it was set": "{name} 是在服务商设置之外配置的,只能在原设置处修改", + "The key was not saved: could not restrict the credential file to your account; nothing was saved": "密钥未保存:无法将凭据文件限制为仅你的账户可访问;未保存任何内容", + "The key was not saved: the existing configuration could not be read, so nothing was changed": "密钥未保存:无法读取现有配置,因此未做任何更改", + "Could not write the {store} store": "无法写入{store}存储", "Save failed ({status}).": "保存失败({status})。", "OAuth check failed: {detail}": "OAuth 检查失败:{detail}", "Could not check ChatGPT sign-in. Try again.": "无法检查 ChatGPT 登录状态,请重试。", @@ -206,6 +382,7 @@ "Use OPENAI_API_KEY for the next cloud voice session": "下次云语音会话使用 OPENAI_API_KEY", "Use the signed-in local ChatGPT/Codex OAuth session for the next cloud voice session": "下次云语音会话使用本机已登录的 ChatGPT/Codex OAuth 会话", "Metered — a billing-enabled account": "按量计费——需要启用结算的账号", + "Optional paid token — public access works without one": "可选的付费令牌——无需令牌也可公开访问", "Free key — register, paste, done": "免费密钥——注册、粘贴,即可使用", "browser-side": "浏览器端", "This key runs in the browser by design — restrict it at the provider (see SECURITY.md)": "此密钥按设计在浏览器中运行——请在服务商处设置限制(见 SECURITY.md)", @@ -217,6 +394,7 @@ "paste {envVar}": "粘贴 {envVar}", "REMOVE": "移除", "Remove {title} from this app's saved keys": "从本应用已保存的密钥中移除 {title}", + "unsaved provider keys": "未保存的服务商密钥", "Project keys: {status}": "项目密钥:{status}", "your app configuration": "你的应用配置", "your local .env": "你的本地 .env", @@ -229,14 +407,15 @@ "Could not check ChatGPT sign-in.": "无法检查 ChatGPT 登录状态。", "ChatGPT OAuth selected for cloud voice. Your API key stays saved and available.": "已为云语音选择 ChatGPT OAuth。你的 API 密钥仍会保留并可用。", "ChatGPT sign-in expired. Opening sign-in again…": "ChatGPT 登录已过期。正在重新打开登录…", - "Opening ChatGPT sign-in in your browser…": "正在在你的浏览器中打开 ChatGPT 登录…", + "Opening ChatGPT sign-in in your browser…": "正在浏览器中打开 ChatGPT 登录…", "Could not start ChatGPT sign-in on this machine.": "无法在此机器上启动 ChatGPT 登录。", "Finish ChatGPT sign-in in the browser. Waiting for it to complete…": "请在浏览器中完成 ChatGPT 登录,正在等待其完成…", "ChatGPT sign-in timed out. Click {button} to try again.": "ChatGPT 登录超时。点击“{button}”重试。", "ChatGPT sign-in complete. OAuth will be used for the next cloud voice session.": "ChatGPT 登录完成。下次云语音会话将使用 OAuth。", "Remove this key from your saved configuration?": "要从已保存的配置中移除此密钥吗?", "Removed from {store}. Restarting — this page reloads itself.": "已从{store}中移除。正在重启——本页面将自动重新加载。", - "Places context + Street View fallback; optional separate key": "地点上下文 + 街景备用;可选的独立密钥", + "The photorealistic 3D planet, place search + Street View": "照片级 3D 地球、地点搜索 + 街景", + "Places context + Street View fallback; optional separate key": "地点信息 + 街景备用;可选的独立密钥", "Voice control — API key or ChatGPT OAuth": "语音控制——API 密钥或 ChatGPT OAuth", "Live ships, worldwide": "全球实时船舶", "Live active-fire detections": "实时活跃火点探测", @@ -244,51 +423,88 @@ "Bing imagery map stacks + world terrain": "Bing 影像地图组合 + 全球地形", "More flight-polling credits (anonymous works without)": "更多航班轮询额度(匿名也可使用)", "Street-level imagery and coverage in the Street Level layer. Free: register an app in the Mapillary developer dashboard and paste its Client Token": "街景影像与街景图层中的覆盖范围。免费:在 Mapillary 开发者控制台注册一个应用,并粘贴其 Client Token", + "Public space-missions access needs no key. Leave blank for standard use; paid tokens only raise request limits.": "公开的太空任务数据无需密钥。常规使用请留空;付费令牌只会提高请求上限。", + "Air quality request failed": "空气质量请求失败", + "No readings available": "暂无读数", + "No current readings": "暂无当前读数", + "{count} stations reporting": { + "other": "{count} 个站点上报数据" + }, + "Highest air-quality readings in view": "视野内空气质量读数最高的站点", "Low": "低", + "Low health risk": "低健康风险", + "Moderate": "中等", + "Moderate health risk": "中等健康风险", "High": "高", + "High health risk": "高健康风险", + "Very High": "极高", + "Very High health risk": "极高健康风险", + "Air quality": "空气质量", + "Canada only · ECCC AQHI network": "仅限加拿大 · ECCC AQHI 站网", + "ECCC request timed out": "ECCC 请求超时", + "Malformed AQHI station response": "AQHI 站点响应格式错误", + "No AQHI stations available": "没有可用的 AQHI 站点", + "Malformed AQHI observation response": "AQHI 观测响应格式错误", + "Camera source temporarily unavailable": "摄像头数据源暂时不可用", + "Mapped camera locations": "地图标注的摄像头位置", "SHOW NEAREST": "最近摄像头", "Stop following the current object before navigating to a camera": "请先停止跟踪当前目标,再前往摄像头", "Move to the nearest loaded camera and show its details": "前往最近已加载的摄像头并显示详情", "Camera badges": "摄像头徽标", "Cyan cameras turn coral when selected. Wedges illustrate mapped direction, not measured coverage. Nearby cameras may be outside the screen.": "选中后青色摄像头变为珊瑚色。扇形仅示意已标绘方向,并非实测覆盖范围。附近的摄像头可能不在屏幕内。", + "{count} nearby · {onScreen} on screen": { + "other": "附近 {count} 个 · 屏幕上 {onScreen} 个" + }, + "{count} nearby": { + "other": "附近 {count} 个" + }, "retrying mapped ALPR cameras": "正在重试加载 ALPR 摄像头", "loading mapped ALPR cameras": "正在加载 ALPR 摄像头", - "No ALPR data for this area — US and Canada only": "此区域没有 ALPR 数据 —— 仅覆盖美国和加拿大", - "Zoom in to load mapped cameras": "放大以加载标绘摄像头", + "No ALPR data for this area — US and Canada only": "此区域没有 ALPR 数据——仅覆盖美国和加拿大", + "Zoom in to load mapped cameras": "放大以加载地图标注的摄像头", "Showing cached locations": "正在显示缓存位置", - "Coverage limited — zoom in": "覆盖受限 —— 请放大", + "Coverage limited — zoom in": "覆盖受限——请放大", "No ALPR data for this area": "此区域没有 ALPR 数据", - "None on screen — nearby cameras are outside the view": "屏幕上没有摄像头 —— 附近的摄像头在视野之外", + "None on screen — nearby cameras are outside the view": "屏幕上没有摄像头——附近的摄像头在视野之外", "ALPR camera": "ALPR 摄像头", + "Camera coverage unavailable": "摄像头覆盖不可用", + "OpenStreetMap · community mapped": "OpenStreetMap · 社区标注", + "Overpass returned an incomplete camera response": "Overpass 返回的摄像头数据不完整", "At least {count}": "至少 {count}", - "CONTEXT READY": "上下文就绪", - "GLOBAL CONTEXT OFF": "全局上下文已关闭", + "CONTEXT READY": "态势就绪", + "GLOBAL CONTEXT OFF": "全局态势已关闭", "SELECT A FLIGHT, VESSEL, OR MAPPED INSTALLATION": "选择一个航班、船舶或地图标注设施", "ENABLE TO LOAD OBSERVED / MAPPED PROXIMITY": "启用以加载观测 / 地图标注的邻近信息", "Unavailable": "不可用", "Focus {label}": "聚焦 {label}", - "Global Context navigation": "全局上下文导航", + "Global Context navigation": "全局态势导航", "Previous — prior visited contact in the 250 km window": "上一个:250 km 范围内先前访问过的目标", "PREVIOUS": "上一个", "FOCUS": "聚焦", "Next — nearest unvisited contact in the 250 km window": "下一个:250 km 范围内最近的未访问目标", "NEXT": "下一个", - "Named areas ({n})": "指定区域({n})", - "Open-source mapped/observed context. Missing broadcasts, unloaded map areas, or unmapped sites are not evidence of absence.": "开源的地图标注/观测上下文。缺失的广播、未加载的地图区域或未标注的站点不能作为不存在的证据。", + "Named areas ({n})": "已命名区域({n})", + "{distance} FLIGHT / VESSEL WINDOW": "{distance} 航班 / 船舶范围", + "Open-source mapped/observed context. Missing broadcasts, unloaded map areas, or unmapped sites are not evidence of absence.": "开源的地图标注/观测态势信息。缺失的广播、未加载的地图区域或未标注的设施不能作为不存在的证据。", + "mapped matches within {km} km of the subject": "所选对象 {km} km 内的地图标注匹配", + "none mapped within {km} km; not a complete 250 km survey": "{km} km 内无地图标注;并非完整的 250 km 普查", + "mapped matches from the loaded viewport": "来自已加载视口的地图标注匹配", + "viewport feed is not a complete 250 km survey": "视口数据源并非完整的 250 km 普查", "HDG {value}°": "HDG {value}°", "BRG {value}°": "BRG {value}°", "BRG —": "BRG —", "CRS {value}°": "CRS {value}°", "CURRENT VIEWPORT ONLY": "仅限当前视口", - "WITHIN {km} KM": "{km} KM 以内", + "WITHIN {km} KM": "{km} km 以内", "Flights": "航班", "Military flights": "军用航班", "AIS vessels": "AIS 船舶", "Mapped installations": "地图标注设施", "syncing {count} city feeds...": "正在同步 {count} 个城市数据源…", "scanning nearby systems...": "正在扫描附近系统…", + "GBFS fetch error": "GBFS 获取错误", "Station {id}": "站点 {id}", - "Station": "电台", + "Station": "站点", "🚲 {bikes} avail · {docks} docks · {capacity} cap": "🚲 可用 {bikes} · 车桩 {docks} · 容量 {capacity}", "⚠️ Not installed": "⚠️ 未安装", "⚠️ Not renting": "⚠️ 暂停租车", @@ -297,25 +513,29 @@ "{count} CAMERAS STANDING BY · NO CAMERA SELECTED · CLICK A CAMERA TO ACTIVATE": "{count} 个摄像头待命 · 未选择摄像头 · 点击摄像头以激活", "No cameras available in catalog.": "目录中没有可用摄像头。", "{city} CCTV": "{city} CCTV", + "FOV {value}° (DATASHEET)": "FOV {value}°(规格书)", "FOV {value}°": "FOV {value}°", + "SPEC FOV {span}° (CAPABILITY)": "规格 FOV {span}°(可调范围)", "COVERAGE {value}km²": "覆盖 {value}km²", "OVERLAP {count} cams": "重叠 {count} 个摄像头", "ISOLATED VIEW": "孤立视角", - "PROJ MONITOR": "投影 监视器", - "PROJ OFF": "投影 关", + "PROJ MONITOR": "投影:监视器", + "PROJ OFF": "投影:关", "VIEWSHED": "可视域", "CAL {value}": "CAL {value}", "SRC {value}": "SRC {value}", - "{value} CONTEXT": "{value} 上下文", + "{value} CONTEXT": "{value} 态势", + "{spec} · SPECS: CCTV-DATABASE.COM": "{spec} · 规格:CCTV-DATABASE.COM", "Atlantic and eastern/central North Pacific; not worldwide cyclone coverage.": "覆盖大西洋及北太平洋东部/中部;不含全球气旋。", "Cyclone advisories unavailable": "气旋公告不可用", + "Cyclone data unavailable": "气旋数据不可用", "Potential tropical cyclone": "潜在热带气旋", "Hurricane": "飓风", "Tropical storm": "热带风暴", "Tropical depression": "热带低压", "Subtropical storm": "亚热带风暴", "Subtropical depression": "亚热带低压", - "Track and cone match this advisory": "路径与锥体与本份公告一致", + "Track and cone match this advisory": "路径和锥体与本期公告一致", "Track/cone awaiting advisory {number}": "路径/锥体等待第 {number} 号公告", "Track/cone unavailable": "路径/锥体不可用", "Cached advisory · stale source": "缓存公告 · 数据源已过期", @@ -330,26 +550,103 @@ "Advisories unavailable": "公告不可用", "Cyclones · NHC / CPHC": "气旋 · NHC / CPHC", "Atlantic · E/C Pacific": "大西洋 · 东/中太平洋", + "{storms} · {name} selected": "{storms} · 已选择 {name}", "Official advisory ↗": "官方公告 ↗", "Active NHC and CPHC cyclone advisories": "活跃的 NHC 与 CPHC 气旋公告", "Wind unavailable": "风场数据不可用", "Advisory center / forecast track": "公告中心 / 预报路径", "Center-track uncertainty cone": "中心路径不确定锥", - "Select a storm on the map, or choose a storm in the list to select it and move the camera. Click empty map space to clear the selection. NOAA NHC/CPHC advisory context. The cone describes forecast center-track uncertainty, not storm size or the full hazard area. Forecast point labels are source lead hours, not times computed from advisory issuance. Geometry follows the surface; height is not weather altitude. Consult the official advisory.": "在地图上点选气旋,或在列表中选择以选中并移动相机。点击空白地图区域可清除选择。内容来自 NOAA NHC/CPHC 公报。锥体描述的是预报中心路径的不确定范围,不代表气旋尺寸或全部灾害区域。预报点标注的是数据源的前导小时数,不是按公告发布时刻推算的时间。几何图形贴地显示;显示高度并非天气高度。请以官方公告为准。", + "Select a storm on the map, or choose a storm in the list to select it and move the camera. Click empty map space to clear the selection. NOAA NHC/CPHC advisory context. The cone describes forecast center-track uncertainty, not storm size or the full hazard area. Forecast point labels are source lead hours, not times computed from advisory issuance. Geometry follows the surface; height is not weather altitude. Consult the official advisory.": "在地图上点选气旋,或在列表中选择以选中并移动相机。点击空白地图区域可清除选择。内容来自 NOAA NHC/CPHC 公告。锥体描述的是预报中心路径的不确定范围,不代表气旋尺寸或全部灾害区域。预报点标注的是数据源的前导小时数,不是按公告发布时刻推算的时间。几何图形贴地显示;显示高度并非天气高度。请以官方公告为准。", "DRIVE": "驾车", "Drive": "驾车", "WALK": "步行", "Walk": "步行", "BIKE": "骑行", "Bike": "骑行", - "{error} — try again in a moment": "{error} — 请稍后重试", - "Routing is rate limited — try again in a moment": "路线请求过于频繁 — 请稍后重试", + "rate limited": "已限流", + "routing service is rate limited": "路线服务已限流", + "route leg too long": "路段过长", + "route too long": "路线过长", + "{error} — try again in a moment": "{error}——请稍后重试", + "Routing is rate limited — try again in a moment": "路线请求过于频繁——请稍后重试", "Routing unavailable (HTTP {status})": "路线服务不可用(HTTP {status})", "Routing unavailable": "路线规划不可用", - "Head out on {road}": "沿{road}出发", + "direction|left": "向左", + "direction|right": "向右", + "direction|slightly left": "向左前方", + "direction|slightly right": "向右前方", + "direction|sharply left": "向左后方", + "direction|sharply right": "向右后方", + "direction|straight": "向前", + "direction|around": "向后", + "fork direction|left": "靠左行驶", + "fork direction|right": "靠右行驶", + "fork direction|slightly left": "稍靠左行驶", + "fork direction|slightly right": "稍靠右行驶", + "fork direction|sharply left": "向左后方行驶", + "fork direction|sharply right": "向右后方行驶", + "fork direction|straight": "直行", + "lane direction|left": "左转", + "lane direction|right": "右转", + "lane direction|slightly left": "稍左转", + "lane direction|slightly right": "稍右转", + "lane direction|sharply left": "急左转", + "lane direction|sharply right": "急右转", + "lane direction|straight": "直行", + "ordinal|{number}st": "第 {number} 个", + "ordinal|{number}nd": "第 {number} 个", + "ordinal|{number}rd": "第 {number} 个", + "ordinal|{number}th": "第 {number} 个", + "Head out on {road}": "沿 {road} 出发", "Head out": "出发", + "Arrive at your destination, on the left": "到达目的地,目的地在左侧", + "Arrive at your destination, on the right": "到达目的地,目的地在右侧", "Arrive at your destination": "到达目的地", - "{mode} — reroute for {modeLower}": "{mode} — 按{modeLower}重新规划路线", + "Make a U-turn onto {road}": "掉头进入 {road}", + "Make a U-turn": "掉头", + "Continue straight onto {road}": "直行进入 {road}", + "Continue straight": "继续直行", + "Turn {direction} onto {road}": "{direction}转,进入 {road}", + "Turn {direction}": "{direction}转", + "Turn onto {road}": "转弯进入 {road}", + "maneuver|Turn": "转弯", + "Continue {direction} onto {road}": "继续{direction}行驶,进入 {road}", + "Continue {direction}": "继续{direction}行驶", + "At the end of the road, turn {direction} onto {road}": "在道路尽头{direction}转,进入 {road}", + "At the end of the road, turn {direction}": "在道路尽头{direction}转", + "At the end of the road, continue onto {road}": "在道路尽头继续行驶,进入 {road}", + "At the end of the road, continue": "在道路尽头继续行驶", + "Keep {direction} at the fork onto {road}": "在岔路口{direction},进入 {road}", + "Keep {direction} at the fork": "在岔路口{direction}", + "Continue at the fork onto {road}": "在岔路口继续行驶,进入 {road}", + "Continue at the fork": "在岔路口继续行驶", + "Merge {direction} onto {road}": "{direction}并道,进入 {road}", + "Merge {direction}": "{direction}并道", + "Merge onto {road}": "并入 {road}", + "maneuver|Merge": "并道", + "Take the ramp on the {direction} onto {road}": "{direction}驶入匝道,进入 {road}", + "Take the ramp on the {direction}": "{direction}驶入匝道", + "Take the ramp onto {road}": "驶入匝道,进入 {road}", + "Take the ramp": "驶入匝道", + "Take the exit on the {direction} onto {road}": "从出口{direction}驶出,进入 {road}", + "Take the exit on the {direction}": "从出口{direction}驶出", + "Take the exit onto {road}": "从出口驶出,进入 {road}", + "Take the exit": "从出口驶出", + "At the roundabout, take the {exit} exit onto {road}": "在环岛从{exit}出口驶出,进入 {road}", + "At the roundabout, take the {exit} exit": "在环岛从{exit}出口驶出", + "Enter the roundabout onto {road}": "驶入环岛,进入 {road}", + "Enter the roundabout": "驶入环岛", + "At the roundabout, turn {direction} onto {road}": "在环岛{direction}转,进入 {road}", + "At the roundabout, turn {direction}": "在环岛{direction}转", + "At the roundabout, continue onto {road}": "在环岛继续行驶,进入 {road}", + "At the roundabout, continue": "在环岛继续行驶", + "Exit the roundabout onto {road}": "驶出环岛,进入 {road}", + "Exit the roundabout": "驶出环岛", + "Use the {direction} lane onto {road}": "使用{direction}车道,进入 {road}", + "Use the {direction} lane": "使用{direction}车道", + "Continue onto {road}": "继续行驶,进入 {road}", + "maneuver|Continue": "继续行驶", + "{mode} — reroute for {modeLower}": "{mode}——按{modeLower}重新规划路线", "CLICK MAP": "点击地图", "SET A": "设置 A", "Click a spot on the globe to place A (click again to cancel)": "点击地球上的位置放置 A(再次点击取消)", @@ -363,29 +660,33 @@ "FLY": "飞行", "Fly the camera along the route": "沿路线飞行相机", "Place A and B first": "请先放置 A 和 B", + "action|CLEAR": "清除", "Remove the route and both markers": "移除路线和两个标记点", - "press Escape twice to leave Draw": "按两次 Escape 退出绘图", - "{name} is active — {leave}, then set {endpoint}": "{name} 正在使用 — {leave},然后设置 {endpoint}", + "press Escape twice to leave Draw": "按两次 Escape 退出“绘制”", + "{name} is active — turn {name} off, then set {endpoint}": "{name} 正在使用——请先关闭 {name},再设置 {endpoint}", + "{name} is active — {leave}, then set {endpoint}": "{name} 正在使用——请先{leave},再设置 {endpoint}", "Only the first {count} turns of this route are shown": "此路线仅显示前 {count} 个转向", "Turn-by-turn directions": "逐向导航路线", - "Another map tool is using clicks — close it, then SET A again": "另一个地图工具正在占用点击 — 关闭它,然后重新设置 A", + "Another map tool is using clicks — close it, then SET A again": "另一个地图工具正在占用点击——请先关闭它,再重新设置 A", "Routing…": "正在规划路线…", "No route found": "未找到路线", + "{distance} · {duration} · {mode} · first {count} turns": "{distance} · {duration} · {mode} · 前 {count} 个转向", "{distance} · {duration} · {mode}": "{distance} · {duration} · {mode}", "Click the globe to place {endpoint}": "点击地球放置 {endpoint}", "SET B, then click the globe": "设置 B,然后点击地球", "SET A, then click the globe": "设置 A,然后点击地球", + "Step {step} of {total} · then {leg}": "第 {step}/{total} 步 · 随后 {leg}", "Step {step} of {total}": "第 {step} 步,共 {total} 步", "Then: {instruction}": "接下来:{instruction}", "No route found between A and B": "在 A 和 B 之间未找到路线", "Routing failed: {error}": "路线规划失败:{error}", "Routing timed out": "路线规划超时", - "Routing unavailable — no response from the routing service": "路线服务无响应 — 路线规划不可用", + "Routing unavailable — no response from the routing service": "路线规划不可用——路线服务无响应", "Earthquake source unavailable": "地震数据源不可用", "Focus fire detection {title}, {details}": "聚焦火点探测 {title},{details}", "live feed unavailable": "实时数据不可用", "{value} conf": "置信度 {value}", - "{value} ago": "{value} 前", + "{value} ago": "{value}前", "sensor n/a": "传感器 n/a", "FIRE · {frp} MW": "火点 · {frp} MW", "NIGHT": "夜间", @@ -393,20 +694,62 @@ "FIRE": "火点", "FIRES": "火点", "max {value} MW": "最大 {value} MW", - "new {value}": "新增 {value}", + "new {value}": "最新 {value}前", + "{value}°N": "{value}°N", + "{value}°S": "{value}°S", + "{value}°E": "{value}°E", + "{value}°W": "{value}°W", "just now": "刚刚", "<1m ago": "不到 1 分钟前", "{count}m ago": "{count} 分钟前", "{count}h ago": "{count} 小时前", + "fire confidence|high": "高", + "fire confidence|nominal": "中", + "fire confidence|low": "低", + "age|<1h": "<1 小时", + "age|{count}h": "{count} 小时", + "age|{count}d": "{count} 天", "Fire · FRP {frp} MW": "火点 · FRP {frp} MW", - "STALE · cached {age}": "过期 · 缓存于 {age}", + "STALE · cached {age}": "过期 · {age}前缓存", "refreshing...": "刷新中…", "loading...": "加载中…", "KEY REQUIRED": "需要密钥", "LIVE · updated {age}": "实时 · 更新于 {age}", - "loading mapped installation context": "正在加载地图标注设施上下文", - "Too many mapped sites in view to list them all": "视野内标绘地点过多,无法全部列出", - "Installation context unavailable": "设施背景信息不可用", + "Source snapshot time unavailable": "来源快照时间不可用", + "Live data unavailable": "实时数据不可用", + "Low-accuracy score < 2%": "低精度评分 < 2%", + "Low-accuracy score 2–10%": "低精度评分 2–10%", + "Low-accuracy score > 10%": "低精度评分 > 10%", + "GNSS integrity source unavailable": "GNSS 完好性数据源不可用", + "Regional sample · 250 nm radius · cells from visited views over 30 min": "区域采样 · 半径 250 海里 · 30 分钟内已访问视图中的网格", + "Retrying automatically at {time} UTC": "将于 {time} UTC 自动重试", + "Retrying automatically after provider cooldown": "服务商冷却结束后自动重试", + "Updates automatically after pan or zoom and once a minute. The public feed covers a 250-nautical-mile circle around the view centre; zooming out does not make it a worldwide snapshot. Blank cells are outside sampled coverage or have insufficient observations.": "平移或缩放后以及每分钟自动更新。公共数据源覆盖以视图中心为圆心、半径 250 海里的范围;缩小地图并不会使其成为全球快照。空白网格位于采样覆盖范围之外,或观测数据不足。", + "Low-accuracy score = 100 × max(0, bad − 1) / total: one aircraft is discounted, so this is not the raw share. Cells need {aircraft} aircraft over {minutes} minutes. Navigation-integrity anomalies are not a detection of jamming or spoofing.": "低精度评分 = 100 × max(0, bad − 1) / total:扣除一架飞机,因此并非原始占比。每个网格需在 {minutes} 分钟内有 {aircraft} 架飞机。导航完好性异常不等于检测到干扰或欺骗。", + "GNSS snapshot needs a view anchor": "GNSS 快照需要视图锚点", + "GNSS feed rate limited (HTTP 429)": "GNSS 数据源已限流(HTTP 429)", + "Malformed GNSS integrity snapshot": "GNSS 完好性快照格式错误", + "GNSS integrity snapshot has no observation time": "GNSS 完好性快照缺少观测时间", + "installation class|AIRFIELD": "机场", + "installation class|NAVAL BASE": "海军基地", + "installation class|RANGE": "靶场", + "installation class|MILITARY LAND": "军事用地", + "installation class|BASE": "基地", + "installation class|BARRACKS": "营房", + "installation class|TRAINING AREA": "训练区", + "installation class|PLACES CANDIDATE": "Places 候选", + "installation class|INSTALLATION": "设施", + "No mapped sites within {km} km of the contact": "目标 {km} km 内无地图标注设施", + "{count} mapped sites within {km} km of the contact": { + "other": "目标 {km} km 内有 {count} 个地图标注设施" + }, + "No mapped sites in view": "视野内无地图标注设施", + "{count} mapped sites in view": { + "other": "视野内有 {count} 个地图标注设施" + }, + "loading mapped installation context": "正在加载地图标注设施信息", + "Too many mapped sites in view to list them all": "视野内地图标注设施过多,无法全部列出", + "Installation context unavailable": "设施信息不可用", "MAPPED INSTALLATION": "地图标注设施", "Unknown": "未知", "LAUNCH SITE · {site}": "发射场 · {site}", @@ -422,7 +765,7 @@ "UNSPECIFIED": "未指明", "+{count} additional payload records": "另有 {count} 条有效载荷记录", "PAYLOAD DATA UNAVAILABLE": "有效载荷数据不可用", - "{value} KM DOWNRANGE": "下射程 {value} KM", + "{value} KM DOWNRANGE": "射程 {value} km", "POSITION UNAVAILABLE": "位置不可用", "FLIGHT {count}": "第 {count} 次飞行", "REUSED": "复用", @@ -432,8 +775,8 @@ "DATE UNAVAILABLE": "日期不可用", "UNSPECIFIED OPERATOR": "运营方未指明", "Select {mission}": "选择 {mission}", - "{value} KM": "{value} KM", - "{value} KM/S": "{value} KM/S", + "{value} KM": "{value} km", + "{value} KM/S": "{value} km/s", "Selected Space Mission": "已选太空任务", "SELECTED SPACE MISSION": "已选太空任务", "Show all missions": "显示所有任务", @@ -463,7 +806,7 @@ "SHOW ALL / DESELECT": "显示全部 / 取消选择", "Pause replay": "暂停回放", "Cancel replay": "取消回放", - "EST. DOWNRANGE": "预估下射程", + "EST. DOWNRANGE": "预估射程", "PLANNED · ": "计划 · ", "SUPPLIED TRAJECTORY POINTS": "提供的轨迹点", "RECONSTRUCTED ESTIMATE": "重建估算", @@ -475,13 +818,24 @@ "Ascent replay": "上升回放", "Orbit replay": "轨道回放", ", paused": ",已暂停", + "Malformed launch snapshot": "发射快照格式错误", "Heard by your receiver": "由你的接收机接收", + "1090 MHz": "1090 MHz", + "978 MHz UAT": "978 MHz UAT", "browser SDR": "浏览器 SDR", "decoder feed": "解码器数据源", "Your RTL-SDR receiver and decoder feed": "你的 RTL-SDR 接收机和解码器数据源", "Your decoder feed": "你的解码器数据源", "Your RTL-SDR receiver": "你的 RTL-SDR 接收机", "NO CALLSIGN": "无呼号", + "ALT {altitude} FT · GS {speed} KT · TRK {track}": "高度 {altitude} FT · 地速 {speed} KT · 航迹 {track}", + "V/S {rate}": "升降 {rate}", + "V/S {rate} FPM": "升降 {rate} FPM", + "POSITION {age}": "位置 {age}", + "POSITION {seconds} S AGO": "位置 {seconds} 秒前", + "{count} MSGS": { + "other": "{count} 条消息" + }, "Decoder feeds": "解码器数据源", "WebUSB + decoder feeds": "WebUSB + 解码器数据源", "opening receiver": "正在打开接收机", @@ -489,12 +843,25 @@ "WebUSB needs desktop Chrome or Edge": "WebUSB 需要桌面版 Chrome 或 Edge", "connect a receiver in Radio": "在电台面板连接接收机", "receiver is in FM mode": "接收机处于 FM 模式", - "listening": "聆听中", + "listening": "监听中", "USB error": "USB 错误", "Alt unknown": "高度未知", "Type unknown": "类型未知", "Reg unknown": "注册号未知", "Operator unknown": "运营方未知", + "${amount}K": "{amount} 千美元", + "${amount}M": "{amount} 百万美元", + "${amount}B": "{amount} 十亿美元", + "fire cause|Human": "人为", + "fire cause|Natural": "自然", + "fire cause|Undetermined": "待定", + "fire cause|Unknown": "未知", + "fire behavior|Minimal": "微弱", + "fire behavior|Moderate": "中等", + "fire behavior|Active": "活跃", + "fire behavior|Extreme": "极端", + "Type {level} Incident": "{level} 类事件", + "age|{count}m": "{count} 分钟", "{acres} ac": { "other": "{acres} 英亩" }, @@ -505,7 +872,7 @@ "other": "{personnel} 名人员" }, "{county} County": "{county} 县", - "{cost} to date": "迄今损失 {cost}", + "{cost} to date": "迄今扑救费用 {cost}", "discovered {age} ago": "发现于 {age}前", "updated {age} ago": "更新于 {age}前", "part of {complex}": "属于 {complex} 复合火场", @@ -518,19 +885,32 @@ "Under 50% contained": "控制低于 50%", "50–99% contained": "控制 50–99%", "Fully contained": "已全部控制", - "Colour shows reported containment. Perimeters are simplified to about 100 m.": "颜色表示报告的控制比例。周界简化至约 100 米。", + "Colour shows reported containment. Perimeters are simplified to about 100 m.": "颜色表示报告的控制比例。周界简化至约 100 m。", + "Malformed perimeter snapshot": "周界快照格式错误", + "radio tag|All": "全部", "News": "新闻", - "Talk": "谈话", + "Talk": "访谈", "Weather / Emergency": "天气 / 应急", "Public Safety": "公共安全", "Aviation / Marine": "航空 / 海事", - "Traffic / Transit": "交通 / 公交", + "Traffic / Transit": "交通 / 公共交通", "Music": "音乐", "Other": "其他", "Directory refresh degraded; showing the previous station catalog.": "目录刷新降级;正在显示上一次的电台目录。", "Radio directory coverage is degraded.": "电台目录覆盖已降级。", "Directory refresh failed; showing the previous station catalog.": "目录刷新失败;正在显示上一次的电台目录。", "Radio directory is temporarily unavailable.": "电台目录暂时不可用。", + "radio|NEWS": "新闻", + "radio|TALK": "访谈", + "radio|WEATHER": "天气", + "radio|SAFETY": "安全", + "radio|AIR / SEA": "航空 / 海事", + "radio|TRANSIT": "公共交通", + "radio|MUSIC": "音乐", + "radio|OTHER": "其他", + "Broadcaster stream is unavailable or blocked by the browser.": "广播流不可用,或被浏览器阻止。", + "Playback requires a direct click or tap.": "需直接点击或轻触才能播放。", + "Broadcaster stream could not be started.": "无法启动广播流。", "Alternative": "另类", "Ambient": "氛围", "Blues": "蓝调", @@ -556,7 +936,32 @@ "Techno": "Techno", "Trance": "Trance", "World": "世界音乐", + "overview": "概览", "cloud unknown": "云量未知", + "{percent}% scene cloud": "场景云量 {percent}%", + "{min}–{max}% scene cloud": "场景云量 {min}–{max}%", + "Comparison in use by another scene": "对比功能正被另一个场景占用", + "Esri map unavailable · no swipe": "Esri 地图不可用 · 无法滑动对比", + "Swipe needs a globe map": "滑动对比需要球体地图", + "Imagery stays on Esri · CLEAR to use Google 3D": "影像保留在 Esri · 清除后改用 Google 3D", + "Imagery catalog unavailable": "影像目录不可用", + "Catalog truncated · a newer clear day may exist": "目录已截断 · 可能还有更新的晴天", + "{label} catalog unavailable · {detail}": "{label} 目录不可用 · {detail}", + "Daily overview for today may still be empty until the pass": "卫星过境前,今天的每日概览可能仍为空", + "Box and images cleared": "框选及影像已清除", + "MORE DETAIL · 30 m": "更多细节 · 30 m", + "Sentinel-2 and Landsat via HLS, 30 m, every 2–6 days": "Sentinel-2 与 Landsat,经 HLS,30 m,每 2–6 天", + "DAILY OVERVIEW · 250 m": "每日概览 · 250 m", + "VIIRS daily mosaic, 250 m, follows broad changes; little detail under 25 km": "VIIRS 每日拼图,250 m,反映大范围变化;25 km 以下几乎没有细节", + "Sentinel-2": "Sentinel-2", + "Sentinel-2 via HLS": "Sentinel-2(经 HLS)", + "Landsat 8/9": "Landsat 8/9", + "Landsat 8/9 via HLS": "Landsat 8/9(经 HLS)", + "Daily overview": "每日概览", + "Box edges must be finite": "框选边界必须是有限值", + "Longitudes must be within ±180°": "经度必须在 ±180° 以内", + "Select one side of the dateline": "请只选择日界线的一侧", + "Box must enclose an area": "框选必须包含一定面积", "Add the full Starlink broadband shell (thousands of extra points)": "添加完整的 Starlink 宽带星座(数千个额外点位)", "Loading the Starlink shell…": "正在加载 Starlink 星座…", "Starlink {detail} — click to retry": "Starlink {detail}——点击重试", @@ -565,10 +970,66 @@ "DENSE ···": "密集 ···", "DENSE ✕": "密集 ✕", "DENSE": "密集", + "CelesTrak unreachable": "CelesTrak 无法连接", + "DOCKED · {name} · +{extra}": "对接 · {name} · +{extra}", "DOCKED · {name}": "对接 · {name}", + "{name} needs the Google 3D map: choose it under MAP SOURCE": "{name} 需要 Google 3D 地图:请在地图源(MAP SOURCE)中选择", + "zoom in to a street to open {name}": "放大到街道以打开 {name}", "KEY REJECTED": "密钥被拒绝", - "loading coverage...": "正在加载覆盖范围...", + "loading coverage...": "正在加载覆盖范围…", + "No street-level imagery within {radius} m of that point": "该点 {radius} m 范围内没有街景影像", + "No street-level imagery within {radius} m of the view centre": "视野中心 {radius} m 范围内没有街景影像", + "Coverage tile failed": "覆盖瓦片加载失败", + "Point the camera at the globe for street-level coverage": "将相机对准地球以查看街景覆盖范围", + "The photo could not be loaded": "照片无法加载", + "{name} photo. ← and → step along the sequence. On a 360° photo, drag or use Shift+← →, ↑ ↓ to look around, and + − to zoom": "{name} 照片。← 和 → 沿序列切换。在 360° 照片上,拖动或使用 Shift+← →、↑ ↓ 环视,+ − 缩放", + "{name} street-level photo": "{name} 街景照片", + "Previous photo in this sequence (←)": "此序列中的上一张照片(←)", + "Next photo in this sequence (→)": "此序列中的下一张照片(→)", + "Aim the middle of the view at a street to look for imagery there": "将视野中心对准街道以查找该处影像", + "GOOGLE_MAPS_API_KEY is not set": "未设置 GOOGLE_MAPS_API_KEY", + "The Google Maps JavaScript API could not load": "Google Maps JavaScript API 无法加载", + "Google Street View": "Google 街景", + "GOOGLE STREET VIEW": "Google 街景", + "Google refused GOOGLE_MAPS_API_KEY for Street View — check that the Maps JavaScript API is enabled for it and this site is an allowed referrer": "Google 拒绝了用于街景的 GOOGLE_MAPS_API_KEY——请检查该密钥已启用 Maps JavaScript API,且本站点在允许的引荐来源(referrer)中", + "Street View did not answer — check the network, and that the Maps JavaScript API is enabled for GOOGLE_MAPS_API_KEY": "街景无响应——请检查网络,并确认 GOOGLE_MAPS_API_KEY 已启用 Maps JavaScript API", + "click a street to open Street View": "点击街道以打开街景", + "Street View has no imagery for this place": "街景没有此地的影像", + "KartaView has no position for this photo": "KartaView 没有此照片的位置", + "The KartaView photo could not be loaded": "KartaView 照片无法加载", + "zoom in to see KartaView coverage": "放大以查看 KartaView 覆盖范围", + "no KartaView photos in view": "视野内没有 KartaView 照片", + "KartaView is rate-limiting requests — they resume on their own": "KartaView 正在限制请求频率——稍后会自动恢复", + "KartaView’s photos here are archived and cannot be shown": "KartaView 在此处的照片已归档,无法显示", + "KartaView did not filter this area in time — move the map to ask again": "KartaView 未能及时筛选此区域——移动地图以重新请求", + "None of KartaView’s photos here pass the imagery filter": "此处没有 KartaView 照片通过影像筛选", + "KartaView did not answer in time — try again": "KartaView 未及时响应——请重试", + "KartaView could not be reached — check the network": "无法连接 KartaView——请检查网络", + "KartaView sent an answer that could not be read": "KartaView 返回的响应无法读取", + "KartaView is rate-limiting requests": "KartaView 正在限制请求频率", + "KartaView sent too much data": "KartaView 返回的数据过多", + "KartaView is rate-limiting requests — try again in a moment": "KartaView 正在限制请求频率——请稍后重试", + "KartaView has no such photo": "KartaView 没有这张照片", + "Mapillary rejected MAPILLARY_CLIENT_TOKEN — replace it in Provider Settings": "Mapillary 拒绝了 MAPILLARY_CLIENT_TOKEN——请在服务商设置中更换", + "Coverage requests are being rate-limited — they resume on their own": "覆盖范围请求受到频率限制——稍后会自动恢复", + "The Panoramax photo could not be loaded": "Panoramax 照片无法加载", + "Panoramax is busy — coverage resumes on its own": "Panoramax 繁忙——覆盖范围稍后会自动恢复", + "no Panoramax photos in view": "视野内没有 Panoramax 照片", + "no Panoramax photos here match the filters": "此处没有符合筛选条件的 Panoramax 照片", + "Panoramax sent a map tile that could not be read": "Panoramax 返回的地图瓦片无法读取", + "This Panoramax photo is no longer available": "这张 Panoramax 照片已不可用", + "zoom in to see Panoramax coverage": "放大以查看 Panoramax 覆盖范围", + "Panoramax did not answer in time — try again": "Panoramax 未及时响应——请重试", + "Panoramax could not be reached — check the connection": "无法连接 Panoramax——请检查网络连接", + "Panoramax sent too much data": "Panoramax 返回的数据过多", + "Panoramax is busy": "Panoramax 繁忙", + "Panoramax sent an unreadable answer": "Panoramax 返回的响应无法读取", + "No street-level imagery providers configured": "未配置街景影像提供商", + "{names}: by upload date": "{names}:按上传日期", "Selected": "所选", + "Open the Street Level panel to view imagery": "打开街景面板以查看影像", + "Image could not be opened": "图像无法打开", + "TeleGeography load failed": "TeleGeography 加载失败", "UNAVAILABLE · {source} · Roads unavailable": "不可用 · {source} · 道路不可用", "Roads: {source}": "道路:{source}", "TomTom roads unavailable while the traffic service is unreachable": "交通服务不可达期间 TomTom 道路不可用", @@ -579,25 +1040,71 @@ "Partial coverage": "部分覆盖", "Detailed roads unavailable": "详细道路不可用", "Reduced detail coverage": "详细覆盖范围受限", + "TomTom roads only, each with its live flow": "仅 TomTom 道路,各自带实时路况", + "TomTom roads only, each with its live flow (needs a TomTom key)": "仅 TomTom 道路,各自带实时路况(需要 TomTom 密钥)", "OpenStreetMap roads, with TomTom flow matched when available": "OpenStreetMap 道路,可用时匹配 TomTom 路况", - "SIMULATED — {reason}": "模拟 — {reason}", - "Syncing flow · Roads: {source}": "正在同步流量 · 道路:{source}", - "LIVE · Roads: {source} · Roads without flow hidden": "实时 · 道路:{source} · 已隐藏无流量道路", - "LIVE · Roads: {source} · No flow roads in view": "实时 · 道路:{source} · 视野内无流量道路", - "LIVE · Roads: {source} · Flow {percent}%": "实时 · 道路:{source} · 流量 {percent}%", - "SIMULATED · Roads: {source} · Flow {percent}%": "模拟 · 道路:{source} · 流量 {percent}%", - "Syncing flow · Roads: OpenStreetMap · Flow: TomTom · Unmatched: simulated": "正在同步流量 · 道路:OpenStreetMap · 流量:TomTom · 未匹配:模拟", - "LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov · Unmatched: simulated": "实时 · 道路:OpenStreetMap · 流量:TomTom · 覆盖 {percent}% · 未匹配:模拟", - "LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov": "实时 · 道路:OpenStreetMap · 流量:TomTom · 覆盖 {percent}%", - "SIMULATED · Roads: OpenStreetMap · Flow: TomTom (no matches)": "模拟 · 道路:OpenStreetMap · 流量:TomTom(无匹配)", + "TomTom roads first; OpenStreetMap fills the rest, simulated": "优先 TomTom 道路;其余由 OpenStreetMap 补足,路况为模拟", + "TomTom roads first; OpenStreetMap fills the rest, simulated (needs a TomTom key)": "优先 TomTom 道路;其余由 OpenStreetMap 补足,路况为模拟(需要 TomTom 密钥)", + "Hybrid": "混合", + "TomTom key unavailable": "TomTom 密钥不可用", + "TomTom daily budget reached": "已达 TomTom 每日配额", + "TomTom upstream unreachable": "TomTom 上游服务无法连接", + "TomTom flow unavailable": "TomTom 路况不可用", + "SIMULATED — {reason}": "模拟——{reason}", + "Syncing flow · Roads: {source}": "正在同步路况 · 道路:{source}", + "LIVE · Roads: {source} · Roads without flow hidden": "实时 · 道路:{source} · 已隐藏无路况道路", + "LIVE · Roads: {source} · No flow roads in view": "实时 · 道路:{source} · 视野内无路况道路", + "LIVE · Roads: {source} · Flow {percent}%": "实时 · 道路:{source} · 路况 {percent}%", + "SIMULATED · Roads: {source} · Flow {percent}%": "模拟 · 道路:{source} · 路况 {percent}%", + "Syncing flow · Roads: OpenStreetMap · Flow: TomTom · Unmatched: simulated": "正在同步路况 · 道路:OpenStreetMap · 路况:TomTom · 未匹配:模拟", + "LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov · Unmatched: simulated": "实时 · 道路:OpenStreetMap · 路况:TomTom · 覆盖 {percent}% · 未匹配:模拟", + "LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov": "实时 · 道路:OpenStreetMap · 路况:TomTom · 覆盖 {percent}%", + "SIMULATED · Roads: OpenStreetMap · Flow: TomTom (no matches)": "模拟 · 道路:OpenStreetMap · 路况:TomTom(无匹配)", "SIMULATED — traffic service unreachable": "模拟——交通服务无法连接", "SIMULATED — add TomTom key for live": "模拟——添加 TomTom 密钥以获取实时数据", - "Route {route}": "航线{route}", + "OpenFreeMap tiles rate-limited": "OpenFreeMap 瓦片受到频率限制", + "OpenFreeMap tiles timed out": "OpenFreeMap 瓦片超时", + "OpenFreeMap tiles unavailable": "OpenFreeMap 瓦片不可用", + "occupancy|empty": "空车", + "occupancy|many seats available": "空座较多", + "occupancy|few seats available": "空座较少", + "occupancy|standing room only": "仅剩站位", + "occupancy|crushed standing room only": "拥挤,仅剩站位", + "occupancy|full": "已满", + "occupancy|not accepting passengers": "不载客", + "occupancy|not boardable": "无法上车", + "Route {route}": "线路 {route}", + "Vehicle {vehicle}": "车辆 {vehicle}", + "vehicle motion|Stopped": "停驶", + "No recent fix": "无近期定位", + "Waiting for update": "等待更新", + "Moving": "行驶中", + "reported {speed} km/h": "上报 {speed} km/h", + "reported hdg {heading}°": "上报航向 {heading}°", + "Last report: stopped at stop {stop}": "最近上报:停靠站点 {stop}", + "Last report: arriving at stop {stop}": "最近上报:即将到达站点 {stop}", + "Last report: next stop {stop}": "最近上报:下一站 {stop}", + "Shown at street level · depth not in the feed": "按地面高度显示 · 数据源未提供深度", + "Reported {seconds} s ago": "{seconds} 秒前上报", + "Reported {minutes} min ago": "{minutes} 分钟前上报", + "shown {seconds} s behind": "显示滞后 {seconds} 秒", + "{reported} (feed time)": "{reported}(数据源时间)", + "Available trail: {minutes} min (limited)": "可用轨迹:{minutes} 分钟(受限)", + "Available trail: {minutes} min": "可用轨迹:{minutes} 分钟", + "Bus": "公交车", + "Tram": "有轨电车", + "Subway": "地铁", + "Train": "火车", + "Ferry": "渡轮", + "Transit vehicle": "公共交通车辆", "AIS: {name}": "AIS: {name}", "{type} SPD: {speed} HDG: {heading}": "{type} SPD: {speed} HDG: {heading}", "VESSEL": "船舶", - "POS: LIVE": "位置:实时", - "POS: {time}Z": "位置:{time}Z", + "{speed}KT": "{speed} kn", + "{heading}DEG": "{heading}°", + "POS: LIVE": "POS:实时", + "POS: {time}Z": "POS:{time}Z", + "AIS live load failed": "AIS 实时数据加载失败", "awaiting first AIS position…": "等待首个 AIS 位置…", "awaiting usable AIS positions…": "等待可用的 AIS 位置…", "awaiting first AIS message…": "等待首条 AIS 消息…", @@ -607,32 +1114,44 @@ "feed disconnected": "数据源已断开", "feed down": "数据源中断", "feed idle": "数据源空闲", - "API key rejected — check AISSTREAM_API_KEY": "API 密钥被拒绝 — 请检查 AISSTREAM_API_KEY", - "feed silent {seconds}s — no AIS data": "数据源已静默 {seconds} 秒 — 无 AIS 数据", - "feed silent — no AIS data": "数据源已静默 — 无 AIS 数据", + "API key rejected — check AISSTREAM_API_KEY": "API 密钥被拒绝——请检查 AISSTREAM_API_KEY", + "feed silent {seconds}s — no AIS data": "数据源已静默 {seconds} 秒——无 AIS 数据", + "feed silent — no AIS data": "数据源已静默——无 AIS 数据", + "feed down — retrying slowly (attempt {attempt})": "数据源中断——正在低频重试(第 {attempt} 次)", + "reconnecting to feed… (attempt {attempt})": "正在重新连接数据源…(第 {attempt} 次)", + "feed down — retrying slowly": "数据源中断——正在低频重试", + "reconnecting to feed…": "正在重新连接数据源…", + "Focus vessel {name}, MMSI {mmsi}": "聚焦船舶 {name},MMSI {mmsi}", "Hidden by this map source · choose a globe map": "已被当前地图源隐藏 · 请选择球体地图", + "Weather image HTTP {status}": "天气图像 HTTP {status}", + "Weather HTTP {status}": "天气 HTTP {status}", "Frame unavailable; previous observation retained": "帧不可用;保留上一帧观测", "Weather imagery unavailable": "天气影像不可用", "Weather source unavailable; previous observation retained": "天气数据源不可用;保留上一帧观测", "Weather unavailable": "天气数据不可用", - "{hours}h {minutes}m ago": "{hours} 小时 {minutes} 分前", + "No frame within {gap} min of {time}": "{time} 前后 {gap} 分钟内没有帧", + "No frame within {gap} h of {time}": "{time} 前后 {gap} 小时内没有帧", + "{hours}h {minutes}m ago": "{hours} 小时 {minutes} 分钟前", "{minutes}m ago": "{minutes} 分钟前", "{state} · {time} · {lag}": "{state} · {time} · {lag}", "Observed": "观测", "History": "历史", "Observation unavailable": "观测不可用", "Waiting for observation": "等待观测数据", + "{detail} · synced": "{detail} · 已同步", + "{detail} · nearest": "{detail} · 最近", "RADAR REFLECTIVITY · dBZ": "雷达反射率 · dBZ", "LIGHTNING DENSITY · 15 min accumulation": "闪电密度 · 15 分钟累计", "GLOBAL INFRARED · hourly": "全球红外 · 每小时", "GOES INFRARED · ~5 min": "GOES 红外 · 约 5 分钟", "{state}: {time}": "{state}:{time}", "Latest observation": "最新观测", + "frame {index}/{total}": "第 {index}/{total} 帧", "loading": "加载中", "Observation: unavailable": "观测:不可用", "Contiguous US · gaps ≠ no rain": "美国本土 · 无回波 ≠ 无降雨", "Americas + Pacific · not individual strikes": "美洲与太平洋 · 非单次闪击", - "Color: strikes/km²/min ×10³": "颜色:次闪/km²/分钟 ×10³", + "Color: strikes/km²/min ×10³": "颜色:次闪击/km²/分钟 ×10³", "60°S–60°N · typically 2–3 h delayed": "60°S–60°N · 通常延迟 2–3 小时", "North America · infrared imagery": "北美洲 · 红外影像", "Reduced motion · history playback unavailable": "减弱动效 · 历史回放不可用", @@ -647,38 +1166,50 @@ "Stale source": "数据源已过期", "Loading next frame…": "正在加载下一帧…", "Map center outside coverage": "地图中心在覆盖范围之外", + "strikes/km²/min ×10³": "次闪击/km²/分钟 ×10³", "N. America": "北美", "Global": "全球", "Hourly global mosaic; usually 2–3 hours delayed": "每小时全球拼图;通常延迟 2–3 小时", "GOES regional clouds; approximately 5-minute updates": "GOES 区域云图;约 5 分钟更新", "Clouds only": "仅云区", + "Apply the full cloud brightness key; 100% filter strength": "应用完整的云层亮度映射;滤镜强度 100%", "Full": "完整", + "Show the complete infrared image; 0% filter strength": "显示完整红外图像;滤镜强度 0%", "Soft": "柔和", "Vivid": "鲜明", "Image opacity; does not alter the observed values": "图像不透明度;不改变观测值", "View US radar": "查看美国雷达", "View Americas & Pacific": "查看美洲与太平洋", "View coverage": "查看覆盖范围", - "{value} strikes/km²/min ×10³ (15-minute density)": "{value} 次闪/km²/分钟 ×10³(15 分钟密度)", + "{value} strikes/km²/min ×10³ (15-minute density)": "{value} 次闪击/km²/分钟 ×10³(15 分钟密度)", "{value} dBZ radar reflectivity": "{value} dBZ 雷达反射率", "STALE · cached source metadata": "已过期 · 数据源元数据为缓存", "Map center is outside source coverage": "地图中心在数据源覆盖范围之外", "NOAA/NWS 15-minute lightning density derived from Vaisala NLDN/GLD360. Coverage 110°E across the Pacific/Americas to 0°, 25°S–80°N. Not a live strike count, global coverage or a safety warning.": "NOAA/NWS 15 分钟闪电密度,来源为 Vaisala NLDN/GLD360。覆盖自 110°E 经太平洋/美洲至 0°,25°S–80°N。不是实时闪击计数、不是全球覆盖,也不构成安全警示。", - "NOAA MRMS radar echoes indicate precipitation patterns, not rain rate, a storm warning or a future forecast. Native source approximately 1 km; display is limited to level 6. Frames use exact advertised observation times.": "NOAA MRMS 雷达回波反映降水形态,不代表降雨强度、风暴警报或未来预报。原始数据约 1 公里分辨率;显示限制在第 6 级。各帧使用数据源公布的精确观测时间。", + "NOAA MRMS radar echoes indicate precipitation patterns, not rain rate, a storm warning or a future forecast. Native source approximately 1 km; display is limited to level 6. Frames use exact advertised observation times.": "NOAA MRMS 雷达回波反映降水形态,不代表降雨强度、风暴警报或未来预报。原始数据分辨率约 1 km;显示限制在第 6 级。各帧使用数据源公布的精确观测时间。", "GOES-19/18 longwave infrared Band 14 regional; NESDIS global longwave mosaic. Clouds only dims everything but bright, cold cloud tops; a brightness filter, not a cloud mask. Coverage and freshness differ by region.": "GOES-19/18 第 14 波段长波红外区域图;NESDIS 全球长波拼图。“仅云区”会调暗除明亮冷云顶之外的一切;这是亮度滤镜,不是云掩膜。覆盖范围与新鲜度因区域而异。", "REGION": "区域", "IMAGE": "图像", + "FILTER": "滤镜", + "Cloud filter strength": "云层滤镜强度", "OPACITY": "不透明度", + "Infrared decode failed": "红外图像解码失败", + "Weather prefetch unavailable": "天气预取不可用", "Weather tiles unavailable · previous frame retained": "天气瓦片不可用 · 保留上一帧", "Some weather tiles unavailable": "部分天气瓦片不可用", + "Malformed weather manifest": "天气清单格式错误", + "Weather request timed out": "天气请求超时", "Temperature field unavailable": "温度字段不可用", + "Mean sea level pressure field unavailable": "平均海平面气压场不可用", + "Wind upstream unavailable": "风场上游数据源不可用", + "{source} HTTP {status}": "{source} HTTP {status}", "Wind": "风", "Wind source unavailable": "风场数据源不可用", "Forecast · does not follow history": "预报 · 不随观测历史变化", "Wind motion": "风场运动", "Wind speed": "风速", "Sea-level pressure": "海平面气压", - "Air temperature · 2 m": "2 米气温", + "Air temperature · 2 m": "气温 · 2 m", "Global · 1° grid": "全球 · 1° 网格", "{model} forecast · {time}": "{model} 预报 · {time}", "Loading forecast": "正在加载预报", @@ -703,32 +1234,69 @@ "Read wind at map center": "读取地图中心风速", "Read the forecast at the center of the map without changing selection": "读取地图中心的预报值,不改变当前选择", "{model} forecast · {field} ({unit})": "{model} 预报 · {field}({unit})", - "Valid: {time}": "生效:{time}", + "Valid: {time}": "有效时间:{time}", + "preparing": "准备中", "Issued: {time}": "发布:{time}", "Selected field unavailable · wind remains visible": "所选字段不可用 · 风场仍可见", "{error} · wind remains visible": "{error} · 风场仍可见", - "Surface wind at 10 m. Approximately 1° global grid. Curves follow the 10 m wind field, lifted 12 km for visibility; display height is not weather altitude. View lighting is for readability. Animation shows flow through one fixed forecast; it does not advance time. Color fields drape the globe basemap or the active photorealistic 3D Tiles.": "10 米高度的地表风。约 1° 全球网格。流线沿 10 米风场绘制,为便于观看抬升至 12 公里;显示高度并非天气高度。视图光照仅为可读性服务。动画展示的是单一固定预报内的流场,不推进时间。颜色字段贴附于球体底图或当前的照片级 3D Tiles。", + "Surface wind at 10 m. Approximately 1° global grid. Curves follow the 10 m wind field, lifted 12 km for visibility; display height is not weather altitude. View lighting is for readability. Animation shows flow through one fixed forecast; it does not advance time. Color fields drape the globe basemap or the active photorealistic 3D Tiles.": "10 m 高度的地表风。约 1° 全球网格。流线沿 10 m 风场绘制,为便于观看抬升至 12 km;显示高度并非天气高度。视图光照仅为可读性服务。动画展示的是单一固定预报内的流场,不推进时间。颜色字段贴附于球体底图或当前的照片级 3D Tiles。", "MODEL": "模型", "FIELD": "字段", "UNITS": "单位", "MOTION": "运动", "Forecast": "预报", + "NNE": "北东北", + "NE": "东北", + "ENE": "东东北", + "ESE": "东东南", + "SE": "东南", + "SSE": "南东南", + "SSW": "南西南", + "SW": "西南", + "WSW": "西西南", + "WNW": "西西北", + "NW": "西北", + "NNW": "北西北", "No surface reading": "暂无地表读数", "Aim the map center at Earth": "请将地图中心对准地球", "A location reading needs a loaded forecast and the Earth at the center of the view.": "位置读数需要已加载的预报,且地图中心对准地球。", "Interpolated model forecast on an approximately 1° grid. Broad weather patterns, not a street-level measurement.": "按约 1° 网格插值的模式预报。反映大范围天气形势,不是街道级实测。", - " from {direction}": "从{direction}出发", + " · calm": " · 静风", + " from {direction}": ",{direction}风", "WIND AT {coordinates}": "{coordinates} 的风", - "{model} · valid {time}": "{model} · 生效 {time}", + "{model} · valid {time}": "{model} · 有效时间 {time}", + "Wind speed field unavailable": "风速场不可用", "Globe imagery unavailable": "球体影像不可用", "Globe field image unavailable": "球体字段图像不可用", + "Unknown wind model": "未知风场模式", + "Unknown weather overlay": "未知天气叠加层", + "Wind request timed out": "风场请求超时", + "Malformed wind manifest": "风场清单格式错误", + "Malformed wind grid": "风场网格格式错误", "Error: {message}": "错误:{message}", "Needs {envVars} — add it in Provider Settings": "需要 {envVars},请在服务商设置中添加", "{label} is unavailable": "{label} 不可用", "This map stack": "此地图组合", "{label} unavailable: {hint}": "{label} 不可用:{hint}", - "Google 3D tiles unavailable — check the key's API restrictions, quota, or network": "Google 3D 瓦片不可用 — 请检查密钥的 API 限制、配额或网络", + "Google 3D tiles unavailable — check the key's API restrictions, quota, or network": "Google 3D 瓦片不可用——请检查密钥的 API 限制、配额或网络", + "Needs {envVars} — add it in Provider Settings — or a Cesium ion token for the ion-hosted route": "需要 {envVars}(请在服务商设置中添加),或使用 Cesium ion 令牌走 ion 托管通道", + "Exit cockpit to fly to a camera": "请先退出座舱,再飞往摄像头", + "Exit cockpit to fly to a location": "请先退出座舱,再飞往该地点", + "Exit cockpit to fly to a photo": "请先退出座舱,再飞往照片", + "Exit cockpit to fly to a shared view": "请先退出座舱,再飞往分享的视图", + "Exit cockpit to fly to a target": "请先退出座舱,再飞往目标", + "Exit cockpit to fly to a vessel": "请先退出座舱,再飞往船舶", + "Exit cockpit to fly to a fire": "请先退出座舱,再飞往火点", + "Exit cockpit to fly to a route": "请先退出座舱,再飞往路线", + "Exit cockpit to fly to a file": "请先退出座舱,再飞往文件位置", + "Exit cockpit to fly to a scene": "请先退出座舱,再飞往场景", + "Exit cockpit to view weather coverage": "请先退出座舱,再查看天气覆盖范围", + "Exit cockpit to fly to a cyclone": "请先退出座舱,再飞往气旋", + "Exit cockpit to fly to an aircraft": "请先退出座舱,再飞往飞机", + "Exit cockpit to fly to a satellite": "请先退出座舱,再飞往卫星", + "Exit cockpit to frame contacts": "请先退出座舱,再取景目标", "Visible map targets": "可见的地图目标", + "Focusing {label}": "正在聚焦 {label}", "Source": "来源", "Camera ownership changed": "相机所有权已变更", "Saved project could not be read; storage preserved. Import a valid file to resume saving.": "已保存的项目无法读取;存储已保留。请导入有效文件以恢复保存。", @@ -782,11 +1350,41 @@ "Thermal Threat Board": "热源威胁看板", "City Overload": "城市过载", "Omniscience Pullback": "全知拉远", + "Global Incident Context": "全球事件背景", + "Nepal-Focused Globe Rotation": "聚焦尼泊尔的地球旋转", + "Bhote Koshi Regional Approach": "Bhote Koshi 区域接近", + "Bhote Koshi Nearby Cities": "Bhote Koshi 周边城市", + "Bhote Koshi Incident Corridor": "Bhote Koshi 事件走廊", + "Bhote Koshi Flood Path": "Bhote Koshi 洪水路径", + "Bhote Koshi Corridor Overview": "Bhote Koshi 走廊概览", + "Bhote Koshi Upper Valley": "Bhote Koshi 上游河谷", + "Upper Valley Collapse": "上游河谷崩塌", + "Debris-Dammed Lake": "碎屑堰塞湖", + "Nepal-China Border Gate": "尼中边境口岸", + "Timure Evidence Cluster": "Timure 证据群", + "Syabru Besi Passage": "Syabru Besi 通道", + "Bidur / Trishuli Consequence": "Bidur / Trishuli 后果", + "Final view": "最终视图", "Scene {n}": "场景 {n}", "Shot {n}": "镜头 {n}", + "the title of scene {scene} is empty or longer than {max} characters": "场景 {scene} 的标题为空或超过 {max} 个字符", + "the title of shot {shot} in scene {scene} is empty or longer than {max} characters": "场景 {scene} 中镜头 {shot} 的标题为空或超过 {max} 个字符", + "the project is larger than 5 MiB": "项目大于 5 MiB", + "the project has too many shots": "项目中的镜头过多", + "the scene data failed validation": "场景数据未通过校验", "Could not complete this action. Check the file, references and selected assets.": "无法完成此操作。请检查文件、引用和所选资源。", + "{count} scenes": { + "other": "{count} 个场景" + }, + "{count} shots": { + "other": "{count} 个镜头" + }, + "{count} data packs": { + "other": "{count} 个数据包" + }, "{scene} / {pack}: {status}. File: {path}. {attribution} · {license}": "{scene} / {pack}:{status}。文件:{path}。{attribution} · {license}", "Unavailable layers: {layers}": "不可用图层:{layers}", + "Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "使用了已注册内容或链接媒体。这些外部文件不包含在场景资源包中。其原始声明仍然适用。", "{bytes} bundled bytes verified. Files stay in memory for this session. Reimport the bundle after reloading the app.": "已校验 {bytes} 个打包字节。文件仅保留在本次会话的内存中。重新加载应用后请再次导入资源包。", "Review scene import": "审查场景导入", "Nothing is loaded or changed until you apply this file.": "在你应用此文件之前,不会载入或更改任何内容。", @@ -815,35 +1413,115 @@ "Or choose a data-pack folder": "或选择数据包文件夹", "Download asset bundle": "下载资源包", "Asset bundle downloaded": "资源包已下载", + "No RTL-SDR device selected": "未选择 RTL-SDR 设备", + "WebUSB requires desktop Chrome or Edge on localhost/HTTPS": "WebUSB 需要在 localhost/HTTPS 上使用桌面版 Chrome 或 Edge", + "RTL-SDR access failed; close other SDR apps and check USB permissions": "RTL-SDR 访问失败;请关闭其他 SDR 应用并检查 USB 权限", + "RTL-SDR connection failed": "RTL-SDR 连接失败", + "Receiving 1090 MHz ADS-B": "正在接收 1090 MHz ADS-B", + "Receiving broadcast FM": "正在接收广播 FM", + "Connect an RTL-SDR to begin.": "连接 RTL-SDR 即可开始。", + "WebUSB is unavailable in this browser": "此浏览器不支持 WebUSB", + "USB work abandoned after a stalled transfer": "USB 传输停滞,已放弃操作", + "SDR decoder worker failed": "SDR 解码器工作线程失败", + "SDR decoder did not start; receiver stopped": "SDR 解码器未能启动;接收机已停止", + "SDR decoder stopped responding; receiver stopped": "SDR 解码器停止响应;接收机已停止", + "SDR decoder fell behind; receiver stopped": "SDR 解码器处理滞后;接收机已停止", + "WebUSB requires desktop Chrome or Edge": "WebUSB 需要桌面版 Chrome 或 Edge", + "Waiting for an RTL-SDR device…": "正在等待 RTL-SDR 设备…", + "Browser audio is suspended; press Connect again to resume it": "浏览器音频已挂起;再次按“连接”以恢复", + "RTL-SDR sample stream stopped": "RTL-SDR 采样流已停止", + "Switching to {mode}…": "正在切换到 {mode}…", + "Browser audio is suspended; select FM again to resume it": "浏览器音频已挂起;再次选择 FM 以恢复", + "RTL-SDR gain change failed": "RTL-SDR 增益调整失败", + "Tuned to {mhz} MHz FM": "已调谐到 FM {mhz} MHz", + "Seeking up…": "正在向上搜台…", + "Seeking down…": "正在向下搜台…", + "Seek stopped": "搜台已停止", + "Scanning {mhz} MHz…": "正在扫描 {mhz} MHz…", + "Found {mhz} MHz · {snr} dB": "找到 {mhz} MHz · {snr} dB", + "FM signal found at {mhz} MHz": "在 {mhz} MHz 找到 FM 信号", + "No FM signal found": "未找到 FM 信号", + "Browser location is unavailable": "浏览器位置不可用", + "Receiver location was not granted": "未授予接收机位置权限", + "RTL-SDR disconnected": "RTL-SDR 已断开", + "Upstream response too large": "上游响应过大", + "Vector tile request timed out": "矢量瓦片请求超时", + "Vector tiles unavailable": "矢量瓦片不可用", + "Vector tile metadata unavailable": "矢量瓦片元数据不可用", + "Zoom in for vector tile coverage": "放大以查看矢量瓦片覆盖", + "Saturation": "饱和度", + "Edge Thickness": "描边粗细", + "Contrast": "对比度", + "Grain": "颗粒", + "Vignette": "暗角", + "Pixelation": "像素化", + "Distortion": "畸变", + "Instability": "不稳定", "Density": "密度", "Gain": "增益", "Bloom": "泛光", + "Scanlines": "扫描线", + "Sensitivity": "灵敏度", + "WHOT/BHOT": "WHOT/BHOT", + "Ironbow": "Ironbow", + "unsaved CCTV calibration": "未保存的 CCTV 校准", + "the open cockpit": "当前打开的座舱", + "Switching language reloads the page. This will be lost: {items}. Switch anyway?": "切换语言会重新加载页面。以下内容将会丢失:{items}。仍要切换吗?", "VISUAL PRESETS": "视觉预设", "LOCATION": "位置", - "CONTACTS — nearest planes · vessels · sites": "目标:最近的飞机 · 船舶 · 站点", + "CONTACTS — nearest planes · vessels · sites": "目标:最近的飞机 · 船舶 · 设施", "SPACE MISSIONS — launches & orbital assets": "太空任务:发射与在轨资产", "LIVE SIGNALS": "实时信号", "CCTV layer unavailable": "CCTV 图层不可用", "Link copied!": "链接已复制!", "Copy failed": "复制失败", + "Return map to straight-down view": "将地图恢复为垂直俯视", "Tilt map to oblique view": "将地图倾斜至斜视角", + "Reset map to north up. Current heading {heading} degrees": "将地图重置为朝北。当前航向 {heading} 度", + "Tilted view": "倾斜视角", + "Straight-down view": "垂直俯视", + "North up": "朝北", "CCTV calibration saved": "CCTV 校准已保存", + "HDG": "HDG", + "PITCH": "俯仰", + "FOV": "FOV", "RANGE": "范围", - "{value}m": "{value}分钟", + "HGT": "高度", + "ΔN": "ΔN", + "ΔE": "ΔE", + "{value}m": "{value} m", "CCTV calibration reset": "CCTV 校准已重置", "ADJUST ON": "调整开", "ADJUST": "调整", + "SNAPSHOT": "快照", "LIVE": "实时", + "UPSTREAM": "上游", + "SYNTHETIC": "合成", + "CONFIGURED": "已配置", + "SEED": "种子", "UNKNOWN": "未知", "ERROR": "错误", + "Live HLS connected": "实时 HLS 已连接", + "Live HLS unavailable": "实时 HLS 不可用", + "No stream URL configured": "未配置流 URL", + "Live stream connected": "实时流已连接", + "Snapshot feed connected": "快照数据源已连接", + "Media fetch failed": "媒体获取失败", + "Upstream snapshot active": "上游快照已启用", + "Fallback Street View frame": "备用街景画面", + "Upstream unavailable": "上游不可用", + "No source configured": "未配置来源", "Configured source": "已配置数据源", "Configured Source": "已配置数据源", + "Synthetic fallback": "合成备用", + "Upstream HTTP {status}": "上游 HTTP {status}", + "Unexpected media type {type}": "意外的媒体类型 {type}", "SOURCE · UNKNOWN": "来源 · 未知", "FRAME · LOADING": "画面 · 加载中", "FRAME · UNAVAILABLE": "画面 · 不可用", "CALIBRATED": "已校准", "CURATED": "已整理", - "RAW PRIOR": "原始默认", + "RAW PRIOR": "初始估计", "CCTV ON": "CCTV 开", "CCTV OFF": "CCTV 关", "VIEWSHED ON": "可视域开", @@ -857,14 +1535,14 @@ "CAL · {badge}": "CAL · {badge}", "MONITOR": "监视器", "OFF": "关", - "{city} · HDG {heading} · FOV {fov} · RANGE {range}m · {projection}{calBadge} · {provider}{status}": "{city} · HDG {heading} · FOV {fov} · 范围 {range}m · {projection}{calBadge} · {provider}{status}", + "{city} · HDG {heading} · FOV {fov} · RANGE {range}m · {projection}{calBadge} · {provider}{status}": "{city} · HDG {heading} · FOV {fov} · 范围 {range} m · {projection}{calBadge} · {provider}{status}", "{count} cameras loaded · click a camera to activate": { "other": "已加载 {count} 个摄像头 · 点击摄像头以激活" }, "{count} cameras loaded · enable CCTV to activate": { "other": "已加载 {count} 个摄像头 · 启用 CCTV 以激活" }, - "Enable CCTV to load camera intersections": "启用 CCTV 以加载摄像头路口", + "Enable CCTV to load camera intersections": "启用 CCTV 以加载路口摄像头", "Enable CCTV to start camera-linked intelligence summaries.": "启用 CCTV 以开始生成与摄像头关联的情报摘要。", "No summary available.": "暂无摘要。", "loading frames": "正在加载画面", @@ -884,7 +1562,7 @@ "MIXED CONDITIONS": "混合天气", "CYCLE ON": "轮换开", "CYCLE OFF": "轮换关", - "Stop automatic page cycling. Previous, Next, and the SIG/NEWS/LOCAL tabs stay available.": "停止自动翻页。上一页、下一页以及 SIG/NEWS/LOCAL 标签仍可使用。", + "Stop automatic page cycling. Previous, Next, and the SIG/NEWS/LOCAL tabs stay available.": "停止自动翻页。上一页、下一页以及“信号”“新闻”“本地”标签仍可使用。", "Cycle briefing pages automatically every 9 seconds (Signals → News → Local). Pauses while you hover or focus the panel. Live signal data refreshes continuously either way.": "每 9 秒自动轮换简报页面(信号 → 新闻 → 本地)。悬停或聚焦面板时暂停。无论是否轮换,实时信号数据都会持续刷新。", "ACQUIRING REGIONAL NEWS": "正在获取地区新闻", "REGIONAL NEWS UNAVAILABLE": "地区新闻不可用", @@ -892,12 +1570,15 @@ "REGION UNAVAILABLE": "区域不可用", "NO RECENT LOCATION MATCHES": "暂无近期位置匹配结果", "{domain} · {age}": "{domain} · {age}", + "{value} KM/H": "{value} km/h", "CLOUD {pct}%": "云量 {pct}%", "CLOUD UNKNOWN": "云量未知", "{source} · LOCATION QUERY": "{source} · 位置查询", "REGIONAL NEWS": "地区新闻", - "CONTEXT STANDBY": "上下文待机", - "ENABLE GLOBAL CONTEXT FOR PROXIMITY PINGS": "启用全局上下文以获取邻近 PING", + "{label} · {radius} AIR/SEA WINDOW": "{label} · {radius} 空中 / 海上范围", + "{scope} · INSTALLATIONS {coverage}": "{scope} · 设施 {coverage}", + "CONTEXT STANDBY": "态势待机", + "ENABLE GLOBAL CONTEXT FOR PROXIMITY PINGS": "启用全局态势以获取邻近提示", "CONTACT LOST · LAST KNOWN READOUT · NOT AN ALL-CLEAR": "目标丢失 · 最后已知读数 · 不代表安全无虞", "CONTACT LOST · {subject}": "目标丢失 · {subject}", "SUBJECT LEFT ITS FEED · READOUT HOLDING LAST KNOWN": "目标已脱离其数据源 · 读数保持最后已知值", @@ -912,6 +1593,9 @@ "other": "{count} 项输入未知 · 不代表安全无虞" }, "AVAILABLE INPUTS CURRENT · NOT AN ALL-CLEAR": "可用输入均为最新 · 不代表安全无虞", + "SQUAWK {code} · {status}": "应答码 {code} · {status}", + "ADS-B PRIORITY · {status}": "ADS-B 优先 · {status}", + "ADS-B EMERGENCY · {status}": "ADS-B 紧急 · {status}", "AIRCRAFT": "飞机", "ACQUIRING SURFACE": "正在获取地面数据", "SURFACE FALLBACK": "地面数据备用", @@ -930,7 +1614,7 @@ "Night vision": "夜视", "Thermal": "热成像", "Anime": "动漫", - "Noir": "黑色电影", + "Noir": "黑白", "Snow": "雪景", "Current cockpit vision style: {style}. Activate for next style.": "当前座舱视觉样式:{style}。激活可切换到下一个样式。", "Current style: {style} — click for next": "当前样式:{style}——点击切换下一个", @@ -942,9 +1626,10 @@ "Expand cockpit briefing panel": "展开座舱简报面板", "Collapse briefing panel": "收起简报面板", "Expand briefing panel": "展开简报面板", - "OBSERVED / MAPPED PINGS": "观测 / 地图标注 PING", + "OBSERVED / MAPPED PINGS": "观测 / 地图标注提示", "SOURCE-BACKED EVENTS · NO SYNTHETIC NEWS": "来源可查的事件 · 无合成新闻", "LATEST LOCATION-MATCHED REPORTING": "最新的位置匹配报道", + "{source} · LOCATION QUERY · RECENT": "{source} · 位置查询 · 近期", "LOCAL INFO": "本地信息", "PLACE / CONDITIONS / POSITION": "地点 / 状况 / 位置", "TIME UNKNOWN": "时间未知", @@ -966,7 +1651,7 @@ "Reclassify as TR-3B": "重新归类为 TR-3B", "TRACK ACQUIRED": "已锁定航迹", "{label} · COURSE {heading}°": "{label} · 航向 {heading}°", - "Context could not restore every layer; try again": "上下文未能恢复所有图层,请重试", + "Context could not restore every layer; try again": "态势未能恢复所有图层,请重试", "No selected data layers": "没有已选数据图层", "{count} data layers could not be cleared": { "other": "{count} 个数据图层无法清除" @@ -980,18 +1665,40 @@ "Nearby installations refreshed": "附近设施已刷新", "Nearby installations could not be refreshed; try again": "无法刷新附近设施,请重试", "Space Missions cancellation could not restore the previous layer state": "取消太空任务后无法恢复先前的图层状态", - "That layer is unavailable in the current Context mode": "该图层在当前上下文模式下不可用", + "That layer is unavailable in the current Context mode": "该图层在当前态势模式下不可用", "{layerId} could not {action} cleanly": "{layerId} 未能正常{action}", "start": "启动", "stop": "停止", "{mode} could not start because another layer did not stop cleanly": "{mode}无法启动,因为另一个图层未能正常停止", "Space Missions": "太空任务", - "Context": "上下文", + "Context": "态势", "Rendered globe frames per second · toggle with `": "每秒渲染的地球帧数 · 按 ` 键切换", + "imported map files": "已导入的地图文件", + "Ground heights could not be refreshed. Try the file again when the map has loaded.": "无法刷新地面高度。请在地图加载完成后重新尝试该文件。", + "{count} map objects": { + "other": "{count} 个地图对象" + }, + ", drawn with its own styles": ",使用其自带样式绘制", + "Hide {name}": "隐藏 {name}", "Show {name}": "显示 {name}", - "Fly to {name}": "飞往{name}", + "Fly to {name}": "飞往 {name}", + "Remove {name}": "移除 {name}", + "At most {count} files at once — remove one first.": "一次最多 {count} 个文件——请先移除一个。", + "Reading {name}…": "正在读取 {name}…", + "That file could not be read.": "无法读取该文件。", + "Added {count} files.": { + "other": "已添加 {count} 个文件。" + }, + "{count} files could not be added ({name}: {error})": { + "other": "{count} 个文件无法添加({name}:{error})" + }, + "Some KML content was left out.": "部分 KML 内容未被导入。", "Press on the ground, not the sky": "请按在地面,而不是天空", "Drag to size the box": "拖动以调整框选大小", + "light {source} to use your {name} key": "开启 {source} 以使用你的 {name} 密钥", + "light {source}": "开启 {source}", + ", or ": ",或", + "{requirement}, or {offer}": "{requirement},或{offer}", "Live Flights": "实时航班", "Military Flights": "军用航班", "Earthquakes (24h)": "地震(24 小时)", @@ -1003,32 +1710,39 @@ "Bikeshare": "共享单车", "Live AIS Vessels": "实时 AIS 船舶", "Mapped Installations": "地图标注设施", - "Global Context": "全局上下文", + "Global Context": "全局态势", "Datacenters": "数据中心", "Dams": "水坝", "Submarine Cables": "海底电缆", "FIRMS Active Fires": "FIRMS 活跃火点", "Local ADS-B": "本地 ADS-B", - "Transit": "交通", + "Transit": "公共交通", "Recent Imagery": "近期影像", "Fire Perimeters": "火场周界", + "GNSS Integrity": "GNSS 完好性", "Rain radar": "降雨雷达", "Lightning density": "闪电密度", "Cyclone advisories": "气旋公告", "ALPR Cameras": "ALPR 摄像头", "Live Vessels": "实时船舶", - "Bike Share": "公共自行车", - "Cameras": "相机", + "Bike Share": "共享单车", + "Cameras": "摄像头", "Street Level": "街景", "Mapped ALPR Cameras": "ALPR 摄像头", "Data Centers": "数据中心", "Active Fires": "活跃火点", "Military aircraft": "军用飞机", + "Vessels": "船舶", + "CCTV + Street View fallback": "CCTV + 街景备用", + "OpenStreetMap + optional Google Maps Places": "OpenStreetMap + 可选 Google Maps Places", "GFS / ECMWF IFS · FORECAST": "GFS / ECMWF IFS · 预报", "NOAA nowCOAST · OBSERVED": "NOAA nowCOAST · 观测", + "NASA FIRMS · LIVE": "NASA FIRMS · 实时", + "OSM routing": "OSM 路线规划", + "data source|Local": "本地", "ENABLING": "启用中", "DISABLING": "停用中", - "Movement": "移动", + "Movement": "动态目标", "Infrastructure": "基础设施", "Events": "事件", "Weather": "天气", @@ -1038,7 +1752,7 @@ "UNCERTAIN · {source} · lifecycle state requires reconciliation": "不确定 · {source} · 生命周期状态需要核对", "{state} · {source} · {detail} · retry {seconds}s": "{state} · {source} · {detail} · {seconds} 秒后重试", "{state} · {source} · {detail}": "{state} · {source} · {detail}", - "never": "从未", + "never": "未更新", "{source} · {loadingLabel}": "{source} · {loadingLabel}", "{accepted} of {raw} records accepted": "已接受 {accepted} / {raw} 条记录", "incomplete snapshot": "快照不完整", @@ -1047,20 +1761,33 @@ "{state} · {source} · {ago}": "{state} · {source} · {ago}", "{source} · {ago}": "{source} · {ago}", "UNCERTAIN": "不确定", + "status chip|UNAVAILABLE": "不可用", "Data source unavailable": "数据源不可用", "{name}: {state}": "{name}:{state}", "{count}s ago": "{count} 秒前", + "Local FM stopped: internet radio started": "本地 FM 已停止:网络电台已开始播放", + "IDLE": "空闲", "CONNECTING": "连接中", "TUNING": "调谐中", - "STREAMING": "正在流式接收", + "STREAMING": "接收中", + "UNSUPPORTED": "不支持", + "idle": "空闲", + "running": "运行中", + "suspended": "已挂起", + "closed": "已关闭", + "feed status|live": "实时", + "feed status|stale": "过期", + "feed status|unreachable": "无法访问", + "feed status|invalid": "无效", "waiting for IQ": "等待 IQ 数据", "{count} messages": { "other": "{count} 条消息" }, "DSP {count} blocks": { - "other": "DSP {count} 个模块" + "other": "DSP {count} 个数据块" }, "DSP waiting": "DSP 等待中", + "{level} dBFS audio": "音频 {level} dBFS", "audio signal --": "音频信号 --", "audio {state}": "音频 {state}", "{count} HEARD": { @@ -1072,7 +1799,12 @@ "LOCATED": "已定位", "LOCATE": "定位", " · read while Local ADS-B is on": " · 仅在本地 ADS-B 开启时读取", + "Decoder feeds: {feeds}": "解码器数据源:{feeds}", + "Language: {language}": "语言:{language}", + "Change the interface language": "更改界面语言", "Interface language": "界面语言", + "Search languages": "搜索语言", + "No matching languages": "没有匹配的语言", "📍 Location: --": "📍 位置:--", "Landmark: --": "地标:--", "Searched location": "已搜索的位置", @@ -1093,7 +1825,9 @@ "Collapse Radio": "收起电台", "Expand Radio section": "展开电台区域", "Collapse Radio section": "收起电台区域", + "Double-click the header to snap the panel back": "双击标题栏可将面板复位", "Panel layout updated — positions reset to new defaults": "面板布局已更新——位置已重置为新的默认值", + "Drag to resize · double-click the header to snap back": "拖动以调整大小 · 双击标题栏复位", "Austin": "奥斯汀", "San Francisco": "旧金山", "New York": "纽约", @@ -1103,6 +1837,7 @@ "Dubai": "迪拜", "Washington DC": "华盛顿特区", "Tallinn": "塔林", + "Los Angeles": "洛杉矶", "Collapse Cockpit display options": "收起座舱显示选项", "Expand Cockpit display options": "展开座舱显示选项", "Collapse Cockpit Radio controls": "收起座舱电台控件", @@ -1116,8 +1851,8 @@ "STATION UNAVAILABLE": "电台不可用", "Station unavailable after directory refresh": "目录刷新后该电台不可用", "Radio could not {action} cleanly": "电台未能正常{action}", - "Go to expanded Radio section": "前往展开的电台分区", - "Expand Radio section in Context": "在态势面板中展开电台分区", + "Go to expanded Radio section": "前往展开的电台区域", + "Expand Radio section in Context": "在态势面板中展开电台区域", "Close compact Radio controls": "关闭精简电台控件", "Open compact Radio controls": "打开精简电台控件", "SYNC": "同步", @@ -1128,7 +1863,7 @@ "Disable Radio": "停用电台", "Enable Radio": "启用电台", "DIRECTORY BAND": "目录频段", - "{category} BAND": "{category}频段", + "{category} BAND": "{category} 频段", "NO STATION SELECTED": "未选择电台", "Directory metadata only": "仅目录元数据", "Loading station directory…": "正在加载电台目录…", @@ -1174,7 +1909,12 @@ "Newest frame per product": "每个产品的最新一帧", "Replay observed history": "回放观测历史", "Imagery on Esri · Google 3D returns when cleared": "影像来自 Esri · 清除后恢复 Google 3D", + "Point the camera at the ground to use the view": "将相机对准地面才能使用当前视野", + "That point cannot anchor a box": "该点无法作为框选锚点", + "Imagery stops at ±{limit}° latitude": "影像仅覆盖到纬度 ±{limit}°", "{owner} is using the pointer — close it first": "{owner} 正在使用指针——请先将其关闭", + "Box is {size} wide · limit {limit}": "框选宽 {size} · 上限 {limit}", + "View is {size} wide · limit {limit}": "视野宽 {size} · 上限 {limit}", "VS BASEMAP": "对比底图", "A / B": "A / B", "Newest low-cloud day for this box · scene cloud, not box cloud": "此框选最新的低云日 · 云量为场景云量,而非框内云量", @@ -1187,6 +1927,8 @@ "newest overview": "最新概览", "No imagery": "无影像", "Loading": "加载中", + "coverage|full": "完整", + "coverage|partial": "部分", "unknown": "未知", "no imagery": "无影像", "{percent}% cloud": "{percent}% 云量", @@ -1215,7 +1957,7 @@ "Trade the two sides of the divider": "互换分割线两侧", "SELECT BOX": "框选区域", "Drag a box on the map (Esc cancels)": "在地图上拖出框选(Esc 取消)", - "USE VIEW": "使用当前视野", + "USE VIEW": "使用视野", "Use the current view as the box": "以当前视野作为框选", "Forget the box and its images": "清除框选及其影像", "START HERE": "从此开始", @@ -1225,19 +1967,24 @@ "SHOW": "显示", "Pin {day} as {slot}": "将 {day} 钉为 {slot}", "Show {day}": "显示 {day}", + "pinned {slot}": "已钉为 {slot}", + "shown": "显示中", "preview": "预览", "start here": "从此开始", "No box": "无框选", "Searching": "搜索中", "Every day is empty here": "此处每天都为空", "Sources off": "数据源未开启", + "VS": "对比", "Not set": "未设置", "Basemap": "底图", + "Source off": "数据源已关", "Unpin {slot}": "取消钉住 {slot}", "Unpin the image": "取消钉住图像", "EXPORT {slot}": "导出 {slot}", "EXPORT": "导出", "Download {slot} as a PNG": "将 {slot} 下载为 PNG", + "Download the image as a PNG": "将图像下载为 PNG", "Box {width} × {height} km": "框选 {width} × {height} km", "Acquired {time}": "获取于 {time}", "Coverage {coverage}": "覆盖 {coverage}", @@ -1264,14 +2011,29 @@ "Stopped (Esc)": "已停止(Esc)", "SPARSE": "稀疏", "BALANCED": "均衡", - "No shots yet. Use CAPTURE SHOT to save current look.": "暂无镜头。使用“采集镜头”保存当前画面。", + "No shots yet. Use CAPTURE SHOT to save current look.": "暂无镜头。使用“截取镜头”保存当前画面。", "Double-click to rename": "双击重命名", "Shot name": "镜头名称", "LOAD": "载入", "DEL": "删除", + "{duration}s + {hold}s": "{duration} 秒 + {hold} 秒", + "Choose files": "选择文件", + "{count} files selected": { + "other": "已选择 {count} 个文件" + }, + "No file chosen": "未选择文件", "Cancel": "取消", "EDIT DETAILS": "编辑详情", "SHARE SCENE": "分享场景", + "Shared flight follow expired": "共享的航班跟随已过期", + "Shared military flight follow expired": "共享的军用航班跟随已过期", + "Shared satellite follow expired": "共享的卫星跟随已过期", + "Shared flight could not be restored — feed unavailable": "无法恢复共享的航班——数据源不可用", + "Shared military flight could not be restored — feed unavailable": "无法恢复共享的军用航班——数据源不可用", + "Shared satellite could not be restored — feed unavailable": "无法恢复共享的卫星——数据源不可用", + "Shared flight is unavailable": "共享的航班不可用", + "Shared military flight is unavailable": "共享的军用航班不可用", + "Shared satellite is unavailable": "共享的卫星不可用", "entity": "实体", "Shared {subject} follow expired": "共享的 {subject} 跟随已过期", "Shared {subject} could not be restored — feed unavailable": "无法恢复共享的 {subject}——数据源不可用", @@ -1284,9 +2046,9 @@ "LOAD CANCELLED": "加载已取消", "LOAD FAILED": "加载失败", "LIVE DATA OFF": "实时数据已关闭", - "MAPPED SITES LOADED": "已加载地图站点", - "RETRYING MAPPED SITES": "正在重试地图站点", - "FETCHING MAPPED SITES": "正在获取地图站点", + "MAPPED SITES LOADED": "已加载地图标注设施", + "RETRYING MAPPED SITES": "正在重试地图标注设施", + "FETCHING MAPPED SITES": "正在获取地图标注设施", "RETRYING ALPR CAMERAS": "正在重试 ALPR 摄像头", "FETCHING ALPR CAMERAS": "正在获取 ALPR 摄像头", "OVERPASS TEMPORARILY UNAVAILABLE": "Overpass 暂时不可用", @@ -1301,11 +2063,48 @@ "retry pending": "等待重试", "retrying in {seconds}s": "{seconds} 秒后重试", "ALPR cameras": "ALPR 摄像头", + "OpenSky OAuth client missing/invalid": "OpenSky OAuth 客户端缺失/无效", + "OpenSky OAuth rejected credentials": "OpenSky OAuth 拒绝了凭据", + "OpenSky username/password rejected": "OpenSky 用户名/密码被拒绝", + "OpenSky auth missing": "OpenSky 缺少认证", + "OpenSky auth required": "OpenSky 需要认证", + "OpenSky auth invalid": "OpenSky 认证无效", + "OpenSky auth failed": "OpenSky 认证失败", + "KartaView answered HTTP {status}": "KartaView 返回 HTTP {status}", + "Panoramax answered HTTP {status}": "Panoramax 返回 HTTP {status}", + "KartaView refused the request (code {code})": "KartaView 拒绝了请求(代码 {code})", + "Vector tiles unavailable (HTTP {status})": "矢量瓦片不可用(HTTP {status})", + "GNSS feed unavailable (HTTP {status})": "GNSS 数据源不可用(HTTP {status})", + "{count} CelesTrak groups unavailable": { + "other": "{count} 个 CelesTrak 分组不可用" + }, + "{feed} feed unavailable": "{feed} 数据源不可用", + "Source snapshot {minutes} min old": "数据源快照:{minutes} 分钟前", + "{source} rate limited (anonymous)": "{source} 已限流(匿名)", + "{source} rate limited": "{source} 已限流", + "{source} network error": "{source} 网络错误", + "Malformed {source} aircraft rows": "{source} 飞机数据行格式错误", + "Malformed {source} response": "{source} 响应格式错误", + "entry {n}": "条目 {n}", + "feeds {names} stale": { + "other": "数据源 {names} 过期" + }, + "feeds {names} unreachable": { + "other": "数据源 {names} 无法连接" + }, + "feeds {names} invalid": { + "other": "数据源 {names} 无效" + }, "Loading {feeds}": "正在加载 {feeds}", "{count} heard": { "other": "已收到 {count} 架" }, + "USB {rate} msg/s": "USB {rate} msg/s", "{rate} msg/s": "{rate} msg/s", + "{count} feeds live": { + "other": "实时数据源 {count} 个" + }, + "{feed} {count} ({seconds}s old)": "{feed} {count}({seconds} 秒前)", "Street Level could not start": "街景未能启动", "Street Level toggle failed": "街景切换失败", "Street-level image": "街景图像", @@ -1332,15 +2131,38 @@ "{name} imagery off": "{name} 影像已关闭", "Image by {creator}": "图像作者:{creator}", "Camera follows view: move the globe camera wherever the street-level view looks": "相机跟随视野:街景看向哪里,地球相机就移到哪里", - "Camera follow needs the Google 3D map: choose Google 3D under MAP SOURCE": "相机跟随需要 Google 3D 地图:请在地图源(MAP SOURCE)中选择 Google 3D", + "Camera follow needs the Google 3D map: choose Google 3D under MAP SOURCE": "相机跟随需要 Google 3D 地图:请在“地图源”中选择 Google 3D", + " or ": " 或 ", + "OPEN {labels}": "打开 {labels}", + "Open {names} at the centre of the view": "在视野中心打开 {names}", + "Zoom in to a street to open {names} here": "放大到街道以在此处打开 {names}", "Switch a provider on to draw its coverage.": "开启一个提供商以绘制其覆盖范围。", "Loading this sequence…": "正在加载此序列…", "{n} images in this sequence · Esc clears": { "other": "此序列共 {n} 张图像 · Esc 清除" }, + "{n} streets in view · click a line for its nearest photo": { + "other": "视野内共 {n} 条街道 · 点击线段查看最近的照片" + }, + "{n} sequences in view · click a line for its nearest photo": { + "other": "视野内共 {n} 条序列 · 点击线段查看最近的照片" + }, "{n} sequences in view · click a line for its photos": { "other": "视野内共 {n} 条序列 · 点击线段查看其照片" }, + "km/h": "km/h", + "m/s": "m/s", + "mph": "mph", + "hPa": "hPa", + "kt": "kn", + "kts": "kn", + "ft": "ft", + "km": "km", + "m": "m", + "h": "小时", + "min": "分钟", + "s": "秒", + "dB": "dB", "Celestial ring — reveal the full globe": "天球环——显示完整地球", "Celestial ring — available in Normal style": "天球环——仅在常规样式下可用", "Detection overlay: {mode}": "探测叠加层:{mode}", @@ -1350,9 +2172,16 @@ "Active weather": "活跃天气", "LATEST · newest per product": "最新 · 每个产品的最新一帧", "Forecast · valid {valid} · issued {issued}": "预报 · 生效 {valid} · 发布 {issued}", + "{detail} · Does not follow history": "{detail} · 不随观测历史变化", "Forecast · valid {valid}": "预报 · 生效 {valid}", + "voice|OFF": "关", + "voice|CONNECTING": "连接中", + "voice|LISTENING": "聆听中", + "voice|EXECUTING": "执行中", + "voice|ERROR": "错误", "AI AGENT": "AI 代理", "Voice model tier — applies next session": "语音模型档位——下次会话生效", + "STD": "标准", "Estimated session cost": "预估会话费用", "Voice control — activate to toggle voice; hold Space to speak": "语音控制——激活可切换语音;按住空格键说话", "ON/OFF": "开/关", @@ -1363,13 +2192,14 @@ "VOICE": "语音", "Dismiss voice card": "关闭语音卡片", "YOU": "用户", - "THIS": "此目标", + "THIS": "目标", "Plan": "计划", "Referenced items": "引用条目", "NOTES & SOURCES": "笔记与来源", "VOICE SYSTEM ERROR": "语音系统错误", "DISMISS": "关闭", "Check microphone permission and network access, then try again.": "请检查麦克风权限和网络连接,然后重试。", + "Stopped by voice": "已通过语音停止", "WebRTC microphone support unavailable": "不支持 WebRTC 麦克风", "Requesting microphone": "正在请求麦克风", "Release Space to send": "松开空格键发送", @@ -1379,32 +2209,99 @@ "VOICE UNAVAILABLE": "语音不可用", "VOICE ACTIVE": "语音已启用", "Voice session could not be started.": "无法启动语音会话。", + "MINI": "迷你", "Next session: {pendingId} — this session stays on {modelId}": "下次会话:{pendingId}——本次会话仍使用 {modelId}", "Voice model: {pendingId} — click to switch to {target}; applies next session": "语音模型:{pendingId}——点击切换到 {target};下次会话生效", + "standard": "标准", + "mini": "迷你", "COST UNKNOWN": "费用未知", "ChatGPT OAuth session: {responses} response(s), {input} input and {output} output tokens, {captions} caption transcription(s) reported.": "ChatGPT OAuth 会话:已报告 {responses} 个响应、{input} 个输入 token、{output} 个输出 token、{captions} 次字幕转写。", "USD cost and the API spend cap are unavailable for this auth mode.": "此认证模式下无法提供美元费用与 API 消费上限。", "Usage is incomplete because a response was still in flight when the session ended.": "用量不完整,因为会话结束时仍有响应在处理中。", "Estimated session cost on {modelId} — {responses} response(s). Warns at {warn}, ends the session at {cap}.": "{modelId} 的预估会话费用——{responses} 次响应。达到 {warn} 时警告,达到 {cap} 时结束会话。", - "Estimate is incomplete — a response was still in flight when the session ended, so its usage was never reported.": "估算不完整 — 会话结束时仍有响应在处理中,因此其用量从未上报。", + "Estimate is incomplete — a response was still in flight when the session ended, so its usage was never reported.": "估算不完整——会话结束时仍有响应在处理中,因此其用量从未上报。", + "STANDARD": "标准", "{tier} applies next session": "{tier} 将在下次会话生效", "Session ended — cost cap {cost}": "会话已结束——已达费用上限 {cost}", + "MIC": "麦克风", "Radio did not start": "电台未能启动", "Running command": "正在执行指令", + "{n} aircraft": { + "other": "{n} 架飞机" + }, + "{n} military aircraft": { + "other": "{n} 架军用飞机" + }, + "{n} ships": { + "other": "{n} 艘船舶" + }, + "{n} satellites": { + "other": "{n} 颗卫星" + }, + "{n} launches": { + "other": "{n} 次发射" + }, + "{n} earthquakes": { + "other": "{n} 次地震" + }, "{n} fires": { "other": "{n} 处火点" }, + "{n} fire perimeters": { + "other": "{n} 处火场周界" + }, + "{n} cyclones": { + "other": "{n} 个气旋" + }, + "{n} buses": { + "other": "{n} 辆公交车" + }, + "{n} bike share": { + "other": "{n} 个共享单车站点" + }, + "{n} flock cameras": { + "other": "{n} 个 Flock 摄像头" + }, + "{n} military bases": { + "other": "{n} 处军事基地" + }, + "{n} datacenters": { + "other": "{n} 个数据中心" + }, + "{n} dams": { + "other": "{n} 座水坝" + }, + "{n} air quality": { + "other": "{n} 个空气质量站点" + }, + "{n} results": { + "other": "{n} 条结果" + }, "anywhere in the loaded data": "已加载数据中的任意位置", "in view": "视野内", + "within {radius} of the view centre": "视野中心 {radius} 范围内", + "within {radius} of {place} (Contacts window)": "{place} {radius} 范围内(目标窗口)", + "within {radius} of {place}": "{place} {radius} 范围内", + "within {radius}": "{radius} 范围内", "over {region}": "{region} 范围内", "At least {items}": "至少 {items}", "{items} {scope}": "{items} {scope}", + "last 24 h": "过去 24 小时", + "past 30 d and upcoming": "过去 30 天及未来", + "the flights layer loads by viewport": "航班图层按视口加载", + "distance is ground distance, not overhead": "距离为地面距离,而非空中直线距离", + "the count is a lower bound because at least one layer reached its retained limit.": "该计数为下限,因为至少一个图层已达到其保留上限。", "{label} off": "{label} 已关闭", + "{label} loading": "{label} 加载中", + "{label} error": "{label} 出错", "counted the first {returned} loaded records": "已统计最先加载的 {returned} 条记录", "counted {returned} of {total} loaded records": "已统计已加载 {total} 条记录中的 {returned} 条", - "Fly below {km} km to a covered region": "请飞行至 {km} 公里以下的覆盖区域", + "{layers} just turned on — still loading, counts will rise": "{layers} 刚开启——仍在加载,计数还会增加", + "Contacts panel snapshot at {time} — the same retained cohort supplies the card, speech, and follow-up memory": "目标面板快照({time})——卡片、语音播报和后续追问记忆均来自同一组保留目标", + "Contacts panel snapshot — the same retained cohort supplies the card, speech, and follow-up memory": "目标面板快照——卡片、语音播报和后续追问记忆均来自同一组保留目标", + "Fly below {km} km to a covered region": "请降至 {km} km 以下并飞往有覆盖的区域", "No feed here yet · {n} regions available": { - "other": "此区域暂无线路 · 有 {n} 个覆盖区域" + "other": "此处暂无数据源 · 有 {n} 个覆盖区域" }, "degraded": "降级", "stale": "过期", @@ -1416,21 +2313,72 @@ "Activate to toggle voice": "点击切换语音", "Voice active": "语音已启用", "Voice could not be started.": "无法启动语音。", + "the active voice session": "当前语音会话", "{value} feet": "{value} 英尺", + "The flights layer is not enabled": "航班图层未开启", + "The military layer is not enabled": "军用航班图层未开启", + "The satellites layer is not enabled": "卫星图层未开启", + "The ais-live-vessels layer is not enabled": "ais-live-vessels 图层未开启", + "Could not enable the requested layer": "无法开启所请求的图层", + "Could not disable the requested layer": "无法关闭所请求的图层", + "Radio layer unavailable": "电台图层不可用", + "Camera navigation is unavailable in the current view": "当前视图下无法进行相机导航", + "Camera navigation policy unavailable": "相机导航策略不可用", + "Nearest-aircraft selection supports Flights or Military Flights only": "最近飞机选择仅支持航班或军用航班", + "Nearest-aircraft selection needs a preset, place name, or latitude and longitude": "最近飞机选择需要预设、地名或经纬度", + "Could not arrive at the requested place": "无法抵达所请求的地点", + "The nearest airborne aircraft could not be selected": "无法选中最近的空中飞机", + "No flights within {radius} of the current view": "当前视野 {radius} 范围内没有航班", + "No military within {radius} of the current view": "当前视野 {radius} 范围内没有军用航班", + "No satellites within {radius} of the current view": "当前视野 {radius} 范围内没有卫星", + "No vessels within {radius} of the current view": "当前视野 {radius} 范围内没有船舶", + "{layer} is enabled on the {source} feed, but no airborne aircraft is loaded in the {place} view yet": "{layer} 已在 {source} 数据源上开启,但 {place} 视野中尚未加载空中飞机", + "{layer} is enabled, but no airborne aircraft is loaded in the {place} view yet": "{layer} 已开启,但 {place} 视野中尚未加载空中飞机", + "{layer} is enabled, but its aircraft feed is unavailable": "{layer} 已开启,但其飞机数据源不可用", + "{layer} is enabled, but its destination refresh did not complete": "{layer} 已开启,但目的地刷新未完成", + "{layer} is enabled, but {source} is unavailable": "{layer} 已开启,但 {source} 不可用", + "Could not arrive at {place}": "无法抵达 {place}", + "{name} is no longer loaded": "{name} 已不在加载数据中", + "Unknown target layer: {target}": "未知目标图层:{target}", "nominal": "正常", "{label} on": "{label} 已开启", + "Frame aircraft": "取景飞机", + "Frame military aircraft": "取景军用飞机", + "Frame ships": "取景船舶", + "Frame satellites": "取景卫星", + "Frame contacts": "取景目标", + "{count} aircraft in frame": { + "other": "画面中 {count} 架飞机" + }, + "{count} military aircraft in frame": { + "other": "画面中 {count} 架军用飞机" + }, + "{count} ships in frame": { + "other": "画面中 {count} 艘船舶" + }, + "{count} satellites in frame": { + "other": "画面中 {count} 颗卫星" + }, + "{count} contacts in frame": { + "other": "画面中 {count} 个目标" + }, + "Nearest military aircraft": "最近的军用飞机", + "Nearest ships": "最近的船舶", + "Nearest satellites": "最近的卫星", + "Nearest aircraft": "最近的飞机", + "{first} and {second}": "{first}和{second}", "Layer": "图层", "there": "那里", - "Camera range {range}": "相机距离{range}", - "Within {radius} of the view": "距当前视野{radius}内", + "Camera range {range}": "相机距离 {range}", + "Within {radius} of the view": "距当前视野 {radius} 内", "Labels on": "标签已开启", "Framing uses the nearest 80 loaded contacts": "取景仅使用已加载的最近 80 个目标", - "{distance} from {location}": "{distance} 自 {location}", - "Altitude {altitude}": "高度{altitude}", + "{distance} from {location}": "距 {location} {distance}", + "Altitude {altitude}": "高度 {altitude}", "{origin} to {destination}": "{origin} 至 {destination}", - "from {origin}": "从{origin}出发", - "to {destination}": "前往{destination}", - "Registration {registration}": "注册号{registration}", + "from {origin}": "从 {origin} 出发", + "to {destination}": "前往 {destination}", + "Registration {registration}": "注册号 {registration}", "View state": "视野状态", "Marked {count} places": { "other": "已标记 {count} 个地点" @@ -1438,10 +2386,42 @@ "Nothing marked": "没有标记", "Not found: {places}": "未找到:{places}", "Tracing outlines": "正在描画边界", - "Frame {target}": "取景{target}", + "Frame flights": "取景航班", + "Frame military": "取景军用航班", + "Frame vessels": "取景船舶", + "Frame {target}": "取景 {target}", + "Zoom out to globe": "缩小至地球视图", + "Open data layers": "打开数据图层", + "Open or close a panel": "打开或关闭面板", + "Change context mode": "切换态势模式", + "Control cockpit": "控制座舱", + "Change visual style": "切换视觉样式", + "Adjust HUD": "调整 HUD", + "Adjust cyber sonar": "调整赛博声呐", + "Adjust detection overlay": "调整探测叠加层", + "Change map stack": "切换地图组合", + "Adjust post-processing": "调整后期处理", + "Control scene": "控制场景", + "Control CCTV": "控制 CCTV", + "Control radio": "控制电台", + "Track contact": "跟踪目标", + "Stop tracking": "停止跟踪", + "Clear marks": "清除标注", + "Move camera": "移动相机", + "Fly route": "沿路线飞行", + "Analyst query": "分析查询", + "Next ISS pass": "下一次 ISS 过境", + "Next satellite pass": "下一次卫星过境", "Mark the map": "在地图上标注", - "Fly to {place}": "飞往{place}", - "Nearest aircraft to {place}": "距离{place}最近的飞机", + "Mark {targets} +{more}": "标注 {targets} +{more}", + "Mark {targets}": "标注 {targets}", + "Fly to {place}": "飞往 {place}", + "Fly to Location": "飞往地点", + "Nearest aircraft to {place}": "距离 {place} 最近的飞机", + "Nearest aircraft to Here": "距离此处最近的飞机", + "Layer on": "图层已开启", + "Layer off": "图层已关闭", + "Zoom out": "缩小", "Zoom in": "放大", "Read the view": "读取视野", "Check view state": "检查视野状态", @@ -1465,13 +2445,13 @@ "DONE": "完成", "HERE": "此处", "Aircraft cockpit view": "飞机座舱视图", - "LEVEL": "等级", + "LEVEL": "水平", "Estimated destination direction": "预估目的地方向", "DEST ---°": "目的地 ---°", "OPTICAL PLANE · 01": "光学平面 · 01", "VISOR LOCK · ACTIVE": "面罩锁定 · 激活", - "GROUND SPEED · KTS": "地速 · KTS", - "ALTITUDE · FT": "高度 · FT", + "GROUND SPEED · KTS": "地速 · kn", + "ALTITUDE · FT": "高度 · ft", "FIRST PERSON": "第一人称", "LIVE TRACK · COURSE ALIGNED": "实时航迹 · 航向对准", "Cockpit vision style": "座舱视觉样式", @@ -1484,15 +2464,21 @@ "Next vision style": "下一个视觉样式", "Current aircraft heading": "飞机当前航向", "GROUND SPEED": "地速", + "KTS": "kn", "ALTITUDE": "高度", + "FT": "ft", "Contact cockpit summary": "目标座舱摘要", "CONTACT": "目标", - "CONTACTS · 250 KM": "目标 · 250 KM", + "CONTACTS · 250 KM": "目标 · 250 km", "Contact navigation": "目标导航", "CONTEXT ONLY": "仅供参考", "Nearby cohort counts": "附近各类目标数量", + "FLT": "航班", + "MIL": "军用", + "SITE": "设施", "NEAREST OBSERVED / MAPPED": "最近的观测 / 地图标注目标", "AVAILABLE INPUTS ONLY · NOT AN ALL-CLEAR": "仅限可用输入 · 不代表安全无虞", + "WX": "天气", "Cockpit briefing carousel": "座舱简报轮播", "Estimated flight plan": "预估飞行计划", "ESTIMATED FLIGHT PLAN": "预估飞行计划", @@ -1508,8 +2494,9 @@ "WIND": "风", "SKY": "天空", "PRECIP": "降水", + "MM": "mm", "Weather data by Open-Meteo.com": "天气数据由 Open-Meteo.com 提供", - "Cockpit briefing pages": "驾驶舱简报页面", + "Cockpit briefing pages": "座舱简报页面", "SIG": "信号", "Show Live Signals": "显示实时信号", "NEWS": "新闻", @@ -1542,7 +2529,7 @@ "Simulate night-vision goggles with green intensification and a tube vignette.": "模拟夜视仪,带绿色增强和镜筒暗角。", "Simulate FLIR-style thermal contrast. Turn up Ironbow for color.": "模拟 FLIR 风格的热成像对比。调高 Ironbow 可显示色彩。", "Apply bright cel-shaded color and illustrated outlines.": "应用明亮的赛璐珞色彩和插画轮廓线。", - "Apply high-contrast monochrome film-noir grading.": "应用高对比度的黑白黑色电影调色。", + "Apply high-contrast monochrome film-noir grading.": "应用高对比度的黑白电影风格调色。", "Add a cold, snowy whiteout treatment to the scene.": "为场景添加寒冷的雪天白茫茫效果。", "MAP SOURCE": "地图源", "Map source": "地图源", @@ -1557,24 +2544,24 @@ "WEATHER": "天气", "Active weather products": "活跃天气产品", "RECENT IMAGERY": "近期影像", - "Imagery days in the box": "影像盒内的天数", - "CONTEXT": "上下文", + "Imagery days in the box": "框选内的影像日期", + "CONTEXT": "态势", "Compact Radio controls": "精简电台控件", "Open detailed Radio controls": "打开详细电台控件", "Previous station": "上一个电台", "Next station": "下一个电台", "Compact Radio volume": "精简电台音量", "Expand panel": "展开面板", - "Context mode": "上下文模式", + "Context mode": "态势模式", "CONTACTS": "目标", "Cycles the nearest contacts of whatever type you select — planes, vessels, installations. Satellites track independently.": "循环切换你所选类型中最近的目标,包括飞机、船舶、设施。卫星独立跟踪。", "SPACE MISSIONS": "太空任务", - "SELECT CONTEXT": "选择上下文", - "Contact Context actions": "目标上下文操作", + "SELECT CONTEXT": "选择态势", + "Contact Context actions": "目标态势操作", "COCKPIT": "座舱", - "SEARCH NEARBY SITES": "搜索附近站点", + "SEARCH NEARBY SITES": "搜索附近设施", "Reclassify tracked contact as TR-3B": "将跟踪目标重新归类为 TR-3B", - "CONTACTS CONTEXT OFF": "目标上下文已关闭", + "CONTACTS CONTEXT OFF": "目标态势已关闭", "SELECT CONTACTS TO LOAD OBSERVED / MAPPED PROXIMITY": "选择目标以加载观测 / 地图标注的邻近信息", "Available Space Missions": "可用太空任务", "AVAILABLE MISSIONS": "可用任务", @@ -1610,7 +2597,7 @@ "GAIN": "增益", "RTL-SDR tuner gain for the current mode": "当前模式下的 RTL-SDR 调谐器增益", "AUTO": "自动", - "MSG/S": "消息/秒", + "MSG/S": "msg/s", "HEARD": "已收到", "POSITIONED": "已定位", "FM MHZ": "FM MHz", @@ -1628,7 +2615,7 @@ "Minimal": "精简", "Cyber": "赛博", "Sonar": "声呐", - "Cyber sonar contact scanner": "赛博声呐接触扫描器", + "Cyber sonar contact scanner": "赛博声呐目标扫描器", "Rings": "圈数", "Sonar ring count": "声呐圈数", "Range": "范围", @@ -1636,8 +2623,8 @@ "Power": "强度", "Sonar intensity": "声呐强度", "Opacity": "不透明度", - "Cyber contact and label opacity": "赛博接触与标注不透明度", - "Minimum opacity for map dots, icons, models, brackets, and labels between sonar passes": "声呐扫描期间地图点、图标、模型、括标与标注的最低不透明度", + "Cyber contact and label opacity": "赛博目标与标签不透明度", + "Minimum opacity for map dots, icons, models, brackets, and labels between sonar passes": "两次声呐扫描之间地图点、图标、模型、括标与标签的最低不透明度", "Sector": "扇区", "Sonar sector width": "声呐扇区宽度", "Detection Overlay (D)": "探测叠加层(D)", @@ -1663,23 +2650,26 @@ "Feather": "羽化", "Scope edge feather": "视域边缘羽化", "Scope edge feather as a percentage of the keyhole radius": "视域边缘羽化,以锁孔半径的百分比表示", - "Draw on the world — click vertices, double-click or Enter to finish, Esc to cancel": "在世界上绘制 — 单击顶点,双击或按 Enter 完成,按 Esc 取消", + "Draw on the world — click vertices, double-click or Enter to finish, Esc to cancel": "在地球上绘制——单击顶点,双击或按 Enter 完成,按 Esc 取消", "Shape": "形状", "Shape to draw": "要绘制的形状", "Area": "面", "Line": "线", - "Pin": "标记", + "Pin": "图钉", "Label (optional)": "标签(可选)", "Label for the drawn shape": "绘制形状的标签", "Colour of the drawn shape": "绘制形状的颜色", - "Primary": "主色", - "Amber": "琥珀", + "Primary": "主题色", + "Amber": "琥珀色", "Cyan": "青色", "Green": "绿色", "Red": "红色", - "Clear": "晴", + "Clear": "清除", "Remove every mark from the board": "清除图板上的所有标记", + "Show your own GeoJSON, KML, KMZ or GPX files on the globe (or drop them onto it). File bytes stay on this device; overlays can appear in map screenshots shared with voice.": "在地球上显示自己的 GeoJSON、KML、KMZ 或 GPX 文件(也可直接拖放到地球上)。文件内容仅保留在本设备上;叠加层可能出现在共享给语音的地图截图中。", "Import": "导入", + "Choose GeoJSON, KML, KMZ or GPX files": "选择 GeoJSON、KML、KMZ 或 GPX 文件", + "Imported files": "已导入的文件", "Celestial": "天球", "Hide UI chrome": "隐藏界面控件", "Clean UI": "纯净界面", @@ -1692,6 +2682,7 @@ "Return UI controls": "恢复界面控件", "Initializing photorealistic world...": "正在初始化逼真世界…", "DATA LAYERS": "数据图层", + "ACQUIRING FRAME": "正在获取画面", "NEAREST": "最近", "CCTV camera": "CCTV 摄像头", "CALIBRATION": "校准", @@ -1708,6 +2699,7 @@ "RESET CAL": "重置校准", "SCENE SUMMARY": "场景摘要", "STREET LEVEL": "街景", + "Turn Street Level on or off": "开启或关闭街景", "Expand the street-level view (Esc shrinks it)": "放大街景视图(Esc 缩小)", "Image fit": "图像适配", "FIT": "适应", @@ -1727,7 +2719,7 @@ "Panorama filter": "全景筛选", "ALL": "全部", "FLAT": "平面", - "SINCE": "时间范围", + "SINCE": "时间", "Coverage colours": "覆盖图例", "SCENES": "场景", "Scene recipe": "场景方案", @@ -1738,10 +2730,10 @@ "EXPORT PRESETS": "导出预设", "IMPORT": "导入", "RUN LOG": "运行日志", - "POWER UP": "启动", + "POWER UP": "增强", "GROUND STATION · PROVIDER SETTINGS": "地面站 · 服务商设置", "Close key setup": "关闭密钥设置", - "Power up the globe": "启动地球", + "Power up the globe": "增强地球", "The globe already flies keyless. Every key below switches on another real feed — paste one and it's saved into this app's local configuration, then the server restarts itself. Server-side keys stay on this machine; Google Maps and Cesium ion run in the browser and must be provider-restricted. Keys you configured elsewhere are shown but never touched.": "地球无需密钥即可运行。下面的每个密钥都会启用一个新的真实数据源:粘贴后会保存到本应用的本地配置,随后服务器自动重启。服务端密钥只保存在本机;Google Maps 和 Cesium ion 在浏览器中运行,必须设置服务商限制。你在其他位置配置的密钥会显示,但不会被改动。", "SAVE KEYS": "保存密钥", "ESC to close": "按 ESC 关闭", @@ -1764,5 +2756,9 @@ "Begin with a clean globe": "从干净的地球开始", "Don't show this again": "不再显示", "ESC to dismiss": "按 ESC 关闭", - "Tip: the GEV MIC button in the dock lets you talk to the map.": "提示:停靠栏中的 GEV MIC 按钮可让你用语音操控地图。" + "Tip: the GEV MIC button in the dock lets you talk to the map.": "提示:停靠栏中的 GEV 麦克风按钮可让你用语音操控地图。", + "hemisphere|N": "N", + "hemisphere|S": "S", + "hemisphere|E": "E", + "hemisphere|W": "W" } diff --git a/src/i18n/same-as-english.json b/src/i18n/same-as-english.json index c159beaab..821b154b9 100644 --- a/src/i18n/same-as-english.json +++ b/src/i18n/same-as-english.json @@ -98,6 +98,9 @@ "fire confidence|nominal", "ft", "hPa", + "hemisphere|E", + "hemisphere|N", + "hemisphere|S", "installation class|BASE", "km", "km/h", @@ -133,5 +136,60 @@ "REC", "VS", "WHOT/BHOT" + ], + "zh-CN": [ + "1090 MHz", + "978 MHz UAT", + "AIS: --", + "AIS: {name}", + "BRG {value}°", + "BRG —", + "CAL {value}", + "CAL · {badge}", + "CCTV", + "CRS {value}°", + "CRT", + "FLIR", + "FOV", + "FOV {value}°", + "FT", + "GEO", + "HDG", + "HDG {value}°", + "House", + "Ironbow", + "KTS", + "Landsat 8/9", + "MM", + "NVG", + "REC", + "SRC {value}", + "Sentinel-2", + "Techno", + "Trance", + "USB {rate} msg/s", + "WHOT/BHOT", + "dB", + "ft", + "hPa", + "hemisphere|E", + "hemisphere|N", + "hemisphere|S", + "hemisphere|W", + "km", + "km/h", + "kt", + "kts", + "mph", + "{city} CCTV", + "{rate} msg/s", + "{source} HTTP {status}", + "{speed} kn", + "{type} SPD: {speed} HDG: {heading}", + "{value} KM", + "{value} KM/S", + "ΔE", + "ΔN", + "▲ {frp} MW" ] } diff --git a/src/ui/localePicker.test.mjs b/src/ui/localePicker.test.mjs index 35b473e42..7f6d11f9b 100644 --- a/src/ui/localePicker.test.mjs +++ b/src/ui/localePicker.test.mjs @@ -40,7 +40,7 @@ test('the current language comes first, the rest by their own names', () => { nativeNames: LOCALE_NAMES, }), ), - ['ru', 'en', 'es'], + ['ru', 'en', 'es', 'zh-CN'], ); assert.deepEqual( codes( @@ -50,7 +50,7 @@ test('the current language comes first, the rest by their own names', () => { nativeNames: LOCALE_NAMES, }), ), - ['es', 'en', 'ru'], + ['es', 'en', 'ru', 'zh-CN'], ); assert.deepEqual( codes( @@ -60,7 +60,7 @@ test('the current language comes first, the rest by their own names', () => { nativeNames: LOCALE_NAMES, }), ), - ['en', 'es', 'ru'], + ['en', 'es', 'ru', 'zh-CN'], ); const rows = buildLocaleOptions({ locales: MANY, @@ -263,7 +263,7 @@ test('keyboard model: open on the current language, move, choose, close', () => assert.deepEqual(model.keydown({ key: 'ArrowDown' }), { handled: true }); assert.equal(model.activeOption.code, 'en'); model.keydown({ key: 'End' }); - assert.equal(model.activeOption.code, 'es'); + assert.equal(model.activeOption.code, 'zh-CN'); model.keydown({ key: 'Home' }); assert.equal(model.activeOption.code, 'ru'); model.keydown({ key: 'ArrowUp' }); diff --git a/src/ui/styles/cjk.css b/src/ui/styles/cjk.css new file mode 100644 index 000000000..c44431520 --- /dev/null +++ b/src/ui/styles/cjk.css @@ -0,0 +1,29 @@ +/* Chinese text. The UI sets Latin capitals with wide tracking; Chinese set + that way falls apart into single glyphs, so its tracking stays tight. The + intel HUD readouts (codes, coordinates) and text kept in English on purpose + (translate="no", and whatever holds it) keep the Latin look. */ +html:lang(zh) + body + :not( + #intel-hud, + #intel-hud *, + [translate='no'], + [translate='no'] *, + :has([translate='no']) + ) { + letter-spacing: 0.02em !important; +} + +/* Below about 9px a Chinese glyph blurs: lift the dock's voice labels and the + panel layer buttons to that floor. */ +html:lang(zh) #command-dock .gev-mic-label, +html:lang(zh) #command-dock #gev-voice-detail, +html:lang(zh) .gev-voice-tier-btn, +html:lang(zh) .panel-layer-toggle { + font-size: 9px; +} + +/* A two-glyph button label must not break between its glyphs. */ +html:lang(zh) .pp-mode-btn { + white-space: nowrap; +} diff --git a/style.css b/style.css index 44b7e07bc..8c4599e89 100644 --- a/style.css +++ b/style.css @@ -27,4 +27,5 @@ @import './src/ui/styles/weather.css'; @import './src/ui/styles/panel-surfaces.css'; +@import './src/ui/styles/cjk.css'; @import './src/ui/styles/cyber.css';