Skip to content

i18n(android): translate the Sleep tab's hardcoded copy - #2614

Open
paradix86 wants to merge 1 commit into
ryanbr:mainfrom
paradix86:i18n/android-sleep-screen
Open

paradix86 wants to merge 1 commit into
ryanbr:mainfrom
paradix86:i18n/android-sleep-screen

Conversation

@paradix86

@paradix86 paradix86 commented Sep 30, 2026 •

Copy link
Copy Markdown

What this PR does

On the staged version of noop, the phone's Sleep tab was still largely English. The copy lived as literals in Kotlin, so the existing translations could not reach it. This moves it into string resources for all eight locales, covering the whole tab:

  • SleepScreen.kt: the screen subtitle, the "Last night / Sleep performance · Rest" hero with its score word (Poor/Fair/Good/Optimal) and "On-device" badge, the "Tap to log · Phase 1" sleep-marks header, the nap summary (Main sleep / Nap(s) / Total), the "why this sleep" explainer, the Oura raw-stages note, the stage names and the per-stage insight line, the delete / cannot-save messages and the accessibility labels.
  • SleepHeroLogic.kt: "Last night" and "N nights ago", now a <plurals> so Polish and Russian get their few/many forms.
  • SleepMetricCardsUi.kt: the Night detail tiles, the Sleep-debt ledger, Stages vs typical, Asleep duration and Trend cards, the Hours vs Needed and Consistency cards (legends and footers), and the empty states.
  • SleepNightNavUi.kt: the night editor's Bedtime / Wake-up labels, the picker titles and "Pick night date".
  • SleepFormatting.kt: the "vs typical" / "vs need" / "On target" / "Below need" captions and the three sleep-debt sentences. "The last N nights" is a plural.

Implementation notes:

  • The English stage names ("Awake" / "REM" / "Light" / "Deep") and "On-device" stay the internal keys the code switches on. Only what is drawn is translated (stageDisplayName, sourceBadgeLabel).
  • The helpers covered by JVM tests now return a resource id (sleepScoreWordRes, debtCaptionRes, mainSleepReasonCopy), the same approach as i18n(android): translate the Settings screen's hardcoded copy #2498. The tests read the English value from values/strings.xml through a shared test helper, EnglishResources, so they still assert the text an English user sees.
  • Two small fixes on the same screen: the consistency chart's weekday labels follow the app language instead of Locale.US, and the sleep-debt trend footer's empty value reads — again instead of the mis-encoded —.
  • Unit symbols (7h 12m, 6.5 h, %) are left as they are. They are shared app-wide and belong in a separate change.

About the translations:

  • Most strings reuse the translation the iOS catalogue (Localizable.xcstrings) already has for the same English sentence, so both platforms read alike.
  • About 30 strings had no iOS counterpart and are new translations. Italian is reviewed by me (native speaker). The new strings in the other locales would benefit from a native speaker's check.
  • Three catalogue entries were still English and are translated here instead: pt-PT "balanced", and pl "vs typ" and "min SD".

Android only: the iOS Sleep screen already reads these strings from the catalogue.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • CI / tooling

How it was tested

  • python3 Tools/i18n_audit.py --ci upstream/main passes. The baseline drops the six SleepScreen.kt entries. The gate also caught a resource whose trailing space AAPT2 strips, and two Polish echoes; both are fixed.
  • python3 Tools/doc_comment_lint.py passes, and unittest discover over Tools/ passes.
  • A resource check confirms no duplicate keys, no extra translations, and the same placeholders in every locale as in English.
  • Not run locally: the Gradle build and the Kotlin unit tests. Android CI on this PR is the compile and test check for SleepHeroLogicTest, MainSleepReasonCopyTest, SleepDebtFormattingTest and the new EnglishResources helper.

Checklist

  • Swift package tests pass for any package I touched (swift test in Packages/<name>)
  • Android unit tests pass if I touched android/ (./gradlew testFullDebugUnitTest; after a branch switch add --no-build-cache --rerun-tasks, or Gradle may fail classes you never touched)
  • No new build warnings introduced
  • UI changes use only StrandDesign tokens — no hardcoded colors, fonts, or spacing
  • No hardcoded hex frame bytes; protocol facts live in the schema / decoders
  • Follows the conventions in docs/CONTRIBUTING.md
  • I did not commit generated output (Strand.xcodeproj/) or any secrets/keystores

Related issues

#2498 same approach for the Settings screen

The Sleep tab still showed English on a translated phone: the screen
subtitle, the "Last night / Sleep performance" hero with its score word
and source badge, the sleep-marks header, the nap summary, the "why
this sleep" explainer, the stage names and insight line, the night
detail tiles, the sleep-debt ledger and its sentences, the trend and
consistency cards, and the night editor's labels and picker titles.

Move that copy into string resources for all eight locales. Where the
iOS catalogue already translates the same English sentence, its
translation is reused so both platforms read alike; the remaining
strings are new translations. "N nights ago" and "the last N nights"
are plurals, so Polish and Russian get their few/many forms.

The English stage names and "On-device" stay the internal keys the code
switches on; only what is drawn is translated. Helpers covered by JVM
tests return a resource id (sleepScoreWordRes, debtCaptionRes,
mainSleepReasonCopy), and the tests read the English value from
values/strings.xml through a shared EnglishResources helper.

Also: the consistency chart's weekday labels follow the app language
instead of Locale.US, and the sleep-debt trend footer's empty value
reads "—" again instead of the mis-encoded "—".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant