@@ -174,6 +174,7 @@ export class LingoDotDevEngine {
174174 * @param payload - The payload to count words in
175175 * @returns The total number of words
176176 */
177+
177178 // private countWordsInRecord(payload: any | Record<string, any> | Array<any>): number {
178179 // if (Array.isArray(payload)) {
179180 // return payload.reduce((acc, item) => acc + this.countWordsInRecord(item), 0);
@@ -186,31 +187,61 @@ export class LingoDotDevEngine {
186187 // }
187188 // }
188189
189- private countWordsInRecord ( payload : any | Record < string , any > | Array < any > ) : number {
190- const stack : any [ ] = [ payload ] ;
191- let totalWordCount = 0 ;
192-
193- while ( stack . length > 0 ) {
194- const current = stack . pop ( ) ;
195-
196- if ( current === null || current === undefined ) {
197- continue ;
198- }
199-
200- if ( Array . isArray ( current ) ) {
201- stack . push ( ...current ) ;
202- } else if ( typeof current === 'object' ) {
203- stack . push ( ...Object . values ( current ) ) ;
204- } else if ( typeof current === 'string' ) {
205- totalWordCount += current . trim ( ) . split ( / \s + / ) . filter ( Boolean ) . length ;
206- }
190+ private countWordsInRecord ( payload : unknown ) : number {
191+ let wordCount = 0 ;
192+ const processingStack : unknown [ ] = [ payload ] ;
193+ const SPACE_CHAR_CODE = 32 ;
194+ const TAB_CHAR_CODE = 9 ;
195+ const NEWLINE_CHAR_CODE = 10 ;
196+
197+ while ( processingStack . length > 0 ) {
198+ const currentItem = processingStack . pop ( ) ;
199+
200+ if ( typeof currentItem === 'string' ) {
201+ let isBetweenWords = true ;
202+ let currentWordCount = 0 ;
203+
204+ for ( let i = 0 ; i < currentItem . length ; i ++ ) {
205+ const charCode = currentItem . charCodeAt ( i ) ;
206+ const isWhitespace = charCode === SPACE_CHAR_CODE ||
207+ charCode === TAB_CHAR_CODE ||
208+ charCode === NEWLINE_CHAR_CODE ;
209+
210+ if ( isBetweenWords && ! isWhitespace ) {
211+ currentWordCount ++ ;
212+ isBetweenWords = false ;
213+ } else if ( ! isBetweenWords && isWhitespace ) {
214+ isBetweenWords = true ;
215+ }
216+ }
217+
218+ wordCount += currentWordCount ;
219+ }
220+ else if ( Array . isArray ( currentItem ) ) {
221+ // Process array elements in reverse to maintain original order
222+ for ( let i = currentItem . length - 1 ; i >= 0 ; i -- ) {
223+ processingStack . push ( currentItem [ i ] ) ;
224+ }
225+ }
226+ else if ( this . isRecord ( currentItem ) ) {
227+ // Process object properties efficiently
228+ for ( const key in currentItem ) {
229+ if ( Object . prototype . hasOwnProperty . call ( currentItem , key ) ) {
230+ processingStack . push ( currentItem [ key ] ) ;
231+ }
232+ }
233+ }
207234 }
208-
209- return totalWordCount ;
210- }
211235
236+ return wordCount ;
237+ }
212238
239+ // Type guard for plain objects
240+ private isRecord ( value : unknown ) : value is Record < string , unknown > {
241+ return value !== null && typeof value === 'object' && ! Array . isArray ( value ) ;
242+ }
213243
244+
214245 /**
215246 * Localize a typical JavaScript object
216247 * @param obj - The object to be localized (strings will be extracted and translated)
0 commit comments