feat(i18n): complete internationalization for iOS + watchOS across all views

Migrate every hardcoded Text("...") string to the L10n / LocalizedStringResource
type-safe key system with full en/fr/de/es translations (4 languages).

iOS changes (TabataGo target):
- Strings.swift: ~90 new L10n keys across 13 groups (action, tab, home, zone,
  level, programs, programDetail, player, profile, settings, policy, paywall,
  health, complete, activity, onboarding, goal)
- Localizable.xcstrings: 145 → 245+ keys with fr/de/es translations
- Model enums: FitnessLevel.label & FitnessGoal.label changed from String to
  LocalizedStringResource, backed by L10n.level/goal keys
- Component param types changed to LocalizedStringResource: StatBadge,
  SectionHeader, ProfileRow, PolicySection, CompletionStat, FeatureRow,
  OnboardingHeader, PrimaryButton, SelectionCard
- All 18 view files updated: HomeTab, ActivityTab, ProgramsTab, ProfileTab,
  MainTabView, SettingsView, PolicyViews, CompletionView, BodyZoneView,
  ProgramDetailView, PaywallView, OnboardingView, PlayerView

Watch changes (TabataGoWatch target):
- New Localizable.xcstrings: 23 keys with en/fr/de/es (phase labels, idle
  state, activity rings, complication strings)
- New WatchL10n.swift: type-safe enum (needs manual Xcode target membership)
- Updated: WatchPlayerView, WatchIdleView, WatchActivityView,
  TabataGoComplication (inline LocalizedStringResource for widget target)

Both iOS and watchOS targets build with zero errors.
This commit is contained in:
Millian Lamiaux
2026-04-22 00:41:19 +02:00
parent e28bebea79
commit 0f5b7b9e18
23 changed files with 4423 additions and 340 deletions

View File

