@@ -143,6 +143,59 @@ fn get_thousands_separator() -> char {
143143 } )
144144}
145145
146+ /// Test-only helper: Get thousands separator without caching
147+ /// This allows tests to change locale environment variables and see the effects
148+ #[ cfg( test) ]
149+ fn get_thousands_separator_uncached ( ) -> char {
150+ #[ cfg( feature = "i18n-decimal" ) ]
151+ {
152+ use crate :: i18n:: get_numeric_locale;
153+ let ( locale, _encoding) = get_numeric_locale ( ) ;
154+
155+ // C and POSIX locales have no thousands separator
156+ // The default locale from i18n is en-US-posix for C/POSIX
157+ if locale. to_string ( ) . contains ( "posix" ) {
158+ return '\0' ;
159+ }
160+
161+ get_grouping_separator_from_icu ( locale. clone ( ) )
162+ }
163+
164+ #[ cfg( not( feature = "i18n-decimal" ) ) ]
165+ {
166+ // Fallback implementation when i18n-decimal feature is disabled
167+ // Try to read LC_NUMERIC or LANG environment variable
168+ if let Ok ( locale) = std:: env:: var ( "LC_NUMERIC" )
169+ . or_else ( |_| std:: env:: var ( "LC_ALL" ) )
170+ . or_else ( |_| std:: env:: var ( "LANG" ) )
171+ {
172+ // C and POSIX locales have no thousands separator
173+ if locale == "C" || locale == "POSIX" || locale. starts_with ( "C." ) {
174+ return '\0' ;
175+ }
176+
177+ // Simple heuristic: European locales use period, others use comma
178+ // This covers common cases like de_DE, fr_FR, it_IT, es_ES, nl_NL, etc.
179+ if locale. starts_with ( "de_" )
180+ || locale. starts_with ( "fr_" )
181+ || locale. starts_with ( "it_" )
182+ || locale. starts_with ( "es_" )
183+ || locale. starts_with ( "nl_" )
184+ || locale. starts_with ( "pt_" )
185+ || locale. starts_with ( "da_" )
186+ || locale. starts_with ( "sv_" )
187+ || locale. starts_with ( "no_" )
188+ || locale. starts_with ( "fi_" )
189+ {
190+ return '.' ;
191+ }
192+ }
193+
194+ // Default to comma (en_US style)
195+ ','
196+ }
197+ }
198+
146199/// Format a number with thousands separators based on LC_NUMERIC locale.
147200///
148201/// This function reads the LC_NUMERIC environment variable to determine
@@ -195,6 +248,39 @@ pub fn format_with_thousands_separator(number: u64) -> String {
195248 result
196249}
197250
251+ /// Test-only helper: Format number with uncached separator detection
252+ /// This allows tests to change locale environment variables and see the effects
253+ #[ cfg( test) ]
254+ fn format_with_thousands_separator_uncached ( number : u64 ) -> String {
255+ const GROUPING_SIZE : usize = 3 ;
256+
257+ let separator = get_thousands_separator_uncached ( ) ;
258+
259+ // C/POSIX locale has no thousands separator
260+ if separator == '\0' {
261+ return number. to_string ( ) ;
262+ }
263+
264+ let num_str = number. to_string ( ) ;
265+ let len = num_str. len ( ) ;
266+
267+ // Numbers less than 1000 don't need separators
268+ if len <= GROUPING_SIZE {
269+ return num_str;
270+ }
271+
272+ let mut result = String :: with_capacity ( len + ( len - 1 ) / GROUPING_SIZE ) ;
273+
274+ for ( i, ch) in num_str. chars ( ) . enumerate ( ) {
275+ if i > 0 && ( len - i) % GROUPING_SIZE == 0 {
276+ result. push ( separator) ;
277+ }
278+ result. push ( ch) ;
279+ }
280+
281+ result
282+ }
283+
198284#[ cfg( test) ]
199285mod tests {
200286 use super :: * ;
@@ -280,23 +366,32 @@ mod tests {
280366
281367 // Test with German locale (uses period as separator)
282368 std:: env:: set_var ( "LC_NUMERIC" , "de_DE.UTF-8" ) ;
283- assert_eq ! ( format_with_thousands_separator( 1234567 ) , "1.234.567" ) ;
369+ assert_eq ! (
370+ format_with_thousands_separator_uncached( 1234567 ) ,
371+ "1.234.567"
372+ ) ;
284373
285374 // Test with French locale (uses period as separator)
286375 std:: env:: set_var ( "LC_NUMERIC" , "fr_FR.UTF-8" ) ;
287- assert_eq ! ( format_with_thousands_separator( 1234567 ) , "1.234.567" ) ;
376+ assert_eq ! (
377+ format_with_thousands_separator_uncached( 1234567 ) ,
378+ "1.234.567"
379+ ) ;
288380
289381 // Test with US locale (uses comma as separator)
290382 std:: env:: set_var ( "LC_NUMERIC" , "en_US.UTF-8" ) ;
291- assert_eq ! ( format_with_thousands_separator( 1234567 ) , "1,234,567" ) ;
383+ assert_eq ! (
384+ format_with_thousands_separator_uncached( 1234567 ) ,
385+ "1,234,567"
386+ ) ;
292387
293388 // Test with C locale (no separator)
294389 std:: env:: set_var ( "LC_NUMERIC" , "C" ) ;
295- assert_eq ! ( format_with_thousands_separator ( 1234567 ) , "1234567" ) ;
390+ assert_eq ! ( format_with_thousands_separator_uncached ( 1234567 ) , "1234567" ) ;
296391
297392 // Test with POSIX locale (no separator)
298393 std:: env:: set_var ( "LC_NUMERIC" , "POSIX" ) ;
299- assert_eq ! ( format_with_thousands_separator ( 1234567 ) , "1234567" ) ;
394+ assert_eq ! ( format_with_thousands_separator_uncached ( 1234567 ) , "1234567" ) ;
300395
301396 // Restore original locale variables
302397 std:: env:: remove_var ( "LC_NUMERIC" ) ;
@@ -325,26 +420,26 @@ mod tests {
325420 std:: env:: remove_var ( "LC_NUMERIC" ) ;
326421 std:: env:: remove_var ( "LC_ALL" ) ;
327422 std:: env:: remove_var ( "LANG" ) ;
328- assert_eq ! ( get_thousands_separator ( ) , ',' ) ;
423+ assert_eq ! ( get_thousands_separator_uncached ( ) , ',' ) ;
329424
330425 // Test C locale
331426 std:: env:: set_var ( "LC_NUMERIC" , "C" ) ;
332- assert_eq ! ( get_thousands_separator ( ) , '\0' ) ;
427+ assert_eq ! ( get_thousands_separator_uncached ( ) , '\0' ) ;
333428
334429 // Test POSIX locale
335430 std:: env:: set_var ( "LC_NUMERIC" , "POSIX" ) ;
336- assert_eq ! ( get_thousands_separator ( ) , '\0' ) ;
431+ assert_eq ! ( get_thousands_separator_uncached ( ) , '\0' ) ;
337432
338433 // Test European locales
339434 std:: env:: set_var ( "LC_NUMERIC" , "de_DE.UTF-8" ) ;
340- assert_eq ! ( get_thousands_separator ( ) , '.' ) ;
435+ assert_eq ! ( get_thousands_separator_uncached ( ) , '.' ) ;
341436
342437 std:: env:: set_var ( "LC_NUMERIC" , "fr_FR.UTF-8" ) ;
343- assert_eq ! ( get_thousands_separator ( ) , '.' ) ;
438+ assert_eq ! ( get_thousands_separator_uncached ( ) , '.' ) ;
344439
345440 // Test US locale
346441 std:: env:: set_var ( "LC_NUMERIC" , "en_US.UTF-8" ) ;
347- assert_eq ! ( get_thousands_separator ( ) , ',' ) ;
442+ assert_eq ! ( get_thousands_separator_uncached ( ) , ',' ) ;
348443
349444 // Restore original locale
350445 std:: env:: remove_var ( "LC_NUMERIC" ) ;
0 commit comments