From f54816d099f5cc7e55cdbe99a8f5be53a8029777 Mon Sep 17 00:00:00 2001 From: phranck Date: Sat, 14 Feb 2026 18:28:54 +0100 Subject: [PATCH] Feat: Phase 2 i18n Core API - LocalizedString View and Language Management - Add LocalizedString View component for displaying localized strings - Add Text(localized:) convenience initializer for Text views - Add AppState.setLanguage(_:) method for runtime language switching - Add AppState.currentLanguage computed property - Integrate LocalizationService into RenderLoop environment - Remove duplicate LocalizedString function (View takes precedence) - All 1069 tests pass Core i18n functionality complete. Ready for Phase 3 (String Replacement) --- Sources/TUIkit/App/RenderLoop.swift | 1 + .../Localization/LocalizationExtensions.swift | 52 +++++++++++++++++++ .../Localization/LocalizationService.swift | 8 --- .../TUIkit/Localization/LocalizedString.swift | 39 ++++++++++++++ 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 Sources/TUIkit/Localization/LocalizationExtensions.swift create mode 100644 Sources/TUIkit/Localization/LocalizedString.swift diff --git a/Sources/TUIkit/App/RenderLoop.swift b/Sources/TUIkit/App/RenderLoop.swift index bf858732..1674922e 100644 --- a/Sources/TUIkit/App/RenderLoop.swift +++ b/Sources/TUIkit/App/RenderLoop.swift @@ -351,6 +351,7 @@ extension RenderLoop { environment.keyEventDispatcher = tuiContext.keyEventDispatcher environment.renderCache = tuiContext.renderCache environment.preferenceStorage = tuiContext.preferences + environment.localizationService = LocalizationService.shared return environment } diff --git a/Sources/TUIkit/Localization/LocalizationExtensions.swift b/Sources/TUIkit/Localization/LocalizationExtensions.swift new file mode 100644 index 00000000..00d66e32 --- /dev/null +++ b/Sources/TUIkit/Localization/LocalizationExtensions.swift @@ -0,0 +1,52 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// LocalizationExtensions.swift +// +// Created by LAYERED.work +// License: MIT + +// MARK: - Text Localization Convenience + +extension Text { + /// Creates a text view with a localized string using a dot-notation key. + /// + /// # Example + /// + /// ```swift + /// Text(localized: "button.ok") + /// ``` + /// + /// This is an alternative to `LocalizedString("button.ok")` for cases where + /// you need a `Text` view directly (e.g., as a return value from a computed property). + /// + /// - Parameter key: The dot-notation key for the localized string. + public init(localized key: String) { + let localizedValue = LocalizationService.shared.string(for: key) + self.init(localizedValue) + } +} + +// MARK: - AppState Language Convenience + +extension AppState { + /// Gets the currently active language. + public var currentLanguage: LocalizationService.Language { + LocalizationService.shared.currentLanguage + } + + /// Changes the active language and triggers a re-render. + /// + /// The language preference is persisted to disk and will survive app restarts. + /// The UI automatically re-renders with the new language strings. + /// + /// # Example + /// + /// ```swift + /// AppState.shared.setLanguage(.german) + /// ``` + /// + /// - Parameter language: The language to activate. + public func setLanguage(_ language: LocalizationService.Language) { + LocalizationService.shared.setLanguage(language) + setNeedsRender() + } +} diff --git a/Sources/TUIkit/Localization/LocalizationService.swift b/Sources/TUIkit/Localization/LocalizationService.swift index 6fbb58e7..63c02ade 100644 --- a/Sources/TUIkit/Localization/LocalizationService.swift +++ b/Sources/TUIkit/Localization/LocalizationService.swift @@ -228,11 +228,3 @@ public final class LocalizationService: @unchecked Sendable { } } -// MARK: - Convenience - -/// A localized string retrieved using a dot-notation key. -/// -/// Example: `LocalizedString("button.ok")` -public func LocalizedString(_ key: String) -> String { - LocalizationService.shared.string(for: key) -} diff --git a/Sources/TUIkit/Localization/LocalizedString.swift b/Sources/TUIkit/Localization/LocalizedString.swift new file mode 100644 index 00000000..4b58ec2b --- /dev/null +++ b/Sources/TUIkit/Localization/LocalizedString.swift @@ -0,0 +1,39 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// LocalizedString.swift +// +// Created by LAYERED.work +// License: MIT + +// MARK: - Localized String View + +/// A view that displays a localized string using a dot-notation key. +/// +/// The string is looked up in the current language from the localization service. +/// If the key is missing, falls back to English, then to the key itself. +/// +/// # Example +/// +/// ```swift +/// VStack { +/// LocalizedString("button.ok") +/// LocalizedString("error.invalid_input") +/// } +/// ``` +/// +/// Use this for all UI strings that need localization. +public struct LocalizedString: View { + /// The dot-notation key for the string to display. + private let key: String + + /// Creates a localized string view. + /// + /// - Parameter key: The dot-notation key (e.g., "button.ok", "error.invalid_input") + public init(_ key: String) { + self.key = key + } + + public var body: some View { + Text(LocalizationService.shared.string(for: key)) + } +} +