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:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user