i18n.test 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. ;;;; i18n.test --- Exercise the i18n API. -*- coding: utf-8; mode: scheme; -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012,
  4. ;;;; 2013, 2014, 2015, 2016, 2017, 2018 Free Software Foundation, Inc.
  5. ;;;; Ludovic Courtès
  6. ;;;;
  7. ;;;; This library is free software; you can redistribute it and/or
  8. ;;;; modify it under the terms of the GNU Lesser General Public
  9. ;;;; License as published by the Free Software Foundation; either
  10. ;;;; version 3 of the License, or (at your option) any later version.
  11. ;;;;
  12. ;;;; This library is distributed in the hope that it will be useful,
  13. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;;;; Lesser General Public License for more details.
  16. ;;;;
  17. ;;;; You should have received a copy of the GNU Lesser General Public
  18. ;;;; License along with this library; if not, write to the Free Software
  19. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. (define-module (test-suite i18n)
  21. #:use-module (ice-9 i18n)
  22. #:use-module (ice-9 format)
  23. #:use-module (srfi srfi-1)
  24. #:use-module (test-suite lib))
  25. ;; Start from a pristine locale state.
  26. (setlocale LC_ALL "C")
  27. (define exception:locale-error
  28. (cons 'system-error "Failed to install locale"))
  29. (with-test-prefix "locale objects"
  30. (pass-if "make-locale (2 args)"
  31. (not (not (make-locale LC_ALL "C"))))
  32. (pass-if "make-locale (2 args, list)"
  33. (not (not (make-locale (list LC_COLLATE LC_NUMERIC) "C"))))
  34. (pass-if "make-locale (3 args)"
  35. (not (not (make-locale (list LC_COLLATE) "C"
  36. (make-locale (list LC_NUMERIC) "C")))))
  37. (pass-if-exception "make-locale with unknown locale" exception:locale-error
  38. (make-locale LC_ALL "does-not-exist"))
  39. (pass-if "locale?"
  40. (and (locale? (make-locale (list LC_ALL) "C"))
  41. (locale? (make-locale (list LC_TIME LC_NUMERIC) "C"
  42. (make-locale (list LC_CTYPE) "C")))))
  43. (pass-if "%global-locale"
  44. (and (locale? %global-locale))
  45. (locale? (make-locale (list LC_MONETARY) "C"
  46. %global-locale))))
  47. (with-test-prefix "text collation (English)"
  48. (pass-if "string-locale<?"
  49. (and (string-locale<? "hello" "world")
  50. (string-locale<? "hello" "world"
  51. (make-locale (list LC_COLLATE) "C"))))
  52. (pass-if "char-locale<?"
  53. (and (char-locale<? #\a #\b)
  54. (char-locale<? #\a #\b (make-locale (list LC_COLLATE) "C"))))
  55. (pass-if "string-locale-ci=?"
  56. (and (string-locale-ci=? "Hello" "HELLO")
  57. (string-locale-ci=? "Hello" "HELLO"
  58. (make-locale (list LC_COLLATE) "C"))))
  59. (pass-if "string-locale-ci<?"
  60. (and (string-locale-ci<? "hello" "WORLD")
  61. (string-locale-ci<? "hello" "WORLD"
  62. (make-locale (list LC_COLLATE) "C"))))
  63. (pass-if "large strings"
  64. ;; In Guile <= 2.2.4, these would overflow the C stack and crash.
  65. (let ((large (make-string 4000000 #\a)))
  66. (and (string-locale-ci=? large large)
  67. (not (string-locale-ci<? large large))
  68. (not (string-locale<? large large))))))
  69. (define mingw?
  70. (string-contains %host-type "-mingw32"))
  71. (define %french-locale-name
  72. (if mingw?
  73. "fra_FRA.850"
  74. "fr_FR.iso88591")) ;"iso88591" is the "normalized codeset"
  75. ;; What we really want for the following locales is that they be Unicode
  76. ;; capable, not necessarily UTF-8, which Windows does not provide.
  77. (define %french-utf8-locale-name
  78. (if mingw?
  79. "fra_FRA.1252"
  80. "fr_FR.utf8")) ;"utf8" is the "normalized codeset"
  81. (define %turkish-utf8-locale-name
  82. (if mingw?
  83. "tur_TRK.1254"
  84. "tr_TR.utf8"))
  85. (define %german-utf8-locale-name
  86. (if mingw?
  87. "deu_DEU.1252"
  88. "de_DE.utf8"))
  89. (define %greek-utf8-locale-name
  90. (if mingw?
  91. "grc_ELL.1253"
  92. "el_GR.utf8"))
  93. (define %american-english-locale-name
  94. "en_US.utf8")
  95. (define %french-locale
  96. (false-if-exception
  97. (make-locale (list LC_CTYPE LC_COLLATE LC_NUMERIC LC_TIME LC_MONETARY)
  98. %french-locale-name)))
  99. (define %french-utf8-locale
  100. (false-if-exception
  101. (make-locale (list LC_CTYPE LC_COLLATE LC_NUMERIC LC_TIME LC_MONETARY)
  102. %french-utf8-locale-name)))
  103. (define %german-utf8-locale
  104. (false-if-exception
  105. (make-locale LC_ALL
  106. %german-utf8-locale-name)))
  107. (define %greek-utf8-locale
  108. (false-if-exception
  109. (make-locale LC_ALL
  110. %greek-utf8-locale-name)))
  111. (define %turkish-utf8-locale
  112. (false-if-exception
  113. (make-locale LC_ALL
  114. %turkish-utf8-locale-name)))
  115. (define %american-english-locale
  116. (false-if-exception
  117. (make-locale LC_ALL
  118. %american-english-locale-name)))
  119. (define (under-locale-or-unresolved locale thunk)
  120. ;; On non-GNU systems, an exception may be raised only when the locale is
  121. ;; actually used rather than at `make-locale'-time. Thus, we must guard
  122. ;; against both.
  123. (if locale
  124. (if (string-contains %host-type "-gnu")
  125. (thunk)
  126. (catch 'system-error thunk
  127. (lambda (key . args)
  128. (throw 'unresolved))))
  129. (throw 'unresolved)))
  130. (define (under-french-locale-or-unresolved thunk)
  131. (under-locale-or-unresolved %french-locale thunk))
  132. (define (under-french-utf8-locale-or-unresolved thunk)
  133. (under-locale-or-unresolved %french-utf8-locale thunk))
  134. (define (under-turkish-utf8-locale-or-unresolved thunk)
  135. ;; FreeBSD 8.2 and 9.1, Solaris 2.10, Darwin 8.11.0, Cygwin, and MinGW have
  136. ;; a broken tr_TR locale where `i' is mapped to uppercase `I'
  137. ;; instead of `İ', so disable tests on that platform.
  138. (if (or (string-contains %host-type "freebsd8")
  139. (string-contains %host-type "freebsd9")
  140. (string-contains %host-type "solaris2.10")
  141. (string-contains %host-type "darwin8")
  142. (string-contains %host-type "mingw32")
  143. (string-contains %host-type "cygwin"))
  144. (throw 'unresolved)
  145. (under-locale-or-unresolved %turkish-utf8-locale thunk)))
  146. (define (under-german-utf8-locale-or-unresolved thunk)
  147. (under-locale-or-unresolved %german-utf8-locale thunk))
  148. (define (under-greek-utf8-locale-or-unresolved thunk)
  149. (under-locale-or-unresolved %greek-utf8-locale thunk))
  150. (define (under-american-english-locale-or-unresolved thunk)
  151. (under-locale-or-unresolved %american-english-locale thunk))
  152. (with-test-prefix "text collation (French)"
  153. (pass-if "string-locale<?"
  154. (under-french-locale-or-unresolved
  155. (lambda ()
  156. (string-locale<? "été" "hiver" %french-locale))))
  157. (pass-if "char-locale<?"
  158. (under-french-locale-or-unresolved
  159. (lambda ()
  160. (char-locale<? #\é #\h %french-locale))))
  161. (pass-if "string-locale-ci=?"
  162. (under-french-locale-or-unresolved
  163. (lambda ()
  164. (string-locale-ci=? "ÉTÉ" "été" %french-locale))))
  165. (pass-if "string-locale-ci=? (2 args, wide strings)"
  166. (under-french-utf8-locale-or-unresolved
  167. (lambda ()
  168. ;; Note: Character `œ' is not part of Latin-1, so these are wide
  169. ;; strings.
  170. (dynamic-wind
  171. (lambda ()
  172. (setlocale LC_ALL %french-utf8-locale-name))
  173. (lambda ()
  174. (string-locale-ci=? "œuf" "ŒUF"))
  175. (lambda ()
  176. (setlocale LC_ALL "C"))))))
  177. (pass-if "string-locale-ci=? (3 args, wide strings)"
  178. (under-french-utf8-locale-or-unresolved
  179. (lambda ()
  180. (string-locale-ci=? "œuf" "ŒUF" %french-utf8-locale))))
  181. (pass-if "string-locale-ci<>?"
  182. (under-french-locale-or-unresolved
  183. (lambda ()
  184. (and (string-locale-ci<? "été" "Hiver" %french-locale)
  185. (string-locale-ci>? "HiVeR" "été" %french-locale)))))
  186. (pass-if "string-locale-ci<>? (wide strings)"
  187. (under-french-utf8-locale-or-unresolved
  188. (lambda ()
  189. ;; One of the strings is UCS-4, the other is Latin-1.
  190. (and (string-locale-ci<? "Œdème" "œuf" %french-utf8-locale)
  191. (string-locale-ci>? "Œuf" "œdÈMe" %french-utf8-locale)))))
  192. (pass-if "string-locale-ci<>? (wide and narrow strings)"
  193. (under-french-utf8-locale-or-unresolved
  194. (lambda ()
  195. ;; One of the strings is UCS-4, the other is Latin-1.
  196. (and (string-locale-ci>? "Œdème" "odyssée" %french-utf8-locale)
  197. (string-locale-ci<? "Odyssée" "œdème" %french-utf8-locale)))))
  198. (pass-if "char-locale-ci<>?"
  199. (under-french-locale-or-unresolved
  200. (lambda ()
  201. (and (char-locale-ci<? #\é #\H %french-locale)
  202. (char-locale-ci>? #\h #\É %french-locale)))))
  203. (pass-if "char-locale-ci<>? (wide)"
  204. (under-french-utf8-locale-or-unresolved
  205. (lambda ()
  206. (and (char-locale-ci<? #\o #\œ %french-utf8-locale)
  207. (char-locale-ci>? #\Œ #\e %french-utf8-locale))))))
  208. (with-test-prefix "text collation (German)"
  209. (pass-if "string-locale-ci=?"
  210. (under-german-utf8-locale-or-unresolved
  211. (lambda ()
  212. (let ((de (make-locale LC_ALL %german-utf8-locale-name)))
  213. (string-locale-ci=? "Straße" "STRASSE"))))))
  214. (with-test-prefix "text collation (Greek)"
  215. (pass-if "string-locale-ci=?"
  216. (under-greek-utf8-locale-or-unresolved
  217. (lambda ()
  218. (let ((gr (make-locale LC_ALL %greek-utf8-locale-name)))
  219. (string-locale-ci=? "ΧΑΟΣ" "χαος" gr))))))
  220. (with-test-prefix "text collation (Czech)"
  221. (pass-if "string-locale<? for 'ch'"
  222. (under-locale-or-unresolved
  223. "cs_CZ.utf8"
  224. (lambda ()
  225. ;; Czech sorts digraph 'ch' between 'h' and 'i'.
  226. ;;
  227. ;; GNU libc 2.22 gets this wrong:
  228. ;; <https://sourceware.org/bugzilla/show_bug.cgi?id=18589>. For
  229. ;; now, just skip it if it fails (XXX).
  230. (or (and (string-locale>? "chxxx" "cxxx")
  231. (string-locale>? "chxxx" "hxxx")
  232. (string-locale<? "chxxxx" "ixxx"))
  233. (throw 'unresolved))))))
  234. (with-test-prefix "character mapping"
  235. (pass-if "char-locale-downcase"
  236. (and (eqv? #\a (char-locale-downcase #\A))
  237. (eqv? #\a (char-locale-downcase #\A (make-locale LC_ALL "C")))))
  238. (pass-if "char-locale-upcase"
  239. (and (eqv? #\Z (char-locale-upcase #\z))
  240. (eqv? #\Z (char-locale-upcase #\z (make-locale LC_ALL "C")))))
  241. (pass-if "char-locale-titlecase"
  242. (and (eqv? #\T (char-locale-titlecase #\t))
  243. (eqv? #\T (char-locale-titlecase #\t (make-locale LC_ALL "C")))))
  244. (pass-if "char-locale-titlecase Dž"
  245. (and (eqv? #\762 (char-locale-titlecase #\763))
  246. (eqv? #\762 (char-locale-titlecase #\763 (make-locale LC_ALL "C")))))
  247. (pass-if "char-locale-upcase Turkish"
  248. (under-turkish-utf8-locale-or-unresolved
  249. (lambda ()
  250. (eqv? #\İ (char-locale-upcase #\i %turkish-utf8-locale)))))
  251. (pass-if "char-locale-downcase Turkish"
  252. (under-turkish-utf8-locale-or-unresolved
  253. (lambda ()
  254. (eqv? #\i (char-locale-downcase #\İ %turkish-utf8-locale))))))
  255. (with-test-prefix "string mapping"
  256. (pass-if "string-locale-downcase"
  257. (and (string=? "a" (string-locale-downcase "A"))
  258. (string=? "a" (string-locale-downcase "A" (make-locale LC_ALL "C")))))
  259. (pass-if "string-locale-upcase"
  260. (and (string=? "Z" (string-locale-upcase "z"))
  261. (string=? "Z" (string-locale-upcase "z" (make-locale LC_ALL "C")))))
  262. (pass-if "string-locale-titlecase"
  263. (and (string=? "Hello, World" (string-locale-titlecase "hello, world"))
  264. (string=? "Hello, World" (string-locale-titlecase
  265. "hello, world" (make-locale LC_ALL "C")))))
  266. (pass-if "large strings"
  267. ;; In Guile <= 2.2.4, these would overflow the C stack and crash.
  268. (let ((hellos (string-join (make-list 700000 "hello")))
  269. (HELLOs (string-join (make-list 700000 "HELLO")))
  270. (Hellos (string-join (make-list 700000 "Hello"))))
  271. (and (string=? hellos (string-locale-downcase Hellos))
  272. (string=? HELLOs (string-locale-upcase Hellos))
  273. (string=? Hellos (string-locale-titlecase hellos)))))
  274. (pass-if "string-locale-upcase German"
  275. (under-german-utf8-locale-or-unresolved
  276. (lambda ()
  277. (let ((de (make-locale LC_ALL %german-utf8-locale-name)))
  278. (string=? "STRASSE"
  279. (string-locale-upcase "Straße" de))))))
  280. (pass-if "string-locale-upcase Greek"
  281. (under-greek-utf8-locale-or-unresolved
  282. (lambda ()
  283. (let ((el (make-locale LC_ALL %greek-utf8-locale-name)))
  284. (string=? "ΧΑΟΣ"
  285. (string-locale-upcase "χαος" el))))))
  286. (pass-if "string-locale-upcase Greek (two sigmas)"
  287. (under-greek-utf8-locale-or-unresolved
  288. (lambda ()
  289. (let ((el (make-locale LC_ALL %greek-utf8-locale-name)))
  290. (string=? "ΓΕΙΆ ΣΑΣ"
  291. (string-locale-upcase "Γειά σας" el))))))
  292. (pass-if "string-locale-downcase Greek"
  293. (under-greek-utf8-locale-or-unresolved
  294. (lambda ()
  295. (let ((el (make-locale LC_ALL %greek-utf8-locale-name)))
  296. (string=? "χαος"
  297. (string-locale-downcase "ΧΑΟΣ" el))))))
  298. (pass-if "string-locale-downcase Greek (two sigmas)"
  299. (under-greek-utf8-locale-or-unresolved
  300. (lambda ()
  301. (let ((el (make-locale LC_ALL %greek-utf8-locale-name)))
  302. (string=? "γειά σας"
  303. (string-locale-downcase "ΓΕΙΆ ΣΑΣ" el))))))
  304. (pass-if "string-locale-upcase Turkish"
  305. (under-turkish-utf8-locale-or-unresolved
  306. (lambda ()
  307. (string=? "İI" (string-locale-upcase "iı" %turkish-utf8-locale)))))
  308. (pass-if "string-locale-downcase Turkish"
  309. (under-turkish-utf8-locale-or-unresolved
  310. (lambda ()
  311. (string=? "iı" (string-locale-downcase "İI" %turkish-utf8-locale))))))
  312. (with-test-prefix "number parsing"
  313. (pass-if "locale-string->integer"
  314. (call-with-values (lambda () (locale-string->integer "123"))
  315. (lambda (result char-count)
  316. (and (equal? result 123)
  317. (equal? char-count 3)))))
  318. (pass-if "locale-string->inexact"
  319. (call-with-values
  320. (lambda ()
  321. (locale-string->inexact "123.456"
  322. (make-locale (list LC_NUMERIC) "C")))
  323. (lambda (result char-count)
  324. (and (equal? result 123.456)
  325. (equal? char-count 7)))))
  326. (pass-if "locale-string->inexact (French)"
  327. (under-french-locale-or-unresolved
  328. (lambda ()
  329. (call-with-values
  330. (lambda ()
  331. (locale-string->inexact "123,456" %french-locale))
  332. (lambda (result char-count)
  333. (and (equal? result 123.456)
  334. (equal? char-count 7))))))))
  335. ;;;
  336. ;;; `nl-langinfo'
  337. ;;;
  338. (setlocale LC_ALL "C")
  339. (define %c-locale (make-locale LC_ALL "C"))
  340. (define %english-days
  341. '("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"))
  342. (define (every? . args)
  343. (not (not (apply every args))))
  344. (with-test-prefix "nl-langinfo et al."
  345. (pass-if "locale-day (1 arg)"
  346. (every? equal?
  347. %english-days
  348. (map locale-day (map 1+ (iota 7)))))
  349. (pass-if "locale-day (2 args)"
  350. (every? equal?
  351. %english-days
  352. (map (lambda (day)
  353. (locale-day day %c-locale))
  354. (map 1+ (iota 7)))))
  355. (pass-if "locale-day (2 args, using `%global-locale')"
  356. (every? equal?
  357. %english-days
  358. (map (lambda (day)
  359. (locale-day day %global-locale))
  360. (map 1+ (iota 7)))))
  361. (pass-if "locale-day (French)"
  362. (under-french-locale-or-unresolved
  363. (lambda ()
  364. (let ((result (locale-day 3 %french-locale)))
  365. (and (string? result)
  366. (string-ci=? result "mardi"))))))
  367. (pass-if "locale-day (French, using `%global-locale')"
  368. ;; Make sure `%global-locale' captures the current locale settings as
  369. ;; installed using `setlocale'.
  370. (under-french-locale-or-unresolved
  371. (lambda ()
  372. (dynamic-wind
  373. (lambda ()
  374. (setlocale LC_TIME %french-locale-name))
  375. (lambda ()
  376. (let* ((fr (make-locale (list LC_MONETARY) "C" %global-locale))
  377. (result (locale-day 3 fr)))
  378. (setlocale LC_ALL "C")
  379. (and (string? result)
  380. (string-ci=? result "mardi"))))
  381. (lambda ()
  382. (setlocale LC_ALL "C"))))))
  383. (pass-if "default locale"
  384. ;; Make sure the default locale does not capture the current locale
  385. ;; settings as installed using `setlocale'. The default locale should be
  386. ;; "C".
  387. (under-french-locale-or-unresolved
  388. (lambda ()
  389. (dynamic-wind
  390. (lambda ()
  391. (setlocale LC_ALL %french-locale-name))
  392. (lambda ()
  393. (let* ((locale (make-locale (list LC_MONETARY) "C"))
  394. (result (locale-day 3 locale)))
  395. (setlocale LC_ALL "C")
  396. (and (string? result)
  397. (string-ci=? result "Tuesday"))))
  398. (lambda ()
  399. (setlocale LC_ALL "C"))))))
  400. (pass-if "locale-am-string"
  401. (not (not (member (locale-am-string)
  402. '("AM" "am" "A.M." "a.m.")))))
  403. (pass-if "locale-am-string (greek)"
  404. (under-greek-utf8-locale-or-unresolved
  405. (lambda ()
  406. (not (not (member (locale-am-string %greek-utf8-locale)
  407. '("ΠΜ" "πμ" "Π.Μ." "π.μ.")))))))
  408. (pass-if "locale-pm-string"
  409. (not (not (member (locale-pm-string)
  410. '("PM" "pm" "P.M." "p.m.")))))
  411. (pass-if "locale-pm-string (Greek)"
  412. (under-greek-utf8-locale-or-unresolved
  413. (lambda ()
  414. (not (not (member (locale-pm-string %greek-utf8-locale)
  415. '("ΜΜ" "μμ" "Μ.Μ." "μ.μ.")))))))
  416. (pass-if "locale-digit-grouping"
  417. ;; In the C locale, there is no rule for grouping.
  418. (null? (locale-digit-grouping)))
  419. (pass-if "locale-digit-grouping (French)"
  420. (under-french-locale-or-unresolved
  421. (lambda ()
  422. ;; All systems that have a GROUPING nl_item should know
  423. ;; that French numbers are grouped in 3 digit chunks.
  424. ;; Those systems that have no GROUPING nl_item may use
  425. ;; the hard-coded default of no grouping.
  426. (let ((result (locale-digit-grouping %french-locale)))
  427. (cond
  428. ((null? result)
  429. (throw 'unresolved))
  430. ((eqv? 3 (false-if-exception (car result)))
  431. #t)
  432. (else
  433. #f))))))
  434. (pass-if "locale-positive-separated-by-space?"
  435. ;; In any locale, this must be a boolean.
  436. (let ((result (locale-positive-separated-by-space? #f)))
  437. (or (eqv? #t result)
  438. (eqv? #f result))))
  439. (pass-if "locale-positive-separated-by-space? (international)"
  440. ;; In any locale, this must be a boolean.
  441. (let ((result (locale-positive-separated-by-space? #t)))
  442. (or (eqv? #t result)
  443. (eqv? #f result))))
  444. (pass-if "locale-monetary-grouping"
  445. ;; In the C locale, there is no rule for grouping of digits
  446. ;; of monetary values.
  447. (null? (locale-monetary-grouping)))
  448. (pass-if "locale-monetary-grouping (French)"
  449. (under-french-utf8-locale-or-unresolved
  450. (lambda ()
  451. ;; All systems that have a MON_GROUPING nl_item should know
  452. ;; that French monetary values are grouped in 3 digit chunks.
  453. ;; Those systems that have no MON_GROUPING nl_item may use the
  454. ;; hard-coded default of no grouping.
  455. (let ((result (locale-monetary-grouping %french-utf8-locale)))
  456. (cond
  457. ((null? result)
  458. (throw 'unresolved))
  459. ((eqv? 3 (false-if-exception (car result)))
  460. #t)
  461. (else
  462. #f)))))))
  463. ;;;
  464. ;;; Numbers.
  465. ;;;
  466. (define (french-number-string=? expected result)
  467. ;; Return true if RESULT is equal to EXPECTED, modulo white space.
  468. ;; This is meant to deal with French locales: glibc 2.27+ uses
  469. ;; NO-BREAK SPACE to separate 3-digit groups, whereas earlier versions
  470. ;; used SPACE.
  471. (or (string=? expected result)
  472. (string=? (string-map (lambda (chr)
  473. (case chr
  474. ((#\space) #\240)
  475. (else chr))) ;NO-BREAK SPACE
  476. expected)
  477. result)))
  478. (with-test-prefix "number->locale-string"
  479. ;; We assume the global locale is "C" at this point.
  480. (with-test-prefix "C"
  481. (pass-if-equal "no thousand separator"
  482. ""
  483. ;; Unlike in English, the "C" locale has no thousand separator.
  484. ;; If this doesn't hold, the following tests will fail.
  485. (locale-thousands-separator))
  486. (pass-if-equal "integer"
  487. "123456"
  488. (number->locale-string 123456))
  489. (pass-if-equal "fraction"
  490. "1234.567"
  491. (number->locale-string 1234.567))
  492. (pass-if-equal "fraction, 1 digit"
  493. "1234.6"
  494. (number->locale-string 1234.567 1))
  495. (pass-if-equal "fraction, 10 digits"
  496. "0.0000300000"
  497. (number->locale-string .00003 10))
  498. (pass-if-equal "trailing zeros"
  499. "-10.00000"
  500. (number->locale-string -10.0 5))
  501. (pass-if-equal "positive inexact zero, 1 digit"
  502. "0.0"
  503. (number->locale-string .0 1)))
  504. (with-test-prefix "French"
  505. (pass-if "integer"
  506. (under-french-locale-or-unresolved
  507. (lambda ()
  508. (let ((fr (make-locale LC_ALL %french-locale-name)))
  509. (french-number-string=? "123 456"
  510. (number->locale-string 123456 #t fr))))))
  511. (pass-if "negative integer"
  512. (under-french-locale-or-unresolved
  513. (lambda ()
  514. (let ((fr (make-locale LC_ALL %french-locale-name)))
  515. (french-number-string=? "-1 234 567"
  516. (number->locale-string -1234567 #t fr))))))
  517. (pass-if "fraction"
  518. (under-french-locale-or-unresolved
  519. (lambda ()
  520. (let ((fr (make-locale LC_ALL %french-locale-name)))
  521. (french-number-string=? "1 234,567"
  522. (number->locale-string 1234.567 #t fr))))))
  523. (pass-if "fraction, 1 digit"
  524. (under-french-locale-or-unresolved
  525. (lambda ()
  526. (let ((fr (make-locale LC_ALL %french-locale-name)))
  527. (french-number-string=? "1 234,6"
  528. (number->locale-string 1234.567 1 fr))))))))
  529. (with-test-prefix "format ~h"
  530. ;; Some systems like Darwin lack the `GROUPING' nl_item, and thus
  531. ;; `locale-digit-grouping' defaults to '(); skip the tests in that
  532. ;; case.
  533. (with-test-prefix "French"
  534. (pass-if "12345.678"
  535. (under-french-locale-or-unresolved
  536. (lambda ()
  537. (if (null? (locale-digit-grouping %french-locale))
  538. (throw 'unresolved)
  539. (french-number-string=? "12 345,678"
  540. (format #f "~:h" 12345.678
  541. %french-locale)))))))
  542. (with-test-prefix "English"
  543. (pass-if-equal "12345.678"
  544. "12,345.678"
  545. (under-american-english-locale-or-unresolved
  546. (lambda ()
  547. (if (null? (locale-digit-grouping %american-english-locale))
  548. (throw 'unresolved)
  549. (format #f "~:h" 12345.678
  550. %american-english-locale)))))))
  551. (with-test-prefix "monetary-amount->locale-string"
  552. (with-test-prefix "French"
  553. (pass-if "integer"
  554. (under-french-locale-or-unresolved
  555. (lambda ()
  556. (let* ((fr (make-locale LC_ALL %french-locale-name))
  557. (str (string-trim-both (monetary-amount->locale-string 123456 #f fr))))
  558. ;; Check for both NO-BREAK SPACE and SPACE.
  559. (or (string=? "123 456,00 +EUR" str)
  560. (string=? "123 456,00 +EUR" str))))))
  561. (pass-if "fraction"
  562. (under-french-locale-or-unresolved
  563. (lambda ()
  564. (let* ((fr (make-locale LC_ALL %french-locale-name))
  565. (str (monetary-amount->locale-string 1234.567 #t fr)))
  566. ;; Check for both NO-BREAK SPACE and SPACE.
  567. (or (string=? "1 234,57 EUR " str)
  568. (string=? "1 234,57 EUR " str))))))
  569. (pass-if-equal "positive inexact zero"
  570. "0,00 +EUR"
  571. (under-french-locale-or-unresolved
  572. (lambda ()
  573. (let ((fr (make-locale LC_ALL %french-locale-name)))
  574. (string-trim-both (monetary-amount->locale-string 0. #f fr))))))
  575. (pass-if-equal "one cent"
  576. "0,01 EUR "
  577. (under-french-locale-or-unresolved
  578. (lambda ()
  579. (let ((fr (make-locale LC_ALL %french-locale-name)))
  580. (monetary-amount->locale-string .01 #t fr)))))
  581. (pass-if-equal "very little money"
  582. "0,00 EUR "
  583. (under-french-locale-or-unresolved
  584. (lambda ()
  585. (let ((fr (make-locale LC_ALL %french-locale-name)))
  586. (monetary-amount->locale-string .00003 #t fr)))))))