1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
#lang racket/base
(require (for-syntax racket/base
racket/list
(prefix-in english-ct: "private/english-string-constants.rkt"))
racket/file
racket/contract/base
(prefix-in english: "private/english-string-constants.rkt")
(prefix-in spanish: "private/spanish-string-constants.rkt")
(prefix-in german: "private/german-string-constants.rkt")
(prefix-in french: "private/french-string-constants.rkt")
(prefix-in dutch: "private/dutch-string-constants.rkt")
(prefix-in danish: "private/danish-string-constants.rkt")
(prefix-in portuguese: "private/portuguese-string-constants.rkt")
(prefix-in japanese: "private/japanese-string-constants.rkt")
(prefix-in traditional-chinese: "private/traditional-chinese-string-constants.rkt")
(prefix-in simplified-chinese: "private/simplified-chinese-string-constants.rkt")
(prefix-in russian: "private/russian-string-constants.rkt")
(prefix-in ukrainian: "private/ukrainian-string-constants.rkt")
(prefix-in korean: "private/korean-string-constants.rkt")
(prefix-in bulgarian: "private/bulgarian-string-constants.rkt"))
(provide string-constant string-constants
this-language all-languages set-language-pref)
(provide
(contract-out
[string-constant? (-> any/c boolean?)]
[dynamic-string-constant (-> string-constant? string?)]
[dynamic-string-constants (-> string-constant? (listof string?))]))
;; set-language-pref : symbol -> void
(define (set-language-pref language)
(put-preferences (list 'plt:human-language) (list language)))
;; table : (listof (list symbol regexp regexp))
;; this table indicates what the default value of the natural language
;; preference is. the first regexp is used under Windows and the second
;; is used on other platforms. All regexps are compared to the result
;; of (system-language+country)
(define table
'((english #rx"^en_" #rx"^English_")
(spanish #rx"^es_" #rx"^Espanol_")
(german #rx"^de_" #rx"^German_")
(french #rx"^fr_" #rx"French_")
(dutch #rx"nl_" #rx"^Netherlands_")
(danish #rx"^da_DK" #rx"^Danish_")
(portuguese #rx"^pt_" #rx"Portuguese_")
(japanese #rx"^ja_" #rx"^Japan_")
(traditional-chinese #rx"^zh_(HK|TW)" #rx"Chinese_(Hong|Taiwan)")
(simplified-chinese #rx"^zh_CN" #rx"Chinese_China")
(russian #rx"^ru_" #rx"^Russian_")
(ukrainian #rx"^uk_" #rx"^Ukrainian_")
(korean #rx"^ko_" #rx"^Korean_")
(bulgarian #rx"^bg_" #rx"^Bulgarian_")))
;; default-language : -> symbol
;; uses `table' and system-language+contry to find what language to start with
(define (default-language)
(let ([slc (system-language+country)])
(let loop ([table table])
(if (null? table)
'english
(let ([ent (car table)])
(if (or (regexp-match (cadr ent) slc)
(and (cddr ent)
(regexp-match (caddr ent) slc)))
(car ent)
(loop (cdr table))))))))
(define-struct sc (language-name constants [ht #:mutable]))
(define available-string-constant-sets
(list
(make-sc 'english english:string-constants #f)
(make-sc 'spanish spanish:string-constants #f)
(make-sc 'french french:string-constants #f)
(make-sc 'german german:string-constants #f)
(make-sc 'dutch dutch:string-constants #f)
(make-sc 'danish danish:string-constants #f)
(make-sc 'portuguese portuguese:string-constants #f)
(make-sc 'japanese japanese:string-constants #f)
(make-sc 'traditional-chinese traditional-chinese:string-constants #f)
(make-sc 'simplified-chinese simplified-chinese:string-constants #f)
(make-sc 'russian russian:string-constants #f)
(make-sc 'ukrainian ukrainian:string-constants #f)
(make-sc 'korean korean:string-constants #f)
(make-sc 'bulgarian bulgarian:string-constants #f)))
(define first-string-constant-set (car available-string-constant-sets))
;; language : symbol
(define language
(with-handlers ([exn:fail? (lambda (_) (default-language))])
(get-preference 'plt:human-language (lambda () (default-language)))))
(define the-sc
(or (for/or ([sc (in-list available-string-constant-sets)])
(and (equal? language (sc-language-name sc))
sc))
first-string-constant-set))
(define (dynamic-string-constant key)
(dynamic-string-constant/who the-sc key 'dynamic-string-constant))
(define (dynamic-string-constants key)
(for/list ([sc (in-list available-string-constant-sets)])
(dynamic-string-constant/who sc key 'dynamic-string-constants)))
(define (dynamic-string-constant/who an-sc key who)
(show-warning-message)
(hash-ref (sc-constants an-sc) key
(λ ()
(hash-ref (sc-constants first-string-constant-set)
key
(λ ()
(error who
"unknown string-constant\n key: ~e" key))))))
(define (string-constant? sym)
(and (hash-ref (sc-constants first-string-constant-set) sym #f)
#t))
(define already-warned? #f)
(define (show-warning-message)
(when env-var-set
(unless already-warned?
(set! already-warned? #t)
;; type no-warning-cache-key = (cons symbol symbol)
;; warning-table : (listof (list no-warning-cache-key (listof (list sym string))))
(define warning-table null)
(define (extract-ht sc)
(unless (sc-ht sc)
(define ht (make-hash))
(for ([(ent val) (in-hash (sc-constants sc))])
(hash-set! ht ent #t))
(set-sc-ht! sc ht))
(sc-ht sc))
(define (check-one-way sc1 sc2)
(define assoc1 (sc-constants sc1))
(define assoc2 (sc-constants sc2))
(define ht2 (extract-ht sc2))
(for ([(constant1 value1) (in-hash assoc1)])
(define pair2 (hash-ref ht2 constant1 #f))
(unless pair2
(define no-warning-cache-key (cons (sc-language-name sc1)
(sc-language-name sc2)))
(when (or (env-var-set? (sc-language-name sc1))
(env-var-set? (sc-language-name sc2)))
(cond
[(memf (lambda (ent) (equal? (mcar ent) no-warning-cache-key))
warning-table)
=>
(lambda (x)
(let ([ent (car x)])
(set-mcdr! ent (cons (list constant1 value1) (mcdr ent)))))]
[else
(set! warning-table (cons (mcons no-warning-cache-key
(list (list constant1 value1)))
warning-table))])))))
(for ([x (in-list (cdr available-string-constant-sets))])
(check-one-way x first-string-constant-set)
(check-one-way first-string-constant-set x))
(define sp (open-output-string))
(for ([bad (in-list warning-table)])
(define lang-pair (mcar bad))
(define constants (mcdr bad))
(define lang1-name (car lang-pair))
(define lang2-name (cdr lang-pair))
(fprintf sp "WARNING: language ~a has but ~a does not:\n"
lang1-name
lang2-name)
(define sorted-constants
(sort constants string<?
#:key (λ (p) (symbol->string (car p)))
#:cache-keys? #t))
(for ([x (in-list sorted-constants)])
(fprintf sp " ~s\n" x))
(newline sp))
(with-handlers ([exn:fail? (lambda (x) (void))])
;; the output port may no longer be there, in which case
;; we just give up on printing
(eprintf "~a" (get-output-string sp))))))
;; env-var-set? : symbol -> boolean
;; returns #t if the user has requested this langage info.
;; If the environment variable is set to something that
;; isn't well-formed according to `read' you get all output
;; If the environment variable is set to a symbol (according to read)
;; you get that language. If it is set to a list of symbols
;; (again, according to read) you get those languages.
;; if it is set to anything else, you get all languages.
(define (env-var-set? lang)
(cond [(symbol? specific) (equal? lang specific)]
[(list? specific) (member lang specific)]
[else #t]))
(define env-var-set
(or (getenv "PLTSTRINGCONSTANTS")
(getenv "STRINGCONSTANTS")))
(define specific
(and env-var-set
(with-handlers ([exn:fail:read? (lambda (x) #t)])
(read (open-input-string env-var-set)))))
(define-for-syntax (check-name name-stx stx)
(define datum (syntax->datum name-stx))
(unless (symbol? datum)
(raise-syntax-error #f (format "expected name, got: ~s" datum) stx))
(define default-val (hash-ref english-ct:string-constants datum #f))
(unless default-val
(raise-syntax-error
#f
(format "~a is not a known string constant" datum)
stx)))
(define-syntax (string-constant stx)
(syntax-case stx ()
[(_ name)
(begin
(check-name #'name stx)
#'(dynamic-string-constant 'name))]))
(define-syntax (string-constants stx)
(syntax-case stx ()
[(_ name)
(begin
(check-name #'name stx)
#'(dynamic-string-constants 'name))]))
(define (this-language) language)
(define (all-languages) (map sc-language-name available-string-constant-sets))
|