multi player / per user playing and translation
This commit is contained in:
@@ -293,23 +293,29 @@
|
||||
(with-lock playback (lambda () (poll/locked! playback))))
|
||||
(loop)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Create playlist-aware playback for one network renderer.
|
||||
; pre : Device is a media renderer, callbacks are procedures, and
|
||||
; media-server is a running shared media-file-server.
|
||||
; post : A DLNA player and its state-monitor thread are running.
|
||||
; result : A playback adapter that publishes through the supplied server.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (make-dlna-playback device
|
||||
tracks
|
||||
update
|
||||
error
|
||||
#:port [port 8734]
|
||||
#:media-file-server media-server
|
||||
#:poll-seconds [poll-seconds 1])
|
||||
(define raw
|
||||
(make-dlna-player device
|
||||
#:port port
|
||||
#:path "/rkt-web-player/"))
|
||||
#:media-file-server media-server))
|
||||
(define playback
|
||||
(dlna-playback raw tracks update error 'off #f #f #f
|
||||
#f #f #f #f 0 #f #t #t #f
|
||||
(make-semaphore 1)))
|
||||
(set-dlna-playback-monitor!
|
||||
playback
|
||||
(thread (lambda () (monitor-loop playback poll-seconds))))
|
||||
(thread (λ () (monitor-loop playback poll-seconds))))
|
||||
playback)
|
||||
|
||||
(define (dlna-playback-play-index! playback index)
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
racket/path
|
||||
racket/port
|
||||
racket/string
|
||||
simple-log)
|
||||
simple-log
|
||||
"translate.rkt")
|
||||
|
||||
(provide (struct-out player-agent-runtime)
|
||||
make-player-agent-runtime)
|
||||
@@ -321,17 +322,12 @@
|
||||
(with-handlers
|
||||
((exn:fail:agent-denied?
|
||||
(lambda (exception)
|
||||
(define message
|
||||
(string-append
|
||||
"Deze playback agent is niet toegelaten door de server. "
|
||||
"Voeg het volgende applicatie-ID toe aan [playback-agents] "
|
||||
"in de server-INI:\n\n"
|
||||
app-id))
|
||||
(define message (format (tr 'denied-message) app-id))
|
||||
(warn-player-agent "Agent authorization refused: ~a"
|
||||
(exn-message exception))
|
||||
(set-agent-error! message)
|
||||
(status-callback
|
||||
"Niet geautoriseerd — applicatie-ID staat niet in de server-INI")
|
||||
(tr 'unauthorized-status))
|
||||
(unless authorization-notified?
|
||||
(set! authorization-notified? #t)
|
||||
(denied-callback message))
|
||||
@@ -344,7 +340,7 @@
|
||||
(exn-message exception))
|
||||
(set-agent-error! (exn-message exception))
|
||||
(status-callback
|
||||
(format "Niet verbonden: ~a" (exn-message exception)))
|
||||
(format (tr 'disconnected) (exn-message exception)))
|
||||
(when running
|
||||
(sleep 3)
|
||||
(poll-loop)))))
|
||||
@@ -352,7 +348,7 @@
|
||||
"/api/agent/register"
|
||||
(hasheq 'appId app-id 'name assigned-name))
|
||||
(clear-agent-error!)
|
||||
(status-callback "Verbonden")
|
||||
(status-callback (tr 'connected))
|
||||
(info-player-agent "Registered at ~a as ~a" server-url assigned-name)
|
||||
(let loop ()
|
||||
(when running
|
||||
@@ -390,7 +386,7 @@
|
||||
(define (start!)
|
||||
(unless running
|
||||
(set! running #t)
|
||||
(status-callback "Verbinden…")
|
||||
(status-callback (tr 'connecting))
|
||||
(set! worker (thread poll-loop))))
|
||||
|
||||
(define (stop!)
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
simple-log
|
||||
"player-agent-config.rkt"
|
||||
"player-agent-core.rkt"
|
||||
"player-agent-tray.rkt")
|
||||
"player-agent-tray.rkt"
|
||||
"translate.rkt")
|
||||
|
||||
(provide run-player-agent-gui)
|
||||
|
||||
@@ -69,7 +70,7 @@
|
||||
(define (show-denial! message)
|
||||
(queue-callback
|
||||
(lambda ()
|
||||
(message-box "Playback agent niet toegestaan"
|
||||
(message-box (tr 'denied-title)
|
||||
message
|
||||
frame
|
||||
'(ok stop)))
|
||||
@@ -97,13 +98,13 @@
|
||||
((and artist (not (string=? artist "")) title)
|
||||
(format "~a — ~a" artist title))
|
||||
(title title)
|
||||
(else "Geen track geselecteerd")))
|
||||
(else (tr 'no-track-selected))))
|
||||
(define prefix
|
||||
(cond
|
||||
((string=? state "playing") "Speelt")
|
||||
((string=? state "paused") "Gepauzeerd")
|
||||
((string=? state "starting") "Laden")
|
||||
((string=? state "stopped") "Gestopt")
|
||||
((string=? state "playing") (tr 'playing))
|
||||
((string=? state "paused") (tr 'paused))
|
||||
((string=? state "starting") (tr 'loading))
|
||||
((string=? state "stopped") (tr 'stopped))
|
||||
(else state)))
|
||||
(define position (hash-ref snapshot 'position 0))
|
||||
(define duration (hash-ref snapshot 'duration 'null))
|
||||
@@ -124,7 +125,7 @@
|
||||
"")
|
||||
(if (number? channels)
|
||||
(format "~a ~a" channels
|
||||
(if (= channels 1) "kanaal" "kanalen"))
|
||||
(tr (if (= channels 1) 'channel 'channels)))
|
||||
"")
|
||||
(if (and (string? format-name) (not (string=? format-name "")))
|
||||
format-name
|
||||
@@ -138,7 +139,7 @@
|
||||
(format " #~a" track-number)
|
||||
"")
|
||||
track-label)
|
||||
"Er wordt niets afgespeeld"))
|
||||
(tr 'no-track)))
|
||||
(send playback-details set-label (string-join details " · "))
|
||||
(send playback-filename
|
||||
set-label
|
||||
@@ -160,7 +161,7 @@
|
||||
(save-player-agent-config! config)
|
||||
(send name-field set-value next-name)
|
||||
((player-agent-runtime-reconnect! runtime) next-server next-name)
|
||||
(send connect-button set-label "Opnieuw verbinden"))
|
||||
(send connect-button set-label (tr 'reconnect)))
|
||||
|
||||
(define (shutdown!)
|
||||
(unless shutting-down?
|
||||
@@ -191,7 +192,7 @@
|
||||
|
||||
(set! frame
|
||||
(new agent-frame%
|
||||
(label "RKT Web Player Agent")
|
||||
(label (tr 'app-title))
|
||||
(width 560)
|
||||
(height 310)))
|
||||
(define panel
|
||||
@@ -199,13 +200,15 @@
|
||||
(parent frame)
|
||||
(alignment '(left top))))
|
||||
(set! server-field
|
||||
(input-field "RKT Web Player server"
|
||||
(input-field (tr 'server)
|
||||
(player-agent-config-server-url config)
|
||||
panel))
|
||||
(set! name-field
|
||||
(input-field "Naam" (player-agent-config-name config) panel))
|
||||
(input-field (tr 'name) (player-agent-config-name config) panel))
|
||||
(define id-field
|
||||
(input-field "Applicatie-ID" (player-agent-config-app-id config) panel))
|
||||
(input-field (tr 'application-id)
|
||||
(player-agent-config-app-id config)
|
||||
panel))
|
||||
;; Lock the editor, not the native widget. Disabled Windows controls render
|
||||
;; their label and text poorly on some display configurations.
|
||||
(send (send id-field get-editor) lock #t)
|
||||
@@ -213,13 +216,13 @@
|
||||
(define playback-panel
|
||||
(new group-box-panel%
|
||||
(parent panel)
|
||||
(label "Afspelen")
|
||||
(label (tr 'playback))
|
||||
(alignment '(left top))
|
||||
(stretchable-height #f)))
|
||||
(set! playback-message
|
||||
(new message%
|
||||
(parent playback-panel)
|
||||
(label "Er wordt niets afgespeeld")
|
||||
(label (tr 'no-track))
|
||||
(auto-resize #t)))
|
||||
(set! playback-details
|
||||
(new message%
|
||||
@@ -239,12 +242,12 @@
|
||||
(set! connect-button
|
||||
(new button%
|
||||
(parent controls)
|
||||
(label "Opslaan en verbinden")
|
||||
(label (tr 'save-connect))
|
||||
(callback (lambda (_button _event) (reconnect!)))))
|
||||
(set! status-message
|
||||
(new message%
|
||||
(parent controls)
|
||||
(label "Verbinden…")
|
||||
(label (tr 'connecting))
|
||||
(auto-resize #t)))
|
||||
|
||||
(set! playback-timer
|
||||
@@ -268,5 +271,5 @@
|
||||
|
||||
(send frame show #t)
|
||||
((player-agent-runtime-start! runtime))
|
||||
(send connect-button set-label "Opnieuw verbinden")
|
||||
(send connect-button set-label (tr 'reconnect))
|
||||
frame)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#lang racket/base
|
||||
|
||||
(require "translate.rkt")
|
||||
|
||||
;; SDL3 is deliberately loaded dynamically. The GUI agent keeps working
|
||||
;; without the optional Racket package and native SDL3 runtime.
|
||||
|
||||
@@ -20,11 +22,11 @@
|
||||
(define update-trays! (dynamic-require 'sdl3 'update-trays!))
|
||||
(define tray-destroy! (dynamic-require 'sdl3 'tray-destroy!))
|
||||
(sdl-init! '(video events))
|
||||
(define tray (make-tray #f "RKT Web Player Agent"))
|
||||
(define tray (make-tray #f (tr 'app-title)))
|
||||
(define menu (make-tray-menu tray))
|
||||
(define show-entry (insert-tray-entry! menu "RKT Web Player Agent openen"))
|
||||
(define show-entry (insert-tray-entry! menu (tr 'tray-open)))
|
||||
(insert-tray-entry! menu #f)
|
||||
(define quit-entry (insert-tray-entry! menu "Afsluiten"))
|
||||
(define quit-entry (insert-tray-entry! menu (tr 'quit)))
|
||||
(set-tray-entry-callback! show-entry (lambda (_) (show-window!)))
|
||||
(set-tray-entry-callback! quit-entry (lambda (_) (quit!)))
|
||||
(tray-controller
|
||||
|
||||
+611
-328
File diff suppressed because it is too large
Load Diff
+52
-1
@@ -11,7 +11,9 @@
|
||||
open-playlist-store
|
||||
close-playlist-store!
|
||||
load-user-playlists
|
||||
save-user-playlists!)
|
||||
save-user-playlists!
|
||||
load-user-language
|
||||
save-user-language!)
|
||||
|
||||
(struct persisted-tab (id name tracks) #:transparent)
|
||||
(struct playlist-store (keystore lock) #:transparent)
|
||||
@@ -19,6 +21,11 @@
|
||||
(define (user-playlists-key username)
|
||||
(format "playlists-for-~a" username))
|
||||
|
||||
(define (user-language-key username)
|
||||
(format "language-for-~a" username))
|
||||
|
||||
(define supported-language-names '("en" "nl" "de" "fr" "es"))
|
||||
|
||||
(define (track->datum item)
|
||||
(hasheq 'file (path->string (track-file item))
|
||||
'title (track-title item)
|
||||
@@ -116,6 +123,45 @@
|
||||
(ks-set! ks index-key ids))
|
||||
(void)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Load one user's persisted interface language.
|
||||
; pre : Store is #f or an open playlist store; username is normalized.
|
||||
; post : Store contents remain unchanged.
|
||||
; result : A supported ISO language name, or #f when none was saved.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (load-user-language store username)
|
||||
(and store
|
||||
(call-with-semaphore
|
||||
(playlist-store-lock store)
|
||||
(λ ()
|
||||
(define value
|
||||
(ks-get (playlist-store-keystore store)
|
||||
(user-language-key username)
|
||||
#f))
|
||||
(and (member value supported-language-names) value)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Persist one user's interface language.
|
||||
; pre : Store is #f or open, username is normalized, and language is one of
|
||||
; en, nl, de, fr, or es.
|
||||
; post : The user's language key contains language when a store exists.
|
||||
; result : Void.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (save-user-language! store username language)
|
||||
(unless (member language supported-language-names)
|
||||
(raise-argument-error
|
||||
'save-user-language!
|
||||
"one of en, nl, de, fr, or es"
|
||||
language))
|
||||
(when store
|
||||
(call-with-semaphore
|
||||
(playlist-store-lock store)
|
||||
(λ ()
|
||||
(ks-set! (playlist-store-keystore store)
|
||||
(user-language-key username)
|
||||
language))))
|
||||
(void))
|
||||
|
||||
(module+ test
|
||||
(require rackunit
|
||||
uuid/random)
|
||||
@@ -166,6 +212,11 @@
|
||||
(check-equal?
|
||||
(map persisted-tab-name (load-user-playlists store "local" libraries))
|
||||
'("Local"))
|
||||
(check-false (load-user-language store "hans"))
|
||||
(save-user-language! store "hans" "fr")
|
||||
(check-equal? (load-user-language store "hans") "fr")
|
||||
(check-exn exn:fail:contract?
|
||||
(λ () (save-user-language! store "hans" "it")))
|
||||
|
||||
;; Rewriting the user's GUID index durably removes the omitted playlist.
|
||||
(save-user-playlists!
|
||||
|
||||
@@ -119,6 +119,24 @@
|
||||
(request-jsexpr request)
|
||||
#:username (request-username request)))))
|
||||
|
||||
(define (preferences-handler request)
|
||||
(json-response
|
||||
(hasheq
|
||||
'language
|
||||
(or (player-user-language
|
||||
current-player
|
||||
#:username (request-username request))
|
||||
'null))))
|
||||
|
||||
(define (preferences-update-handler request)
|
||||
(with-handlers ((exn:fail? error-response))
|
||||
(define language (hash-ref (request-jsexpr request) 'language #f))
|
||||
(player-user-language!
|
||||
current-player
|
||||
language
|
||||
#:username (request-username request))
|
||||
(json-response (hasheq 'language language))))
|
||||
|
||||
(define (agent-register-handler request)
|
||||
(with-handlers
|
||||
((exn:fail:agent-denied? agent-error-response)
|
||||
@@ -190,6 +208,8 @@
|
||||
[("api" "auth" "logout") #:method "post" auth-logout-handler]
|
||||
[("api" "state") #:method "get" state-handler]
|
||||
[("api" "discover") #:method "post" discover-handler]
|
||||
[("api" "preferences") #:method "get" preferences-handler]
|
||||
[("api" "preferences") #:method "post" preferences-update-handler]
|
||||
[("api" "agent" "register") #:method "post" agent-register-handler]
|
||||
[("api" "agent" "poll") #:method "post" agent-poll-handler]
|
||||
[("api" "agent" "media" (string-arg) (string-arg))
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
#lang racket/base
|
||||
|
||||
(require racket/string)
|
||||
|
||||
(provide tr
|
||||
__
|
||||
languages
|
||||
set-lang!
|
||||
current-lang)
|
||||
|
||||
(define translation-map
|
||||
(hasheq
|
||||
'en
|
||||
(hasheq
|
||||
'app-title "RKT Web Player Agent"
|
||||
'server "RKT Web Player server"
|
||||
'name "Name"
|
||||
'application-id "Application ID"
|
||||
'playback "Playback"
|
||||
'no-track "Nothing is playing"
|
||||
'no-track-selected "No track selected"
|
||||
'save-connect "Save and connect"
|
||||
'reconnect "Reconnect"
|
||||
'connecting "Connecting…"
|
||||
'connected "Connected"
|
||||
'denied-title "Playback agent not allowed"
|
||||
'denied-message "This playback agent is not allowed by the server. Add the following application ID to [playback-agents] in the server INI:\n\n~a"
|
||||
'unauthorized-status "Not authorized — application ID is not in the server INI"
|
||||
'disconnected "Not connected: ~a"
|
||||
'playing "Playing"
|
||||
'paused "Paused"
|
||||
'loading "Loading"
|
||||
'stopped "Stopped"
|
||||
'channel "channel"
|
||||
'channels "channels"
|
||||
'tray-open "Open RKT Web Player Agent"
|
||||
'quit "Quit")
|
||||
'nl
|
||||
(hasheq
|
||||
'app-title "RKT Web Player Agent"
|
||||
'server "RKT Web Player server"
|
||||
'name "Naam"
|
||||
'application-id "Applicatie-ID"
|
||||
'playback "Afspelen"
|
||||
'no-track "Er wordt niets afgespeeld"
|
||||
'no-track-selected "Geen track geselecteerd"
|
||||
'save-connect "Opslaan en verbinden"
|
||||
'reconnect "Opnieuw verbinden"
|
||||
'connecting "Verbinden…"
|
||||
'connected "Verbonden"
|
||||
'denied-title "Playback agent niet toegestaan"
|
||||
'denied-message "Deze playback agent is niet toegelaten door de server. Voeg het volgende applicatie-ID toe aan [playback-agents] in de server-INI:\n\n~a"
|
||||
'unauthorized-status "Niet geautoriseerd — applicatie-ID staat niet in de server-INI"
|
||||
'disconnected "Niet verbonden: ~a"
|
||||
'playing "Speelt"
|
||||
'paused "Gepauzeerd"
|
||||
'loading "Laden"
|
||||
'stopped "Gestopt"
|
||||
'channel "kanaal"
|
||||
'channels "kanalen"
|
||||
'tray-open "RKT Web Player Agent openen"
|
||||
'quit "Afsluiten")
|
||||
'de
|
||||
(hasheq
|
||||
'app-title "RKT Web Player Agent"
|
||||
'server "RKT Web Player Server"
|
||||
'name "Name"
|
||||
'application-id "Anwendungs-ID"
|
||||
'playback "Wiedergabe"
|
||||
'no-track "Keine Wiedergabe"
|
||||
'no-track-selected "Kein Titel ausgewählt"
|
||||
'save-connect "Speichern und verbinden"
|
||||
'reconnect "Neu verbinden"
|
||||
'connecting "Verbinden…"
|
||||
'connected "Verbunden"
|
||||
'denied-title "Playback-Agent nicht zugelassen"
|
||||
'denied-message "Dieser Playback-Agent ist vom Server nicht zugelassen. Fügen Sie die folgende Anwendungs-ID unter [playback-agents] in die Server-INI ein:\n\n~a"
|
||||
'unauthorized-status "Nicht autorisiert — Anwendungs-ID fehlt in der Server-INI"
|
||||
'disconnected "Nicht verbunden: ~a"
|
||||
'playing "Wiedergabe"
|
||||
'paused "Pausiert"
|
||||
'loading "Laden"
|
||||
'stopped "Gestoppt"
|
||||
'channel "Kanal"
|
||||
'channels "Kanäle"
|
||||
'tray-open "RKT Web Player Agent öffnen"
|
||||
'quit "Beenden")
|
||||
'fr
|
||||
(hasheq
|
||||
'app-title "Agent RKT Web Player"
|
||||
'server "Serveur RKT Web Player"
|
||||
'name "Nom"
|
||||
'application-id "ID d’application"
|
||||
'playback "Lecture"
|
||||
'no-track "Aucune lecture en cours"
|
||||
'no-track-selected "Aucune piste sélectionnée"
|
||||
'save-connect "Enregistrer et connecter"
|
||||
'reconnect "Reconnecter"
|
||||
'connecting "Connexion…"
|
||||
'connected "Connecté"
|
||||
'denied-title "Agent de lecture non autorisé"
|
||||
'denied-message "Cet agent de lecture n’est pas autorisé par le serveur. Ajoutez l’ID d’application suivant à [playback-agents] dans le fichier INI du serveur :\n\n~a"
|
||||
'unauthorized-status "Non autorisé — l’ID d’application est absent du fichier INI du serveur"
|
||||
'disconnected "Non connecté : ~a"
|
||||
'playing "Lecture"
|
||||
'paused "En pause"
|
||||
'loading "Chargement"
|
||||
'stopped "Arrêté"
|
||||
'channel "canal"
|
||||
'channels "canaux"
|
||||
'tray-open "Ouvrir l’agent RKT Web Player"
|
||||
'quit "Quitter")
|
||||
'es
|
||||
(hasheq
|
||||
'app-title "Agente de RKT Web Player"
|
||||
'server "Servidor RKT Web Player"
|
||||
'name "Nombre"
|
||||
'application-id "ID de aplicación"
|
||||
'playback "Reproducción"
|
||||
'no-track "No se está reproduciendo nada"
|
||||
'no-track-selected "No hay ninguna pista seleccionada"
|
||||
'save-connect "Guardar y conectar"
|
||||
'reconnect "Volver a conectar"
|
||||
'connecting "Conectando…"
|
||||
'connected "Conectado"
|
||||
'denied-title "Agente de reproducción no permitido"
|
||||
'denied-message "El servidor no permite este agente de reproducción. Añade el siguiente ID de aplicación a [playback-agents] en el INI del servidor:\n\n~a"
|
||||
'unauthorized-status "No autorizado — el ID de aplicación no está en el INI del servidor"
|
||||
'disconnected "Sin conexión: ~a"
|
||||
'playing "Reproduciendo"
|
||||
'paused "En pausa"
|
||||
'loading "Cargando"
|
||||
'stopped "Detenido"
|
||||
'channel "canal"
|
||||
'channels "canales"
|
||||
'tray-open "Abrir el agente de RKT Web Player"
|
||||
'quit "Salir")))
|
||||
|
||||
(define (system-language)
|
||||
(define language-name
|
||||
(string-downcase (format "~a" (system-language+country))))
|
||||
(define short-name (car (regexp-split #px"[-_]" language-name)))
|
||||
(define candidate (string->symbol short-name))
|
||||
(if (hash-has-key? translation-map candidate) candidate 'en))
|
||||
|
||||
(define language (system-language))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Return the supported language symbols and their native names.
|
||||
; pre : None.
|
||||
; post : Translation state remains unchanged.
|
||||
; result : An association list suitable for a language selector.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (languages)
|
||||
'((en "English")
|
||||
(nl "Nederlands")
|
||||
(de "Deutsch")
|
||||
(fr "Français")
|
||||
(es "Español")))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Select the language used by tr and __.
|
||||
; pre : Value is one of the symbols returned by languages.
|
||||
; post : Subsequent translations use value.
|
||||
; result : Void.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (set-lang! value)
|
||||
(unless (hash-has-key? translation-map value)
|
||||
(raise-argument-error 'set-lang! "supported language symbol" value))
|
||||
(set! language value))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Report the active translation language.
|
||||
; pre : None.
|
||||
; post : Translation state remains unchanged.
|
||||
; result : A supported language symbol.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (current-lang)
|
||||
language)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Translate an application string identifier.
|
||||
; pre : Id is a symbol.
|
||||
; post : Translation state remains unchanged.
|
||||
; result : The active translation, its English fallback, or the identifier.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (tr id)
|
||||
(hash-ref (hash-ref translation-map language)
|
||||
id
|
||||
(λ ()
|
||||
(hash-ref (hash-ref translation-map 'en)
|
||||
id
|
||||
(λ () (symbol->string id))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Provide the conventional short alias used by rktplayer GUI code.
|
||||
; pre : Id is a symbol.
|
||||
; post : Translation state remains unchanged.
|
||||
; result : The same translated string as tr.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (__ id)
|
||||
(tr id))
|
||||
|
||||
(module+ test
|
||||
(require rackunit)
|
||||
|
||||
(define original-language (current-lang))
|
||||
(dynamic-wind
|
||||
void
|
||||
(λ ()
|
||||
(set-lang! 'nl)
|
||||
(check-equal? (tr 'connected) "Verbonden")
|
||||
(set-lang! 'de)
|
||||
(check-equal? (__ 'quit) "Beenden")
|
||||
(check-equal? (tr 'unknown-translation) "unknown-translation")
|
||||
(check-exn exn:fail:contract? (λ () (set-lang! 'xx))))
|
||||
(λ () (set-lang! original-language))))
|
||||
Reference in New Issue
Block a user