From 398ebe35bd387b5982c5cddf7618c599b373da3e Mon Sep 17 00:00:00 2001
From: LeonardSEO <130681849+LeonardSEO@users.noreply.github.com>
Date: Wed, 21 Oct 2026 16:30:55 +0200
Subject: [PATCH] Show the 5/MG SpO2 strap reading by default and label it
"strap reading"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The strap computes the value itself (LeonardSEO/wearable5.0 FINDINGS §13). Still never written to spo2Pct and never scored; an explicit off still wins.
Refs #103
---
Strand/BLE/PuffinExperiment.swift | 6 +++++-
Strand/Liquid/LiquidTodayView.swift | 4 ++--
Strand/Resources/Localizable.xcstrings | 4 ++--
Strand/Screens/SettingsView.swift | 2 +-
Strand/Screens/TodayView.swift | 4 ++--
Strand/Screens/VitalSignsSummary.swift | 4 ++--
StrandTests/VitalReadingsTableTests.swift | 4 ++--
StrandTests/VitalSourceResolutionTests.swift | 8 ++++----
android/app/src/main/java/com/noop/ui/MainActivity.kt | 6 +++---
android/app/src/main/res/values-de/strings.xml | 4 ++--
android/app/src/main/res/values-es/strings.xml | 4 ++--
android/app/src/main/res/values-fr/strings.xml | 4 ++--
android/app/src/main/res/values-it/strings.xml | 4 ++--
android/app/src/main/res/values-pl/strings.xml | 4 ++--
android/app/src/main/res/values-pt-rPT/strings.xml | 4 ++--
android/app/src/main/res/values-ru/strings.xml | 4 ++--
android/app/src/main/res/values-zh/strings.xml | 4 ++--
android/app/src/main/res/values/strings.xml | 4 ++--
18 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/Strand/BLE/PuffinExperiment.swift b/Strand/BLE/PuffinExperiment.swift
index 7b3a2e13d2..5637979e33 100644
--- a/Strand/BLE/PuffinExperiment.swift
+++ b/Strand/BLE/PuffinExperiment.swift
@@ -73,7 +73,11 @@ enum PuffinExperiment {
/// ON. Mirrors the Android `NoopPrefs.KEY_SPO2_CANDIDATE_DISPLAY`.
static let spo2CandidateDisplayKey = "noopSpo2CandidateDisplay"
- static var spo2CandidateDisplayEnabled: Bool { UserDefaults.standard.bool(forKey: spo2CandidateDisplayKey) }
+ /// Default ON in this fork: the strap computes this value itself (wearable5.0 FINDINGS §13), and the
+ /// nightly figure is now the windowed median rather than a raw mean. An explicit off still wins.
+ static var spo2CandidateDisplayEnabled: Bool {
+ UserDefaults.standard.object(forKey: spo2CandidateDisplayKey) as? Bool ?? true
+ }
/// Opt-in "Personal daytime-stress baseline" (#463): score TODAY's intraday stress timeline against a
/// PERSONAL cross-day rolling baseline (Oura-style `.baselineRelative`) instead of the day's own calm
diff --git a/Strand/Liquid/LiquidTodayView.swift b/Strand/Liquid/LiquidTodayView.swift
index 7179ee9ba7..d54b54b9d0 100644
--- a/Strand/Liquid/LiquidTodayView.swift
+++ b/Strand/Liquid/LiquidTodayView.swift
@@ -1053,7 +1053,7 @@ struct LiquidTodayView: View {
// is the DEFAULT Today screen on iOS 26. The subtitle is the slot this card has.
cardLink(.metric("spo2"),
title: card.title,
- sub: spo2Candidate != nil ? String(localized: "strap estimate (unverified)") : card.subtitle,
+ sub: spo2Candidate != nil ? String(localized: "strap reading") : card.subtitle,
// Em dash, not the en dash the stub used: the classic Blood Oxygen card and
// skinTempCardValue both return "—", so the stub's "–" would have left the two
// adjacent cards printing different glyphs for the same "no reading" state.
@@ -1420,7 +1420,7 @@ struct LiquidTodayView: View {
: nil
let spo2 = spo2Real ?? spo2CandidateValue
ktile(String(localized: "Blood Oxygen"), icon: keyMetricIcon(metric), intText(spo2), "%", StrandPalette.metricCyan, fracOver(spo2, 100), key: spo2CandidateValue != nil ? "spo2_candidate" : "spo2",
- caption: spo2CandidateValue != nil ? String(localized: "strap estimate (unverified)") : nil)
+ caption: spo2CandidateValue != nil ? String(localized: "strap reading") : nil)
case .respiratory:
let resp = displayDay?.respRateBpm ?? vitalsDay?.respRateBpm ?? respDay?.respRateBpm
ktile(String(localized: "Respiratory"), icon: keyMetricIcon(metric), resp.map { String(format: "%.1f", locale: AppLanguage.activeLocale, $0) } ?? "—", "rpm", StrandPalette.accent, fracOver(resp, 24), key: "resp_rate")
diff --git a/Strand/Resources/Localizable.xcstrings b/Strand/Resources/Localizable.xcstrings
index 7f227851a7..ca08b7fb5d 100644
--- a/Strand/Resources/Localizable.xcstrings
+++ b/Strand/Resources/Localizable.xcstrings
@@ -93,8 +93,8 @@
"no data yet": { "localizations": {
"de": {"stringUnit": {"state": "translated", "value": "noch keine Daten"}}, "es": {"stringUnit": {"state": "translated", "value": "aún sin datos"}}, "fr": {"stringUnit": {"state": "translated", "value": "pas encore de données"}}, "it": {"stringUnit": {"state": "translated", "value": "ancora nessun dato"}}, "pl": {"stringUnit": {"state": "translated", "value": "brak danych"}}, "pt-PT": {"stringUnit": {"state": "translated", "value": "ainda sem dados"}}, "ru": {"stringUnit": {"state": "translated", "value": "данных пока нет"}}, "zh-Hans": {"stringUnit": {"state": "translated", "value": "暂无数据"}}, "zh-Hant": {"stringUnit": {"state": "translated", "value": "暫無資料"}}
} },
- "strap estimate (unverified)": { "localizations": {
- "de": {"stringUnit": {"state": "translated", "value": "Strap-Schätzung (nicht verifiziert)"}}, "es": {"stringUnit": {"state": "translated", "value": "estimación de la banda (sin verificar)"}}, "fr": {"stringUnit": {"state": "translated", "value": "estimation du bracelet (non vérifiée)"}}, "it": {"stringUnit": {"state": "translated", "value": "stima della fascia (non verificata)"}}, "pl": {"stringUnit": {"state": "translated", "value": "szacunek opaski (niezweryfikowany)"}}, "pt-PT": {"stringUnit": {"state": "translated", "value": "estimativa da pulseira (não verificada)"}}, "ru": {"stringUnit": {"state": "translated", "value": "оценка ремешка (не проверена)"}}, "zh-Hans": {"stringUnit": {"state": "translated", "value": "手环估算值(未验证)"}}, "zh-Hant": {"stringUnit": {"state": "translated", "value": "手環估算值(未驗證)"}}
+ "strap reading": { "localizations": {
+ "de": {"stringUnit": {"state": "translated", "value": "Messwert des Armbands"}}, "es": {"stringUnit": {"state": "translated", "value": "lectura de la banda"}}, "fr": {"stringUnit": {"state": "translated", "value": "mesure du bracelet"}}, "it": {"stringUnit": {"state": "translated", "value": "lettura della fascia"}}, "pl": {"stringUnit": {"state": "translated", "value": "odczyt opaski"}}, "pt-PT": {"stringUnit": {"state": "translated", "value": "leitura da pulseira"}}, "ru": {"stringUnit": {"state": "translated", "value": "показание ремешка"}}, "zh-Hans": {"stringUnit": {"state": "translated", "value": "手环读数"}}, "zh-Hant": {"stringUnit": {"state": "translated", "value": "手環讀數"}}
} },
"toggle ON · no estimate yet": { "localizations": {
"de": {"stringUnit": {"state": "translated", "value": "Schalter AN · noch keine Schätzung"}}, "es": {"stringUnit": {"state": "translated", "value": "interruptor activado · aún sin estimación"}}, "fr": {"stringUnit": {"state": "translated", "value": "commutateur activé · pas encore d'estimation"}}, "it": {"stringUnit": {"state": "translated", "value": "interruttore attivo · nessuna stima"}}, "pl": {"stringUnit": {"state": "translated", "value": "przełącznik włączony · brak szacunku"}}, "pt-PT": {"stringUnit": {"state": "translated", "value": "interruptor ligado · ainda sem estimativa"}}, "ru": {"stringUnit": {"state": "translated", "value": "переключатель включён · оценки пока нет"}}, "zh-Hans": {"stringUnit": {"state": "translated", "value": "开关已开启 · 尚无估算值"}}, "zh-Hant": {"stringUnit": {"state": "translated", "value": "開關已開啟 · 尚無估算值"}}
diff --git a/Strand/Screens/SettingsView.swift b/Strand/Screens/SettingsView.swift
index 48e3a5e8df..8e64f28e30 100644
--- a/Strand/Screens/SettingsView.swift
+++ b/Strand/Screens/SettingsView.swift
@@ -54,7 +54,7 @@ struct SettingsView: View {
/// #103 opt-in: surfaces the WHOOP 5/MG `spo2_candidate_82` nightly mean in the Blood Oxygen tile
/// as a "strap estimate (unverified)" fallback when no calibrated `spo2Pct` exists. Display-only —
/// writes nothing to the strap. See [PuffinExperiment.spo2CandidateDisplayKey].
- @AppStorage(PuffinExperiment.spo2CandidateDisplayKey) private var spo2CandidateDisplayEnabled = false
+ @AppStorage(PuffinExperiment.spo2CandidateDisplayKey) private var spo2CandidateDisplayEnabled = true
@AppStorage(AppModel.ouraAllDayLiveHRKey) private var ouraAllDayLiveHREnabled = false // item 27
/// #1545 opt-in: score Effort with Banister's exponential TRIMP instead of Edwards' heart-rate zones.
diff --git a/Strand/Screens/TodayView.swift b/Strand/Screens/TodayView.swift
index 4935abfd9c..b36c80fc25 100644
--- a/Strand/Screens/TodayView.swift
+++ b/Strand/Screens/TodayView.swift
@@ -935,7 +935,7 @@ struct TodayView: View {
// other candidate-fallback surface uses — never a device name, which would misrepresent an
// unvalidated estimate as a calibrated reading in this table's Source column.
if rawSource == spo2CandidateAttributionSource {
- return String(localized: "strap estimate (unverified)")
+ return String(localized: "strap reading")
}
if rawSource.hasSuffix("-noop") { return String(localized: "On-device") }
if rawSource == deviceId || rawSource == Repository.whoopSource { return Self.whoopBrandName }
@@ -4043,7 +4043,7 @@ struct TodayView: View {
? String(format: "%.0f%%", locale: AppLanguage.activeLocale, candidateTail!)
: spo2.value
let spo2Caption: String = spo2.value == "—" && candidateTail != nil
- ? String(localized: "strap estimate (unverified)")
+ ? String(localized: "strap reading")
: (spo2.value == "—" && spo2CandidateOn
? String(localized: "toggle ON · no estimate yet")
: (spo2.caption ?? ""))
diff --git a/Strand/Screens/VitalSignsSummary.swift b/Strand/Screens/VitalSignsSummary.swift
index 1ab041c33e..f48fb8d213 100644
--- a/Strand/Screens/VitalSignsSummary.swift
+++ b/Strand/Screens/VitalSignsSummary.swift
@@ -203,7 +203,7 @@ enum BodyVitalSigns {
let respRow = latest(respPoints)
// #103/queue-11a: fall back to the spo2_candidate mean when no calibrated spo2Pct exists. The
- // candidate is labelled "strap estimate (unverified)" in the tile caption so it is never read as
+ // candidate is labelled "strap reading" in the tile caption so it is never read as
// a calibrated blood-oxygen percentage.
let spo2Row = latest(spo2Points) ?? latest(spo2CandidatePoints)
let spo2IsCandidate = spo2Row != nil && latest(spo2Points) == nil
@@ -345,7 +345,7 @@ enum BodyVitalSigns {
// where Android resolves the selected day — so the ROW differs across platforms by design
// and the parity contract is the relationship between the two tiles, not the row.
missingCaption: spo2IsCandidate
- ? String(localized: "strap estimate (unverified)")
+ ? String(localized: "strap reading")
: (PuffinExperiment.spo2CandidateDisplayEnabled && spo2Row == nil
? String(localized: "toggle ON · no estimate yet")
: (spo2rawRow != nil
diff --git a/StrandTests/VitalReadingsTableTests.swift b/StrandTests/VitalReadingsTableTests.swift
index cb74b3768b..66ffc8d191 100644
--- a/StrandTests/VitalReadingsTableTests.swift
+++ b/StrandTests/VitalReadingsTableTests.swift
@@ -67,7 +67,7 @@ final class VitalReadingsTableTests: XCTestCase {
}
/// #103/queue-11a follow-up: a spo2 candidate-fallback row (see `spo2CandidateAttributionSource`)
- /// must resolve to "strap estimate (unverified)" — the SAME caption every other candidate-fallback
+ /// must resolve to "strap reading" — the SAME caption every other candidate-fallback
/// surface uses — never a device name, which would misrepresent an unvalidated estimate as a
/// calibrated reading.
func testSpo2CandidateSourceReadsStrapEstimateUnverified() {
@@ -75,7 +75,7 @@ final class VitalReadingsTableTests: XCTestCase {
readings: [VitalReading(day: "2026-08-24", value: 97, source: spo2CandidateAttributionSource)],
unit: "%", strapDeviceId: strap, now: now, format: spo2Format
)
- XCTAssertEqual(rows.first?.source, "strap estimate (unverified)")
+ XCTAssertEqual(rows.first?.source, "strap reading")
}
func testValueReusesModelFormatAndUnit() {
diff --git a/StrandTests/VitalSourceResolutionTests.swift b/StrandTests/VitalSourceResolutionTests.swift
index 350eb2f62f..c7f1acee86 100644
--- a/StrandTests/VitalSourceResolutionTests.swift
+++ b/StrandTests/VitalSourceResolutionTests.swift
@@ -158,7 +158,7 @@ final class VitalSourceResolutionTests: XCTestCase {
// MARK: - #103 SpO₂ candidate @82 fallback
/// When the toggle is ON and no calibrated `spo2Pct` exists, the Blood O₂ tile falls back to the
- /// `spo2_candidate` mean from metricSeries, labelled "strap estimate (unverified)".
+ /// `spo2_candidate` value from metricSeries, labelled "strap reading".
func testSpo2CandidateFallsBackWhenNoCalibratedSpo2Pct() {
UserDefaults.standard.set(true, forKey: PuffinExperiment.spo2CandidateDisplayKey)
defer { UserDefaults.standard.set(false, forKey: PuffinExperiment.spo2CandidateDisplayKey) }
@@ -175,7 +175,7 @@ final class VitalSourceResolutionTests: XCTestCase {
let spo2 = readings.first { $0.key == "spo2" }
XCTAssertEqual(spo2?.value, 96.0)
XCTAssertEqual(spo2?.source, .noopComputed)
- XCTAssertTrue(spo2?.missingCaption.contains("strap estimate") == true)
+ XCTAssertTrue(spo2?.missingCaption.contains("strap reading") == true)
}
/// When the toggle is OFF, the candidate is never surfaced even if data exists.
@@ -214,8 +214,8 @@ final class VitalSourceResolutionTests: XCTestCase {
let spo2 = readings.first { $0.key == "spo2" }
XCTAssertEqual(spo2?.value, 98)
XCTAssertEqual(spo2?.source, .whoopImport)
- // The calibrated caption, NOT the "strap estimate" one.
- XCTAssertFalse(spo2?.missingCaption.contains("strap estimate") == true)
+ // The calibrated caption, NOT the "strap reading" one.
+ XCTAssertFalse(spo2?.missingCaption.contains("strap reading") == true)
}
// MARK: - Carry staleness
diff --git a/android/app/src/main/java/com/noop/ui/MainActivity.kt b/android/app/src/main/java/com/noop/ui/MainActivity.kt
index bf197a8ac6..e041de33c9 100644
--- a/android/app/src/main/java/com/noop/ui/MainActivity.kt
+++ b/android/app/src/main/java/com/noop/ui/MainActivity.kt
@@ -596,7 +596,7 @@ object NoopPrefs {
/** #103: whether the SpO₂ candidate @82 strap estimate is surfaced in the Blood Oxygen tile.
* Default false — the @82 candidate has split cross-device evidence and ships behind a toggle. */
fun spo2CandidateDisplay(context: Context): Boolean =
- of(context).getBoolean(KEY_SPO2_CANDIDATE_DISPLAY, false)
+ of(context).getBoolean(KEY_SPO2_CANDIDATE_DISPLAY, true)
/**
* [spo2CandidateDisplay] as a flow that re-emits when the user changes it.
@@ -624,7 +624,7 @@ object NoopPrefs {
// `key` is @Nullable on modern SDKs — it arrives null when the whole file is cleared, which
// reads as "everything changed". The null check is required to compile, not just defensive.
if (key == null || key == KEY_SPO2_CANDIDATE_DISPLAY) {
- trySend(changed.getBoolean(KEY_SPO2_CANDIDATE_DISPLAY, false))
+ trySend(changed.getBoolean(KEY_SPO2_CANDIDATE_DISPLAY, true))
}
}
prefs.registerOnSharedPreferenceChangeListener(listener)
@@ -633,7 +633,7 @@ object NoopPrefs {
// would serve a stale value indefinitely, which is the failure this whole function exists to
// remove. In this order the same interleaving costs at most a duplicate emit, and
// `distinctUntilChanged` drops it.
- trySend(prefs.getBoolean(KEY_SPO2_CANDIDATE_DISPLAY, false))
+ trySend(prefs.getBoolean(KEY_SPO2_CANDIDATE_DISPLAY, true))
awaitClose { prefs.unregisterOnSharedPreferenceChangeListener(listener) }
}.distinctUntilChanged()
diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml
index 8399bc377c..633fdf54c3 100644
--- a/android/app/src/main/res/values-de/strings.xml
+++ b/android/app/src/main/res/values-de/strings.xml
@@ -2036,7 +2036,7 @@
Klassisch
Linie
Balken
- Armband-Schätzung (unbestätigt)
+ Messwert des Armbands
Schalter EIN · noch keine Schätzung
Gerät vergessen…
Gerät vergessen
@@ -2626,7 +2626,7 @@
Eine Effort-Skala nach Wahl, ein Ring, der schlafen darf, und weit weniger vergebliche Neuberechnungen
Für dieses Vitalzeichen braucht NOOP mindestens zwei historische Messwerte, um es darzustellen.
Die Sauerstoffsättigungs-Schätzung ist deaktiviert
- Dein Strap liefert eine Schätzung der Sauerstoffsättigung, sie ist aber unbestätigt und standardmäßig aus. Aktiviere die Strap-Schätzung in den Einstellungen, oder importiere einen WHOOP-Export unter Datenquellen für den kalibrierten Wert.
+ Dein Strap liefert einen Messwert der Sauerstoffsättigung, aber die Anzeige ist ausgeschaltet. Schalte sie in den Einstellungen ein oder importiere einen WHOOP-Export unter Datenquellen.
Kein Sauerstoffsättigungswert von einem WHOOP 4.0
Dein Strap speichert die optischen Rohkanäle, aber NOOP rechnet sie nicht in einen Sauerstoffsättigungswert um - dies bleibt also leer, egal wie lange du ihn trägst. Importiere einen WHOOP-Export unter Datenquellen, um es aus deinem Kontoverlauf zu füllen.
Android um Kopplung bitten (experimentell)
diff --git a/android/app/src/main/res/values-es/strings.xml b/android/app/src/main/res/values-es/strings.xml
index 20aed28131..4949936586 100644
--- a/android/app/src/main/res/values-es/strings.xml
+++ b/android/app/src/main/res/values-es/strings.xml
@@ -2004,7 +2004,7 @@
Clásico
Línea
Barras
- estimación de la correa (sin verificar)
+ lectura de la pulsera
activado · aún sin estimación
Olvidar dispositivo…
Olvidar dispositivo
@@ -2613,7 +2613,7 @@
Una escala de Effort a tu elección, un anillo que puede dormir y muchos menos recálculos en vano
Esta constante necesita al menos dos lecturas históricas antes de que NOOP pueda representarla.
La estimación de oxígeno en sangre está desactivada
- Tu pulsera ofrece una estimación de oxígeno en sangre, pero no está verificada y viene desactivada. Actívala en Ajustes, o importa una exportación de WHOOP en Fuentes de datos para el valor calibrado.
+ Tu pulsera ofrece una lectura de oxígeno en sangre, pero su visualización está desactivada. Actívala en Ajustes para verla o importa una exportación de WHOOP en Fuentes de datos.
Sin porcentaje de oxígeno en sangre desde un WHOOP 4.0
Tu pulsera guarda los canales ópticos en bruto, pero NOOP no los convierte en un porcentaje de oxígeno en sangre, así que esto seguirá vacío por mucho que la lleves. Importa una exportación de WHOOP en Fuentes de datos para rellenarlo con tu historial.
Pedir a Android que vincule (experimental)
diff --git a/android/app/src/main/res/values-fr/strings.xml b/android/app/src/main/res/values-fr/strings.xml
index bd1c4cc3b1..6a1975491d 100644
--- a/android/app/src/main/res/values-fr/strings.xml
+++ b/android/app/src/main/res/values-fr/strings.xml
@@ -2022,7 +2022,7 @@
Classique
Courbe
Barres
- estimation du bracelet (non vérifiée)
+ mesure du bracelet
activé · pas encore d\'estimation
Oublier l\'appareil…
Oublier l\'appareil
@@ -2612,7 +2612,7 @@
Une échelle d\'Effort au choix, une bague qui peut dormir, et bien moins de recalculs inutiles
Cette constante nécessite au moins deux relevés historiques avant que NOOP puisse la tracer.
L\'estimation de l\'oxygène sanguin est désactivée
- Votre bracelet fournit une estimation de l\'oxygène sanguin, mais elle n\'est pas vérifiée et désactivée par défaut. Activez-la dans Réglages, ou importez un export WHOOP dans Sources de données pour la valeur calibrée.
+ Votre bracelet fournit une mesure de l\'oxygène sanguin, mais son affichage est désactivé. Activez-le dans les Réglages pour la voir, ou importez un export WHOOP dans Sources de données.
Pas de pourcentage d\'oxygène sanguin depuis un WHOOP 4.0
Votre bracelet enregistre les canaux optiques bruts, mais NOOP ne les convertit pas en pourcentage d\'oxygène sanguin : cette section restera vide quel que soit le temps de port. Importez un export WHOOP dans Sources de données pour la remplir depuis votre historique.
Demander à Android de s\'appairer (expérimental)
diff --git a/android/app/src/main/res/values-it/strings.xml b/android/app/src/main/res/values-it/strings.xml
index 1a47561f99..e04144e3e2 100644
--- a/android/app/src/main/res/values-it/strings.xml
+++ b/android/app/src/main/res/values-it/strings.xml
@@ -2091,7 +2091,7 @@
Classico
Linea
Barre
- stima della fascia (non verificata)
+ lettura della fascia
interruttore attivo · nessuna stima
Dimentica dispositivo…
Dimentica dispositivo
@@ -2595,7 +2595,7 @@
Una scala di sforzo a tua scelta, un anello che arriva al sonno e molti meno ricalcoli sprecati
Questo parametro vitale richiede almeno due letture storiche prima che NOOP possa mostrarlo in un grafico.
La stima dell\'ossigeno nel sangue è disattivata
- La tua fascia fornisce una stima dell\'ossigeno nel sangue, ma non è verificata ed è disattivata per impostazione predefinita. Attiva la stima della fascia in Impostazioni per vederla, oppure importa un\'esportazione WHOOP in Sorgenti dati per il valore calibrato.
+ La tua fascia fornisce una lettura dell\'ossigeno nel sangue, ma la visualizzazione è disattivata. Attivala nelle Impostazioni per vederla, oppure importa un\'esportazione WHOOP in Origini dati.
Nessuna percentuale di ossigeno nel sangue da una WHOOP 4.0
La tua fascia memorizza i canali ottici grezzi, ma NOOP non li trasforma in una percentuale di ossigeno nel sangue, quindi questo campo resta vuoto per quanto a lungo tu la indossi. Importa un\'esportazione WHOOP in Sorgenti dati per riempirlo dalla cronologia del tuo account.
Raccoglitore dati grezzi 5/MG
diff --git a/android/app/src/main/res/values-pl/strings.xml b/android/app/src/main/res/values-pl/strings.xml
index b5e9c52de8..dc63d635e4 100644
--- a/android/app/src/main/res/values-pl/strings.xml
+++ b/android/app/src/main/res/values-pl/strings.xml
@@ -1997,7 +1997,7 @@
Klasyczny
Linia
Bary
- oszacowanie paska (niezweryfikowane)
+ odczyt opaski
włącz ON · brak danych @82
Zapomnij o urządzeniu…
Zapomnij o urządzeniu
@@ -2623,7 +2623,7 @@
Wybierana skala Effort, pierścień, który może spać, i znacznie mniej zbędnych przeliczeń
Ten parametr wymaga co najmniej dwóch historycznych odczytów, zanim NOOP go wykreśli.
Szacowanie natlenienia krwi jest wyłączone
- Twoja opaska podaje szacowane natlenienie krwi, ale jest ono niezweryfikowane i domyślnie wyłączone. Włącz szacowanie opaski w Ustawieniach lub zaimportuj eksport WHOOP w Źródłach danych, aby uzyskać wartość kalibrowaną.
+ Twoja opaska podaje odczyt natlenienia krwi, ale jego wyświetlanie jest wyłączone. Włącz je w Ustawieniach albo zaimportuj eksport WHOOP w Źródłach danych.
Brak wartości procentowej natlenienia krwi z WHOOP 4.0
Twoja opaska zapisuje surowe kanały optyczne, ale NOOP nie przelicza ich na wartość procentową natlenienia krwi, więc pozostanie to puste niezależnie od czasu noszenia. Zaimportuj eksport WHOOP w Źródłach danych, aby wypełnić to historią konta.
Poproś Androida o sparowanie (eksperymentalne)
diff --git a/android/app/src/main/res/values-pt-rPT/strings.xml b/android/app/src/main/res/values-pt-rPT/strings.xml
index b64877aeb3..534d08aecc 100644
--- a/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -1997,7 +1997,7 @@
Clássico
Linha
Barras
- estimativa da pulseira (não verificada)
+ leitura da bracelete
ativado · ainda sem estimativa
Esquecer dispositivo…
Esquecer dispositivo
@@ -2605,7 +2605,7 @@
Uma escala de Effort à sua escolha, um anel que pode dormir e muito menos recálculos em vão
Este sinal vital precisa de pelo menos duas leituras históricas antes de o NOOP o poder representar.
A estimativa de oxigénio no sangue está desligada
- A sua bracelete fornece uma estimativa de oxigénio no sangue, mas não está verificada e vem desligada. Ative-a nas Definições, ou importe uma exportação WHOOP em Fontes de dados para o valor calibrado.
+ A sua bracelete fornece uma leitura de oxigénio no sangue, mas a apresentação está desligada. Ligue-a nas Definições para a ver, ou importe uma exportação WHOOP em Fontes de dados.
Sem percentagem de oxigénio no sangue a partir de um WHOOP 4.0
A sua bracelete guarda os canais óticos em bruto, mas o NOOP não os converte numa percentagem de oxigénio no sangue, por isso isto fica vazio por muito tempo que a use. Importe uma exportação WHOOP em Fontes de dados para preencher a partir do seu histórico.
Pedir ao Android para emparelhar (experimental)
diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml
index 6fbec7268f..ed9a0c4e5f 100644
--- a/android/app/src/main/res/values-ru/strings.xml
+++ b/android/app/src/main/res/values-ru/strings.xml
@@ -1994,7 +1994,7 @@
Классические
Линия
Столбцы
- оценка браслета (не проверена)
+ показание браслета
переключатель ВКЛ · оценки пока нет
Забыть устройство…
Забыть устройство
@@ -2457,7 +2457,7 @@
Шкала Усилия на ваш выбор, кольцо, которое засыпает, и намного меньше лишних пересчётов
Этому показателю нужно минимум два измерения в истории, прежде чем NOOP сможет построить график.
Оценка насыщения крови кислородом отключена
- Ваш браслет сообщает оценку насыщения крови кислородом, но она не проверена и по умолчанию отключена. Включите оценку браслета в Настройках, чтобы увидеть её, или импортируйте выгрузку WHOOP в «Источниках данных» для калиброванного значения.
+ Ваш браслет сообщает показание насыщения крови кислородом, но его отображение выключено. Включите его в Настройках или импортируйте экспорт WHOOP в разделе «Источники данных».
WHOOP 4.0 не даёт процент насыщения крови кислородом
Ваш браслет накапливает сырые оптические каналы, но NOOP не превращает их в процент насыщения крови кислородом, поэтому здесь будет пусто, сколько бы вы его ни носили. Импортируйте выгрузку WHOOP в разделе «Источники данных», чтобы заполнить это из истории вашего аккаунта.
Сборщик сырых данных 5/MG
diff --git a/android/app/src/main/res/values-zh/strings.xml b/android/app/src/main/res/values-zh/strings.xml
index 75a9e8a04f..f61e3c6559 100644
--- a/android/app/src/main/res/values-zh/strings.xml
+++ b/android/app/src/main/res/values-zh/strings.xml
@@ -1925,7 +1925,7 @@
经典
折线
柱形
- 腕带估算(未验证)
+ 手环读数
已开启 · 无 @82 数据
忘记设备…
忘记设备
@@ -2519,7 +2519,7 @@
可自选的 Effort 计分方式、能安心睡觉的戒指,以及大幅减少的无谓重算
该项体征需要至少两条历史读数,NOOP 才能绘制图表。
血氧估算已关闭
- 你的手环会给出血氧估算值,但该值未经验证且默认关闭。可在“设置”中开启手环估算,或在“数据来源”导入 WHOOP 导出文件以获得校准值。
+ 你的手环会提供血氧读数,但其显示已关闭。请在设置中开启以查看,或在数据来源中导入 WHOOP 导出文件。
WHOOP 4.0 无法提供血氧百分比
你的手环会记录原始光学通道,但 NOOP 不会将其换算成血氧百分比,因此无论佩戴多久这里都会是空的。请在“数据来源”中导入 WHOOP 导出文件,以用账户历史填充。
请求 Android 配对(实验性)
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 5ae979343a..94762a52b4 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -2144,7 +2144,7 @@
Line
Bars
- strap estimate (unverified)
+ strap reading
toggle ON · no estimate yet
Forget device…
Forget device
@@ -2651,7 +2651,7 @@
An Effort scale you choose, a ring that gets to sleep, and far fewer wasted re-scores
This vital needs at least two historical readings before NOOP can chart it.
The blood oxygen estimate is turned off
- Your strap reports a blood oxygen estimate, but it is unverified and off by default. Turn on the strap estimate in Settings to see it, or import a WHOOP export in Data Sources for the calibrated value.
+ Your strap reports a blood oxygen reading, but its display is turned off. Turn on the strap reading in Settings to see it, or import a WHOOP export in Data Sources.
No blood oxygen percentage from a WHOOP 4.0
Your strap banks the raw optical channels, but NOOP does not turn them into a blood oxygen percentage, so this stays empty however long you wear it. Import a WHOOP export in Data Sources to fill it from your account history.
5/MG Raw Data Collector