import Foundation import SwiftUI /// Schlüssel wie Android `strings.xml` – Auflösung über `Localizable.strings` (de/en). enum L10n { static func tr(_ key: String) -> String { String(localized: String.LocalizationValue(key)) } static func timeoutIn(_ time: String) -> String { String(format: tr("timeout_in"), time) } static func inboxNew(_ count: Int) -> String { String(format: tr("inbox_new_count"), locale: .current, count) } static func feedbackCreatedAt(_ date: String) -> String { String(format: tr("feedback_created_at"), locale: .current, date) } static func countriesLoadError(_ detail: String) -> String { String(format: tr("countries_load_error"), locale: .current, detail) } static func userBlocked(_ name: String) -> String { String(format: tr("user_blocked"), locale: .current, name) } static func userUnblocked(_ name: String) -> String { String(format: tr("user_unblocked"), locale: .current, name) } } func formatTimeout(totalSeconds: Int) -> String { let minutes = totalSeconds / 60 let seconds = totalSeconds % 60 return String(format: "%d:%02d", minutes, seconds) } func localizeRuntimeMessage(_ message: String) -> String { if message.hasPrefix("Country list could not be loaded:") { let detail = String(message.dropFirst("Country list could not be loaded:".count)).trimmingCharacters(in: .whitespacesAndNewlines) return L10n.countriesLoadError(detail) } switch message { case "Image uploaded": return L10n.tr("image_upload_success") case "Image upload failed": return L10n.tr("image_upload_failed") case "Image exceeds 5 MB": return L10n.tr("image_upload_too_large") case "Image could not be opened": return L10n.tr("image_upload_open_failed") case "Feedback saved": return L10n.tr("feedback_saved") default: break } if message.hasSuffix(" blocked") { let name = String(message.dropLast(" blocked".count)) return L10n.userBlocked(name) } if message.hasSuffix(" unblocked") { let name = String(message.dropLast(" unblocked".count)) return L10n.userUnblocked(name) } return message } func formatFeedbackTimestamp(_ createdAt: String) -> String? { guard !createdAt.isEmpty else { return nil } return ISO8601DateFormatter().date(from: createdAt).map { DateFormatter.localizedString(from: $0, dateStyle: .medium, timeStyle: .short) } } func smileyEmoji(hexCode: String) -> String { guard let v = UInt32(hexCode, radix: 16), let scalar = UnicodeScalar(v) else { return "" } return String(Character(scalar)) } struct SmileyItem: Identifiable { var id: String { token } let token: String let hexCode: String } let ypChatSmileys: [SmileyItem] = [ SmileyItem(token: ":)", hexCode: "1F642"), SmileyItem(token: ":D", hexCode: "1F600"), SmileyItem(token: ":(", hexCode: "1F641"), SmileyItem(token: ";)", hexCode: "1F609"), SmileyItem(token: ":p", hexCode: "1F60B"), SmileyItem(token: ";p", hexCode: "1F61C"), SmileyItem(token: "O)", hexCode: "1F607"), SmileyItem(token: ":*", hexCode: "1F617"), SmileyItem(token: "(h)", hexCode: "1FA77"), SmileyItem(token: "xD", hexCode: "1F602"), SmileyItem(token: ":@", hexCode: "1F635"), SmileyItem(token: ":O", hexCode: "1F632"), SmileyItem(token: ":3", hexCode: "1F63A"), SmileyItem(token: ":|", hexCode: "1F610"), SmileyItem(token: ":/", hexCode: "1FAE4"), SmileyItem(token: ":#", hexCode: "1F912"), SmileyItem(token: "#)", hexCode: "1F973"), SmileyItem(token: "%)", hexCode: "1F974"), SmileyItem(token: "(t)", hexCode: "1F44D"), SmileyItem(token: ":'(", hexCode: "1F622"), ] struct GenderOptionRow: Identifiable { var id: String { value } let value: String let label: String } func displayCountryName(user: UserDto, countries: [CountryOption]) -> String { if let byEnglish = countries.first(where: { $0.englishName == user.country }) { return byEnglish.displayName } if let byIso = countries.first(where: { $0.isoCode.caseInsensitiveCompare(user.isoCountryCode) == .orderedSame }) { return byIso.displayName } return user.country }