@@ -55,6 +55,76 @@ enum LocalizationHelper {
5555 return key
5656 }
5757 }
58+
59+ /// Formats a string using ICU MessageFormat with pluralization support
60+ static func formatPlural( _ pattern: String , arguments: [ String : Any ] , locale: Locale = Locale . current) -> String {
61+ // Convert arguments dictionary to format expected by MessageFormatter
62+ var formattedArgs : [ String : Any ] = [ : ]
63+ var argumentArray : [ Any ] = [ ]
64+ var argumentNames : [ String ] = [ ]
65+
66+ // Extract argument names and values, maintaining order
67+ for (key, value) in arguments {
68+ formattedArgs [ key] = value
69+ argumentNames. append ( key)
70+ argumentArray. append ( value)
71+ }
72+
73+ return formatterPlural ( pattern, arguments: arguments)
74+ }
75+
76+ // TODO: implement a ICU message format library
77+ /// Fallback pluralization formatter for when ICU MessageFormat isn't available
78+ private static func formatterPlural( _ pattern: String , arguments: [ String : Any ] ) -> String {
79+ var result = pattern
80+
81+ // Handle basic plural syntax: {count, plural, one {...} other {...}}
82+ let pluralRegex = try ! NSRegularExpression ( pattern: " \\ {( \\ w+), \\ s*plural, \\ s*one \\ s* \\ {([^}]+) \\ } \\ s*other \\ s* \\ {([^}]+) \\ } \\ } " , options: [ ] )
83+
84+ let matches = pluralRegex. matches ( in: pattern, options: [ ] , range: NSRange ( location: 0 , length: pattern. count) )
85+
86+ for match in matches. reversed ( ) { // Process in reverse to maintain string indices
87+ let fullMatchRange = match. range
88+ let countVarRange = match. range ( at: 1 )
89+ let oneFormRange = match. range ( at: 2 )
90+ let otherFormRange = match. range ( at: 3 )
91+
92+ let countVarName = String ( pattern [ Range ( countVarRange, in: pattern) !] )
93+ let oneForm = String ( pattern [ Range ( oneFormRange, in: pattern) !] )
94+ let otherForm = String ( pattern [ Range ( otherFormRange, in: pattern) !] )
95+
96+ if let countValue = arguments [ countVarName] {
97+ let count : Int = if let intValue = countValue as? Int {
98+ intValue
99+ } else if let doubleValue = countValue as? Double {
100+ Int ( doubleValue)
101+ } else if let stringValue = countValue as? String , let intValue = Int ( stringValue) {
102+ intValue
103+ } else {
104+ 0
105+ }
106+
107+ let selectedForm = ( count == 1 ) ? oneForm : otherForm
108+ var processedForm = selectedForm. replacingOccurrences ( of: " # " , with: " \( count) " )
109+
110+ // Replace other variables in the selected form
111+ for (key, value) in arguments {
112+ if key != countVarName {
113+ processedForm = processedForm. replacingOccurrences ( of: " { \( key) } " , with: " \( value) " )
114+ }
115+ }
116+
117+ result = result. replacingCharacters ( in: Range ( fullMatchRange, in: result) !, with: processedForm)
118+ }
119+ }
120+
121+ // Replace any remaining simple variables
122+ for (key, value) in arguments {
123+ result = result. replacingOccurrences ( of: " { \( key) } " , with: " \( value) " )
124+ }
125+
126+ return result
127+ }
58128}
59129
60130// MARK: - Public API
@@ -71,6 +141,11 @@ func t(_ key: String, comment: String = "", variables: [String: String] = [:]) -
71141 return localizedString
72142}
73143
144+ func tPlural( _ key: String , comment: String = " " , arguments: [ String : Any ] = [ : ] ) -> String {
145+ let localizedString = LocalizationHelper . getString ( for: key, comment: comment)
146+ return LocalizationHelper . formatPlural ( localizedString, arguments: arguments)
147+ }
148+
74149// These are for keys that are not yet translated
75150func tTodo( _ key: String , comment: String = " " , variables: [ String : String ] = [ : ] ) -> String {
76151 var localizedString = key
0 commit comments