@@ -4,16 +4,17 @@ import Foundation
/// Usage: Text(L10n.action.start) or String(localized: L10n.player.phase.work)
enum L10n {
enum action {
static let back = LocalizedStringResource("action.back")
static let cancel = LocalizedStringResource("action.cancel")
static let `continue` = LocalizedStringResource("action.continue")
static let done = LocalizedStringResource("action.done")
static let save = LocalizedStringResource("action.save")
static let share = LocalizedStringResource("action.share")
static let start = LocalizedStringResource("action.start")
static let startWorkout = LocalizedStringResource("action.startWorkout")
static let startTraining = LocalizedStringResource("action.startTraining")
static let unlockPremium = LocalizedStringResource("action.unlockPremium")
static let back = LocalizedStringResource("action.back")
static let cancel = LocalizedStringResource("action.cancel")
static let `continue` = LocalizedStringResource("action.continue")
static let done = LocalizedStringResource("action.done")
static let retry = LocalizedStringResource("action.retry")
static let save = LocalizedStringResource("action.save")
static let share = LocalizedStringResource("action.share")
static let start = LocalizedStringResource("action.start")
static let startWorkout = LocalizedStringResource("action.startWorkout")
static let startTraining = LocalizedStringResource("action.startTraining")
static let unlockPremium = LocalizedStringResource("action.unlockPremium")
static let restorePurchases = LocalizedStringResource("action.restorePurchases")
}
enum tab {
@@ -26,14 +27,21 @@ enum L10n {
static let featuredTitle = LocalizedStringResource("home.featuredTitle")
static let featuredSubtitle = LocalizedStringResource("home.featuredSubtitle")
static let browseTitle = LocalizedStringResource("home.browseTitle")
static let browseSubtitle = LocalizedStringResource("home.browseSubtitle")
static let streak = LocalizedStringResource("home.streak")
static let thisWeek = LocalizedStringResource("home.thisWeek")
static let allTime = LocalizedStringResource("home.allTime")
static let free = LocalizedStringResource("home.free")
static let failedToLoad = LocalizedStringResource("home.failedToLoad")
}
enum zone {
static let upper = LocalizedStringResource("zone.upper")
static let lower = LocalizedStringResource("zone.lower")
static let full = LocalizedStringResource("zone.full")
static let upper = LocalizedStringResource("zone.upper")
static let lower = LocalizedStringResource("zone.lower")
static let full = LocalizedStringResource("zone.full")
static let upperDescription = LocalizedStringResource("zone.upper.description")
static let lowerDescription = LocalizedStringResource("zone.lower.description")
static let fullDescription = LocalizedStringResource("zone.full.description")
static let defaultDescription = LocalizedStringResource("zone.default.description")
static func label(for zone: String) -> LocalizedStringResource {
switch zone.lowercased() {
@@ -43,12 +51,45 @@ enum L10n {
default: return LocalizedStringResource(stringLiteral: zone.capitalized)
}
}
static func description(for zone: String) -> LocalizedStringResource {
switch zone.lowercased() {
case "upper-body": return upperDescription
case "lower-body": return lowerDescription
case "full-body": return fullDescription
default: return defaultDescription
}
}
}
enum level {
static let beginner = LocalizedStringResource("level.beginner")
static let intermediate = LocalizedStringResource("level.intermediate")
static let advanced = LocalizedStringResource("level.advanced")
}
enum programs {
static let filterZone = LocalizedStringResource("programs.filterZone")
static let all = LocalizedStringResource("programs.all")
static let allLevels = LocalizedStringResource("programs.allLevels")
static let noneFound = LocalizedStringResource("programs.noneFound")
static let searchPrompt = LocalizedStringResource("programs.searchPrompt")
}
enum programDetail {
static let warmUp = LocalizedStringResource("programDetail.warmUp")
static let coolDown = LocalizedStringResource("programDetail.coolDown")
static let startWorkout = LocalizedStringResource("programDetail.startWorkout")
static let unlockPremium = LocalizedStringResource("programDetail.unlockPremium")
/// printf: %d = block number
static let blockFmt = LocalizedStringResource("programDetail.blockFmt")
/// printf: %d block, %d of total
static let blockOfFmt = LocalizedStringResource("programDetail.blockOfFmt")
/// printf: %d rounds, %d workTime, %d restTime
static let blockSubtitleFmt = LocalizedStringResource("programDetail.blockSubtitleFmt")
/// printf: %d rounds
static let roundsFmt = LocalizedStringResource("programDetail.roundsFmt")
/// printf: %d minutes
static let minFmt = LocalizedStringResource("programDetail.minFmt")
/// printf: %d kcal
static let kcalFmt = LocalizedStringResource("programDetail.kcalFmt")
}
enum player {
enum phase {
static let getReady = LocalizedStringResource("player.phase.getReady")
@@ -77,6 +118,7 @@ enum L10n {
}
enum complete {
static let title = LocalizedStringResource("complete.title")
static let saving = LocalizedStringResource("complete.saving")
static let saveToHealth = LocalizedStringResource("complete.saveToHealth")
static let savedToHealth = LocalizedStringResource("complete.savedToHealth")
static let backToHome = LocalizedStringResource("complete.backToHome")
@@ -84,23 +126,68 @@ enum L10n {
static let calories = LocalizedStringResource("complete.calories")
static let rounds = LocalizedStringResource("complete.rounds")
static let avgHeartRate = LocalizedStringResource("complete.avgHeartRate")
static let completion = LocalizedStringResource("complete.completion")
static let shareWorkout = LocalizedStringResource("complete.shareWorkout")
}
enum activity {
static let currentStreak = LocalizedStringResource("activity.currentStreak")
static let bestStreak = LocalizedStringResource("activity.bestStreak")
static let history = LocalizedStringResource("activity.history")
static let workouts = LocalizedStringResource("activity.workouts")
static let minutes = LocalizedStringResource("activity.minutes")
static let noWorkouts = LocalizedStringResource("activity.noWorkouts")
static let noWorkoutsMessage = LocalizedStringResource("activity.noWorkoutsMessage")
static let currentStreak = LocalizedStringResource("activity.currentStreak")
static let bestStreak = LocalizedStringResource("activity.bestStreak")
static let history = LocalizedStringResource("activity.history")
static let workouts = LocalizedStringResource("activity.workouts")
static let minutes = LocalizedStringResource("activity.minutes")
static let calories = LocalizedStringResource("activity.calories")
static let days = LocalizedStringResource("activity.days")
static let today = LocalizedStringResource("activity.today")
static let noWorkouts = LocalizedStringResource("activity.noWorkouts")
static let noWorkoutsMessage = LocalizedStringResource("activity.noWorkoutsMessage")
}
enum profile {
static let subscription = LocalizedStringResource("profile.subscription")
static let fitnessProfile = LocalizedStringResource("profile.fitnessProfile")
static let level = LocalizedStringResource("profile.level")
static let goal = LocalizedStringResource("profile.goal")
static let weeklyGoal = LocalizedStringResource("profile.weeklyGoal")
static let yearly = LocalizedStringResource("profile.yearly")
static let monthly = LocalizedStringResource("profile.monthly")
static let athlete = LocalizedStringResource("profile.athlete")
/// printf: %@ = formatted date. e.g. "Joined Jan 15, 2026"
static let joinedFmt = LocalizedStringResource("profile.joinedFmt")
/// printf: %d = frequency. e.g. "3x / week"
static let weeklyGoalFmt = LocalizedStringResource("profile.weeklyGoalFmt")
}
enum onboarding {
static let whatIsYourName = LocalizedStringResource("onboarding.whatIsYourName")
static let fitnessLevel = LocalizedStringResource("onboarding.fitnessLevel")
static let mainGoal = LocalizedStringResource("onboarding.mainGoal")
static let howOften = LocalizedStringResource("onboarding.howOften")
static let allSet = LocalizedStringResource("onboarding.allSet")
static let whatIsYourName = LocalizedStringResource("onboarding.whatIsYourName")
static let whatIsYourNameSubtitle = LocalizedStringResource("onboarding.whatIsYourNameSubtitle")
static let fitnessLevel = LocalizedStringResource("onboarding.fitnessLevel")
static let fitnessLevelSubtitle = LocalizedStringResource("onboarding.fitnessLevelSubtitle")
static let mainGoal = LocalizedStringResource("onboarding.mainGoal")
static let mainGoalSubtitle = LocalizedStringResource("onboarding.mainGoalSubtitle")
static let howOften = LocalizedStringResource("onboarding.howOften")
static let howOftenSubtitle = LocalizedStringResource("onboarding.howOftenSubtitle")
static let allSet = LocalizedStringResource("onboarding.allSet")
static let allSetSubtitle = LocalizedStringResource("onboarding.allSetSubtitle")
static let getStarted = LocalizedStringResource("onboarding.getStarted")
static let startFirstWorkout = LocalizedStringResource("onboarding.startFirstWorkout")
static let enterName = LocalizedStringResource("onboarding.enterName")
static let perWeek = LocalizedStringResource("onboarding.perWeek")
static let anyChallenges = LocalizedStringResource("onboarding.anyChallenges")
static let challengesSubtitle = LocalizedStringResource("onboarding.challengesSubtitle")
static let pill4MinWorkouts = LocalizedStringResource("onboarding.pill4MinWorkouts")
static let pillNoEquipment = LocalizedStringResource("onboarding.pillNoEquipment")
static let pillVoiceGuided = LocalizedStringResource("onboarding.pillVoiceGuided")
static let tabataDesc = LocalizedStringResource("onboarding.tabataDesc")
enum levelDesc {
static let beginner = LocalizedStringResource("onboarding.level.beginnerDesc")
static let intermediate = LocalizedStringResource("onboarding.level.intermediateDesc")
static let advanced = LocalizedStringResource("onboarding.level.advancedDesc")
}
enum goalDesc {
static let weightLoss = LocalizedStringResource("onboarding.goal.weightLossDesc")
static let cardio = LocalizedStringResource("onboarding.goal.cardioDesc")
static let strength = LocalizedStringResource("onboarding.goal.strengthDesc")
static let wellness = LocalizedStringResource("onboarding.goal.wellnessDesc")
}
}
enum goal {
static let weightLoss = LocalizedStringResource("goal.weightLoss")
@@ -109,24 +196,62 @@ enum L10n {
static let wellness = LocalizedStringResource("goal.wellness")
}
enum settings {
static let title = LocalizedStringResource("settings.title")
static let audio = LocalizedStringResource("settings.audio")
static let soundEffects = LocalizedStringResource("settings.soundEffects")
static let voiceCoaching = LocalizedStringResource("settings.voiceCoaching")
static let music = LocalizedStringResource("settings.music")
static let haptics = LocalizedStringResource("settings.haptics")
static let hapticFeedback = LocalizedStringResource("settings.hapticFeedback")
static let reminders = LocalizedStringResource("settings.reminders")
static let dailyReminder = LocalizedStringResource("settings.dailyReminder")
static let resetProgress = LocalizedStringResource("settings.resetProgress")
static let title = LocalizedStringResource("settings.title")
static let audio = LocalizedStringResource("settings.audio")
static let soundEffects = LocalizedStringResource("settings.soundEffects")
static let voiceCoaching = LocalizedStringResource("settings.voiceCoaching")
static let music = LocalizedStringResource("settings.music")
static let haptics = LocalizedStringResource("settings.haptics")
static let hapticFeedback = LocalizedStringResource("settings.hapticFeedback")
static let reminders = LocalizedStringResource("settings.reminders")
static let dailyReminder = LocalizedStringResource("settings.dailyReminder")
static let reminderTime = LocalizedStringResource("settings.reminderTime")
static let manageHealth = LocalizedStringResource("settings.manageHealth")
static let account = LocalizedStringResource("settings.account")
static let notSet = LocalizedStringResource("settings.notSet")
static let about = LocalizedStringResource("settings.about")
static let resetProgress = LocalizedStringResource("settings.resetProgress")
static let resetProgressConfirm = LocalizedStringResource("settings.resetProgressConfirm")
static let confirmReset = LocalizedStringResource("settings.confirmReset")
static let name = LocalizedStringResource("settings.name")
static let joined = LocalizedStringResource("settings.joined")
static let version = LocalizedStringResource("settings.version")
static let resetProgressMessage = LocalizedStringResource("settings.resetProgressMessage")
}
enum policy {
static let privacyTitle = LocalizedStringResource("policy.privacyTitle")
static let termsTitle = LocalizedStringResource("policy.termsTitle")
static let dataWeCollect = LocalizedStringResource("policy.dataWeCollect")
static let appleHealth = LocalizedStringResource("policy.appleHealth")
static let analytics = LocalizedStringResource("policy.analytics")
static let purchases = LocalizedStringResource("policy.purchases")
static let dataStorage = LocalizedStringResource("policy.dataStorage")
static let contact = LocalizedStringResource("policy.contact")
static let useOfApp = LocalizedStringResource("policy.useOfApp")
static let subscription = LocalizedStringResource("policy.subscription")
static let healthDisclaimer = LocalizedStringResource("policy.healthDisclaimer")
static let limitationOfLiability = LocalizedStringResource("policy.limitationOfLiability")
static let changesToTerms = LocalizedStringResource("policy.changesToTerms")
}
enum paywall {
static let title = LocalizedStringResource("paywall.title")
static let subtitle = LocalizedStringResource("paywall.subtitle")
static let startPremium = LocalizedStringResource("paywall.startPremium")
static let premiumActive = LocalizedStringResource("paywall.premiumActive")
static let upgradePrompt = LocalizedStringResource("paywall.upgradePrompt")
static let cancelAnytime = LocalizedStringResource("paywall.cancelAnytime")
static let title = LocalizedStringResource("paywall.title")
static let subtitle = LocalizedStringResource("paywall.subtitle")
static let startPremium = LocalizedStringResource("paywall.startPremium")
static let premiumActive = LocalizedStringResource("paywall.premiumActive")
static let upgradePrompt = LocalizedStringResource("paywall.upgradePrompt")
static let cancelAnytime = LocalizedStringResource("paywall.cancelAnytime")
static let processing = LocalizedStringResource("paywall.processing")
static let bestValue = LocalizedStringResource("paywall.bestValue")
static let unlimitedWorkouts = LocalizedStringResource("paywall.unlimitedWorkouts")
static let unlimitedWorkoutsDesc = LocalizedStringResource("paywall.unlimitedWorkoutsDesc")
static let healthkitSync = LocalizedStringResource("paywall.healthkitSync")
static let healthkitSyncDesc = LocalizedStringResource("paywall.healthkitSyncDesc")
static let progressSync = LocalizedStringResource("paywall.progressSync")
static let progressSyncDesc = LocalizedStringResource("paywall.progressSyncDesc")
static let voiceCoaching = LocalizedStringResource("paywall.voiceCoaching")
static let voiceCoachingDesc = LocalizedStringResource("paywall.voiceCoachingDesc")
static let error = LocalizedStringResource("paywall.error")
static let somethingWrong = LocalizedStringResource("paywall.somethingWrong")
}
enum health {
static let appleHealth = LocalizedStringResource("health.appleHealth")
@@ -134,5 +259,6 @@ enum L10n {
static let exercise = LocalizedStringResource("health.exercise")
static let stand = LocalizedStringResource("health.stand")
static let restingHR = LocalizedStringResource("health.restingHR")
static let today = LocalizedStringResource("health.today")
}
}