diff options
51 files changed, 2059 insertions, 487 deletions
@@ -2,3 +2,4 @@ /html coverage obj-* +localization @@ -20,9 +20,15 @@ GUILE_C_FLAGS = -Lmodule \ -Wmacro-use-before-definition -Warity-mismatch \ -Wduplicate-case-datum -Wbad-case-datum -all: go_files README static +# All po-files inside po/, except new.po, and hidden files +PO_FILES = $(shell find po -type f -name \*.po -and -not -name new.po -and -not -name .\*) +LOCALIZATIONS = $(PO_FILES:po/%.po=localization/%/LC_MESSAGES/calp.mo) + +all: go_files README static $(LOCALIZATIONS) $(MAKE) -C doc/ref +XGETTEXT_FLAGS = --from-code=UTF-8 --add-comments --indent -k_ + static: $(MAKE) -C static @@ -34,6 +40,16 @@ obj-$(GUILE_VERSION)/%.go: module/%.scm # automatically compile everything before they run. go_files: $(GO_FILES) +po/%.po: $(SCM_FILES) + xgettext $(XGETTEXT_FLAGS) --output $@ -L scheme $^ --join-existing --omit-header + +po/new.po: $(SCM_FILES) + xgettext $(XGETTEXT_FLAGS) --output $@ -L scheme $^ + +localization/%/LC_MESSAGES/calp.mo: po/%.po + -@mkdir -p $(shell dirname $@) + msgfmt --check -o $@ $< + clean: -$(MAKE) -C static clean -rm -r obj-* diff --git a/module/calp/entry-points/benchmark.scm b/module/calp/entry-points/benchmark.scm index 5db9b9df..31ea958a 100644 --- a/module/calp/entry-points/benchmark.scm +++ b/module/calp/entry-points/benchmark.scm @@ -5,6 +5,8 @@ :use-module (hnh util options) :use-module ((srfi srfi-41) :select (stream->list)) + :use-module (calp translation) + :use-module ((vcomponent util instance methods) :select (get-event-set)) :autoload (vcomponent util instance) (global-event-object) @@ -15,9 +17,9 @@ (define opt-spec `((enable-output (single-char #\o) (description - "Output is be default supressed, since many fields contain way to much data " - "to read. This turns it on again.")) - (help (single-char #\h) (description "Print this help.")))) + ,(_ "Output is by default supressed, since many fields contain way to much data to read. This turns it on again.") + )) + (help (single-char #\h) (description ,(_ "Print this help."))))) (define (main args) diff --git a/module/calp/entry-points/convert.scm b/module/calp/entry-points/convert.scm index 1ce33d9c..d416b004 100644 --- a/module/calp/entry-points/convert.scm +++ b/module/calp/entry-points/convert.scm @@ -4,18 +4,19 @@ :use-module (hnh util options) :use-module (ice-9 getopt-long) :use-module (sxml simple) + :use-module (calp translation) ) (define opt-spec - `((from (single-char #\f) (value (options "xcal" "ical")) - (description "Input format (infered from " (i "infile") ")")) + `((from (single-char #\f) (value (options "xcal" "ical")) + (description ,(xml->sxml (_ "<group>Input format (otherwise infered from <i>infile</i>)</group>")))) (to (single-char #\t) (value (options "xcal" "ical")) - (description "Output format (infered from " (i "outfile") ")")) - (infile (value #t) (single-char #\i) (description "Input file")) - (outfile (value #t) (single-char #\o) (description "Output file")) - (help (single-char #\h) (description "Print this help.")))) + (description ,(xml->sxml (_ "<group>Output format (otherwise infered from <i>outfile</i>)</group>")))) + (infile (value #t) (single-char #\i) (description ,(_ "Input file"))) + (outfile (value #t) (single-char #\o) (description ,(_ "Output file"))) + (help (single-char #\h) (description ,(_ "Print this help."))))) (define (filename-to-type filename) diff --git a/module/calp/entry-points/html.scm b/module/calp/entry-points/html.scm index 04b09cf3..8478aa6c 100644 --- a/module/calp/entry-points/html.scm +++ b/module/calp/entry-points/html.scm @@ -19,9 +19,10 @@ :use-module ((vcomponent util instance methods) :select (get-calendars get-event-set)) - :use-module ((sxml simple) :select (sxml->xml)) + :use-module ((sxml simple) :select (sxml->xml xml->sxml)) :use-module ((sxml transformations) :select (href-transformer)) :use-module ((xdg basedir) :prefix xdg-) + :use-module (calp translation) :autoload (vcomponent util instance) (global-event-object) ) @@ -29,39 +30,35 @@ (define opt-spec `((from (value #t) (single-char #\F) - (description "Start date of output.") + (description ,(_ "Start date of output.")) ) (count (value #t) - (description "How many pages should be rendered." - "If --style=" (b "week") " and --from=" (b "2020-04-27") - " then --count=" (b 4) " would render the four pages " - "2020-04-27, 2020-05-04, 2020-05-11, and 2020-05-25. " - "Defaults to 12 to give a whole year when --style=" (b "month") "." - )) + (description ,(xml->sxml (_ "<group>How many pages should be rendered. +If --style=<b>week</b> and --from=<b>2020-04-27</b>; +then --count=<b>4</b> would render the four pages +2020-04-27, 2020-05-04, 2020-05-11, and 2020-05-25. +Defaults to 12 to give a whole year when --style=<b>month</b></group>")))) (target (single-char #\t) (value #t) - (description "Directory where html files should end up. Default to " (b "./html"))) + (description ,(xml->sxml (_ "<group>Directory where html files should end up. Default to <b>./html</b></group>")))) (style (value #t) (predicate ,(lambda (v) (memv (string->symbol v) '(small wide week table)))) - (description "How the body of the HTML page should be layed out. " - (br) (b "week") - " gives a horizontally scrolling page with 7 elements, " - "where each has events graphically laid out hour by hour." - (br) (b "table") - " gives a month in overview as a table. Each block contains " - "the events for the given day, in order of start time. They are " - "however not graphically sized. " - (br) (b "wide") - " is the same as week, but gives a full month.") - ) + (description ,(xml->sxml (_ "<group>How the body of the HTML page should be layed out. +<br/><b>week</b> +gives a horizontally scrolling page with 7 elements, where each has events +graphically laid out hour by hour. +<br/><b>table</b> +gives a month in overview as a table. Each block contains the events for the +given day, in order of start time. They are however not graphically sized. +<br/><b>wide</b> +is the same as week, but gives a full month.</group>")))) (standalone - (description "Creates a standalone document instead of an HTML fragment " - "for embedding in a larger page. Currently only applies to the " - (i "small") "style")) + (description ,(xml->sxml (_ "<group>Creates a standalone document instead of an HTML fragment +for embedding in a larger page. Currently only applies to the <i>small</i> style</group>")))) - (help (single-char #\h) (description "Print this help.")))) + (help (single-char #\h) (description ,(_ "Print this help."))))) @@ -115,7 +112,7 @@ (stream-for-each (lambda (start-date) (define fname (path-append target-directory (date->string start-date "~1.xml"))) - (format (current-error-port) "Writing to [~a]~%" fname) + (format (current-error-port) (_ "Writing to [~a]~%") fname) (with-output-to-file fname (lambda () (sxml->xml (re-root-static (apply html-generate @@ -183,7 +180,7 @@ pre-start: (start-of-week start) post-end: (end-of-week (end-of-month start)))] [else - (scm-error 'misc-error "html-main" "Unknown html style: ~a" (list style) #f)]) + (scm-error 'misc-error "html-main" (_ "Unknown html style: ~a") (list style) #f)]) - ((@ (calp util time) report-time!) "all done") + ((@ (calp util time) report-time!) (_ "all done")) ) diff --git a/module/calp/entry-points/ical.scm b/module/calp/entry-points/ical.scm index 938b0b35..e164c340 100644 --- a/module/calp/entry-points/ical.scm +++ b/module/calp/entry-points/ical.scm @@ -5,14 +5,16 @@ :use-module (vcomponent formats ical output) :use-module (ice-9 getopt-long) :use-module (datetime) + :use-module (calp translation) + :use-module (calp translation) ) (define opt-spec - '((from (value #t) (single-char #\F)) + `((from (value #t) (single-char #\F)) (to (value #t) (single-char #\T) - (description "Returns all elements between these two dates.")) + (description ,(_ "Returns all elements between these two dates."))) (help (single-char #\h) - (description "Print this help.")))) + (description ,(_ "Print this help."))))) (define (main args) (define opts (getopt-long args (getopt-opt opt-spec))) diff --git a/module/calp/entry-points/import.scm b/module/calp/entry-points/import.scm index 213a720d..cb8b9485 100644 --- a/module/calp/entry-points/import.scm +++ b/module/calp/entry-points/import.scm @@ -12,16 +12,17 @@ :use-module (vcomponent) ;; :use-module ((vcomponent formats ical parse) :select (parse-cal-path)) :use-module ((vcomponent util parse-cal-path) :select (parse-cal-path)) + :use-module (calp translation) :autoload (vcomponent util instance) (global-event-object) ) (define options - '((calendar (value #t) (single-char #\c) - (description "Name of calendar to import into")) + `((calendar (value #t) (single-char #\c) + (description ,(_ "Name of calendar to import into"))) (file (value #t) (single-char #\f) - (description "ics file to import")) + (description ,(_ "ics file to import"))) (help (single-char #\h) - (description "Print this help.")))) + (description ,(_ "Print this help."))))) (define (main args) (define opts (getopt-long args (getopt-opt options))) @@ -40,27 +41,24 @@ (get-calendars global-event-object))))) (unless calendar - (format (current-error-port) "No calendar named ~s~%" cal-name) + (format (current-error-port) (_ "No calendar named ~s~%") cal-name) (throw 'return)) (let ((new-events (parse-cal-path fname))) - (format #t "About to the following ~a events into ~a~%~{~a~^~%~}~%" + (format #t (_ "About to import the following ~a events into ~a~%") (length (children new-events)) - (prop calendar 'NAME) + (prop calendar 'NAME)) + (format #t "~{~a~^~%~}~%" (map (extract 'SUMMARY) (children new-events))) - (format #t "Continue? [Y/n] ") + (format #t (_ "Continue? [Y/n] ")) - (let loop ((c #\space)) - (case c - [(#\n #\N) (throw 'return)] - [(#\y #\Y) (map (lambda (e) - (add-event calendar e) - (save-event e)) - (children new-events))] - [else - (let ((line (read-line))) - (loop (if (string-null? line) - #\Y (string-ref line 0))))])) - ))) + (let loop ((line (read-line))) + (case (if (string-null? line) 'yes (yes-no-check line)) + [(no) (throw 'return)] + [(yes) (map (lambda (e) + (add-event calendar e) + (save-event e)) + (children new-events))] + [else (loop line (read-line))]))))) diff --git a/module/calp/entry-points/server.scm b/module/calp/entry-points/server.scm index 4c5637f3..1888a8a7 100644 --- a/module/calp/entry-points/server.scm +++ b/module/calp/entry-points/server.scm @@ -7,6 +7,8 @@ :use-module (ice-9 getopt-long) :use-module (ice-9 format) + :use-module (calp translation) + :use-module (sxml simple) :use-module ((calp server server) :select (start-server)) @@ -14,25 +16,23 @@ (define options - '((port (value #t) (single-char #\p) - (description "Bind to TCP port, defaults to " (i 8080) "." - (br) "Can also be set through the config variable " - (i "port") ".")) + `((port (value #t) (single-char #\p) + (description ,(xml->sxml (_ "<group>Bind to TCP port, defaults to <i>8080</i>. +<br/>Can also be set through the config variable +<i>port</i>.</group>")))) (addr (value #t) - - (description "Address to use, defaults to " (i "0.0.0.0") - " for IPv4, and " (i "[::]") " for IPv6.") - ) + (description ,(xml->sxml (_ "<group>Address to use, defaults to <i>0.0.0.0</i> for IPv4, +and <i>[::]</i> for IPv6</group>")))) ;; numbers as single-char doesn't work. - (six (description "Use IPv6.")) - (four (description "Use IPv4.")) - (sigusr (description "Reload events on SIGUSR1")) + (six (description ,(_ "Use IPv6."))) + (four (description ,(_ "Use IPv4."))) + (sigusr (description ,(_ "Reload events on SIGUSR1"))) (help (single-char #\h) - (description "Print this help.")))) + (description ,(_ "Print this help."))))) (define-config port 8080 - description: "Port to which the web server should bind.") + description: (_ "Port to which the web server should bind.")) (define-public (main args) @@ -61,18 +61,22 @@ "::" "0.0.0.0"))) (when (option-ref opts 'sigusr #f) - (display "Listening for SIGUSR1\n" (current-error-port)) + (format (current-error-port) (_ "Listening for SIGUSR1~%")) ;; NOTE this uses the main thread, and does therefore block HTTP requests ;; while reloading. However, it appears to not cause any race conditions. (sigaction SIGUSR1 (lambda _ - (display "Received SIGUSR1, reloading calendars\n" - (current-error-port)) + (format (current-error-port) (_ "Received SIGUSR1, reloading calendars~%")) ((@ (vcomponent util instance) reload))))) - (format #t "Starting server on ~a:~a~%I'm ~a, runing from ~a~%" + ;; Arguments are + ;; IP-address which we bind to + ;; Port which we listen to + ;; PID of this process + ;; PWD of this process + (format #t (_ "Starting server on ~a:~a~%I'm ~a, runing from ~a~%") addr port (getpid) (getcwd)) diff --git a/module/calp/entry-points/terminal.scm b/module/calp/entry-points/terminal.scm index b0be318c..dd35b8f3 100644 --- a/module/calp/entry-points/terminal.scm +++ b/module/calp/entry-points/terminal.scm @@ -6,12 +6,13 @@ :use-module (datetime) :use-module (vulgar) :use-module (hnh util options) + :use-module (calp translation) ) (define options - '((date (value #t) (single-char #\d) - (description "Which date to start on.")) - (help (single-char #\t) (description "Print this help.")) + `((date (value #t) (single-char #\d) + (description ,(_ "Which date to start on."))) + (help (single-char #\t) (description ,(_ "Print this help."))) )) (define (main args) diff --git a/module/calp/entry-points/text.scm b/module/calp/entry-points/text.scm index 0a5744b3..921afb80 100644 --- a/module/calp/entry-points/text.scm +++ b/module/calp/entry-points/text.scm @@ -4,16 +4,18 @@ :use-module (ice-9 getopt-long) :use-module (hnh util io) :use-module (hnh util options) + :use-module (calp translation) + :use-module (sxml simple) ) (define options - '((width (value #t) (single-char #\w) - (description "Width of written text, defaults to 70 chars.")) + `((width (value #t) (single-char #\w) + (description ,(_ "Width of written text, defaults to 70 chars."))) (file (value #t) (single-char #\f) - (description "Read from " (i "file") " instead of standard input.")) + (description ,(xml->sxml (_ "<group>Read from <i>file</i> instead of standard input.</group>")))) (help (single-char #\h) - (description "Prints this help.")))) + (description ,(_ "Prints this help."))))) (define (main args) (define opts (getopt-long args (getopt-opt options))) @@ -24,6 +26,9 @@ (for-each (lambda (l) (display l) (newline)) (flow-text - (with-input-from-port (open-input-port (option-ref opts 'file "-")) + (with-input-from-port (let ((fname (option-ref opts 'file "-"))) + (if (string=? fname "-") + (current-input-port) + (open-input-file fname))) (@ (ice-9 rdelim) read-string)) #:width (or (string->number (option-ref opts 'width "")) 70)))) diff --git a/module/calp/entry-points/tidsrapport.scm b/module/calp/entry-points/tidsrapport.scm index 5ff43cf7..a05c3b78 100644 --- a/module/calp/entry-points/tidsrapport.scm +++ b/module/calp/entry-points/tidsrapport.scm @@ -42,6 +42,8 @@ :use-module (hnh util) :use-module (hnh util options) :use-module (ice-9 getopt-long) + :use-module (calp translation) + :use-module (sxml simple) :use-module (datetime) ) @@ -165,20 +167,20 @@ trailer ) (define opt-spec - '((pdf (value #t) - (description "Input pdf fill")) + `((pdf (value #t) + (description ,(_ "Input pdf file"))) (output (single-char #\o) (value optional) - (description "Output file")) + (description ,(_ "Output file"))) (data (value optional) - (description "Static data to fill fields with") + (description ,(_ "Static data to fill fields with")) ) (template (value optional) - (description "Map between real field names and human readable names." (br) - "If data is given, but not trans, then data is assumed to be in a correct format")) + (description ,(xml->sxml (_ "<group>Map between real field names and human readable names.<br/> +If data is given, but not trans, then data is assumed to be in a correct format</group>")))) (search (value #t) (description - "Search term for dynamic filling. Supports basic globbing")))) + ,(_ "Search term for dynamic filling. Supports basic globbing"))))) (define (parse-search str) (cond [(string-match "\\{(.*)\\}" str) @@ -204,7 +206,7 @@ trailer (define template (call-with-input-file (or (option-ref opts 'template #f) - (error "Template required")) + (error (_ "Template required"))) read)) (define prepared-data @@ -232,9 +234,9 @@ trailer (define days (let ((days (assoc-ref group 'days))) (cond ((not (list? days)) - (error "Needs list, not pair")) + (error (_ "Needs list, not pair"))) ((null? days) - (error "Need more days")) + (error (_ "Need more days"))) ((and (list? (car days)) (eqv? '- (caar days))) (map (lambda (s) (string-append prefix (->string s))) (iota (1+ (- (list-ref (car days) 2) @@ -250,7 +252,7 @@ trailer ,@(build-alist work-hours days) (,sum ,(apply + work-hours)))) (or (assoc-ref template 'groups) - (error "Groups required in template")) + (error (_ "Groups required in template"))) search))) (define report diff --git a/module/calp/html/caltable.scm b/module/calp/html/caltable.scm index dd2d4b03..77580844 100644 --- a/module/calp/html/caltable.scm +++ b/module/calp/html/caltable.scm @@ -1,4 +1,5 @@ (define-module (calp html caltable) + :use-module (hnh util) :use-module (calp html util) :use-module (datetime) @@ -35,6 +36,7 @@ ;; making the text red for all holidays, or creating a yellow background ;; for events from a specific source. (time (@ (datetime ,(date->string date "~Y-~m-~d"))) + ;; TODO should this field be translated? ,(day date))))) (define month-start (start-of-month start-date)) @@ -49,11 +51,13 @@ ;; top row, names of week days ,@(map (lambda (d) `(div (@ (class "column-head")) + ;; TODO this SHOULD be translated ,(string-titlecase (week-day-name d 2)))) (weekday-list)) ;; left columun, week numbers ,@(map (lambda (v) `(div (@ (class "row-head")) ,v)) + ;; TODO translate this (map week-number (stream->list (stream-take-while (lambda (s) (date<= s post-end)) diff --git a/module/calp/html/components.scm b/module/calp/html/components.scm index 77a2a520..6642b1fe 100644 --- a/module/calp/html/components.scm +++ b/module/calp/html/components.scm @@ -2,6 +2,7 @@ :use-module (hnh util) :use-module (ice-9 curried-definitions) :use-module (ice-9 match) + :use-module (calp translation) :export (xhtml-doc) ) @@ -58,7 +59,7 @@ rest: args) (when (and onclick href) (scm-error 'wrong-type-arg "btn" - "href and onclick are mutually exclusive. href = ~s, onclick = ~s." + (_ "href and onclick are mutually exclusive. href = ~s, onclick = ~s.") (list href onclick) #f)) diff --git a/module/calp/html/config.scm b/module/calp/html/config.scm index 6bd1e0ec..08a4b2e8 100644 --- a/module/calp/html/config.scm +++ b/module/calp/html/config.scm @@ -1,11 +1,12 @@ (define-module (calp html config) :use-module (hnh util) :use-module (calp util config) + :use-module (calp translation) ) (define-public debug (make-parameter #f)) (define-config debug #f - description: "Places the generated thingy in debug mode" + description: (_ "Places the generated thingy in debug mode") post: debug) @@ -13,6 +14,6 @@ ;;; but this works for the time being. (define-public edit-mode (make-parameter #t)) (define-config edit-mode #t - description: "Makes the document editable" + description: (_ "Makes the document editable") post: edit-mode) diff --git a/module/calp/html/util.scm b/module/calp/html/util.scm index 54c92e92..affaf5d2 100644 --- a/module/calp/html/util.scm +++ b/module/calp/html/util.scm @@ -1,5 +1,6 @@ (define-module (calp html util) - :use-module (hnh util)) + :use-module (hnh util) + :use-module (calp translation)) (define-public (date-link date) @@ -31,6 +32,6 @@ #xFF)) "#000000" "#FFFFFF"))) (lambda args - (format (current-error-port) "Error calculating foreground color?~%~s~%" args) + (format (current-error-port) (_ "Error calculating foreground color?~%~s~%") args) "#FF0000" ))) diff --git a/module/calp/html/vcomponent.scm b/module/calp/html/vcomponent.scm index c832ea93..5c92e1e7 100644 --- a/module/calp/html/vcomponent.scm +++ b/module/calp/html/vcomponent.scm @@ -24,6 +24,7 @@ :use-module (calp util config) :use-module ((base64) :select (base64encode)) :use-module (ice-9 format) + :use-module (calp translation) ) (define-config summary-filter (lambda (_ a) a) @@ -68,10 +69,13 @@ "unknown"))))) (time ,(let ((dt (prop event 'DTSTART))) (if (datetime? dt) - (datetime->string dt "~Y-~m-~d ~H:~M") - (date->string dt "~Y-~m-~d" )))) + ;; Compact event list date + time + (datetime->string dt (_ "~Y-~m-~d ~H:~M")) + ;; Compact event list date only + (date->string dt (_ "~Y-~m-~d") )))) (a (@ (href ,(date->string (as-date (prop event 'DTSTART)) "/week/~Y-~m-~d.html"))) - "View 📅") + ;; Button for viewing calendar, accompanied by a calendar icon + ,(_ "View") " 📅") (span ,(prop event 'SUMMARY))))) (cons (calendar-styles calendars) @@ -109,6 +113,7 @@ (data-property "summary")) ,(prop ev 'SUMMARY)))) (div + ;; TODO localize this? ,(call-with-values (lambda () (fmt-time-span ev)) (case-lambda [(start) `(div (time (@ (class "dtstart") @@ -141,7 +146,7 @@ (div (@ (class "fields")) ,(when (and=> (prop ev 'LOCATION) (negate string-null?)) - `(div (b "Plats: ") + `(div (b ,(_ "Location: ")) (div (@ (class "location") (data-property "location")) ,(string-map (lambda (c) (if (char=? c #\,) #\newline c)) (prop ev 'LOCATION))))) @@ -218,8 +223,10 @@ ,@(format-recurrence-rule ev))) ,(when (prop ev 'LAST-MODIFIED) - `(div (@ (class "last-modified")) "Senast ändrad " - ,(datetime->string (prop ev 'LAST-MODIFIED) "~1 ~H:~M")))) + `(div (@ (class "last-modified")) ,(_ "Last modified") " " + ,(datetime->string (prop ev 'LAST-MODIFIED) + ;; Last modified datetime + (_ "~1 ~H:~M"))))) )))) @@ -229,7 +236,9 @@ (define-public (fmt-day day) (let* (((date . events) day)) `(section (@ (class "text-day")) - (header (h2 ,(let ((s (date->string date "~Y-~m-~d"))) + (header (h2 ,(let ((s (date->string date + ;; Header for sidebar day + (_ "~Y-~m-~d")))) `(a (@ (href "#" ,s) (class "hidelink")) ,s)))) ,@(stream->list @@ -314,12 +323,13 @@ ;; TODO possibly unused? (define (repeat-info event) `(div (@ (class "eventtext")) - (h2 "Upprepningar") + (h2 ,(_ "Recurrences")) (table (@ (class "recur-components")) ,@((@@ (vcomponent recurrence internal) map-fields) (lambda (key value) `(tr (@ (class ,key)) (th ,key) (td + ;; TODO Should these date string be translated? ,(case key ((wkst) (week-day-name value)) ((until) (if (date? value) @@ -364,6 +374,7 @@ `(select (@ ,@args) (option "-") ,@(map (lambda (x) `(option (@ (value ,(car x))) ,(cadr x))) + ;; TODO translate '((MO "Monday") (TU "Tuesday") (WE "Wednesday") @@ -383,7 +394,8 @@ (div (@ (class " eventtext edit-tab ")) (form (@ (class "edit-form")) (select (@ (class "calendar-selection")) - (option "- Choose a Calendar -") + ;; NOTE flytta "muffarna" utanför + (option ,(_ "- Choose a Calendar -")) ,@(let ((dflt (get-config 'default-calendar))) (map (lambda (calendar) (define name (prop calendar 'NAME)) @@ -393,7 +405,7 @@ ,name)) calendars))) (h3 (input (@ (type "text") - (placeholder "Sammanfattning") + (placeholder ,(_ "Summary")) (name "summary") (required) (data-property "summary") ; (value ,(prop ev 'SUMMARY)) @@ -402,24 +414,24 @@ (div (@ (class "timeinput")) ,@(with-label - "Starttid" + (_ "Start time") '(date-time-input (@ (name "dtstart") (data-property "dtstart") ))) ,@(with-label - "Sluttid" + (_ "End time") '(date-time-input (@ (name "dtend") (data-property "dtend")))) (div (@ (class "checkboxes")) ,@(with-label - "Heldag?" + (_ "Whole day?") `(input (@ (type "checkbox") (name "wholeday") ))) ,@(with-label - "Upprepande?" + (_ "Recurring?") `(input (@ (type "checkbox") (name "has_repeats") )))) @@ -427,8 +439,8 @@ ) ,@(with-label - "Plats" - `(input (@ (placeholder "Plats") + (_ "Location") + `(input (@ (placeholder ,(_ "Location")) (name "location") (type "text") (data-property "location") @@ -436,20 +448,20 @@ ))) ,@(with-label - "Beskrivning" - `(textarea (@ (placeholder "Beskrivning") + (_ "Description") + `(textarea (@ (placeholder ,(_ "Description")) (data-property "description") (name "description")) ; ,(prop ev 'DESCRIPTION) )) ,@(with-label - "Kategorier" + (_ "Categories") `(input-list (@ (name "categories") (data-property "categories")) (input (@ (type "text") - (placeholder "Kattegori"))))) + (placeholder (_ "Category")))))) ;; TODO This should be a "list" where any field can be edited ;; directly. Major thing holding us back currently is that @@ -481,6 +493,7 @@ "↺") (span (@ (class "summary") (data-property "summary"))))) + ;; TODO should't the time tags contain something? (div (div (time (@ (class "dtstart") (data-property "dtstart") (data-fmt "~L~H:~M") @@ -497,7 +510,7 @@ ; "20:56" )) (div (@ (class "fields")) - (div (b "Plats: ") + (div (b ,("Location: ")) (div (@ (class "location") (data-property "location")) ; "Alsättersgatan 13" @@ -519,7 +532,7 @@ ;; "varje vecka" ;; ".") (div (@ (class "last-modified")) - "Senast ändrad -" + ,(_ "Last Modified") " -" ; "2021-09-29 19:56" )))))) @@ -527,21 +540,21 @@ `(template (@ (id "vevent-edit-rrule")) (div (@ (class "eventtext")) - (h2 "Upprepningar") + (h2 ,(_ "Recurrences")) (dl - (dt "Frequency") + (dt ,(_ "Frequency")) (dd (select (@ (name "freq")) (option "-") ,@(map (lambda (x) `(option (@ (value ,x)) ,(string-titlecase (symbol->string x)))) '(SECONDLY MINUTELY HOURLY DAILY WEEKLY MONTHLY YEARLY)))) - (dt "Until") + (dt ,(_ "Until")) (dd (date-time-input (@ (name "until")))) - (dt "Conut") + (dt ,(_ "Conut")) (dd (input (@ (type "number") (name "count") (min 0)))) - (dt "Interval") + (dt ,(_ "Interval")) (dd (input (@ (type "number") (name "interval") ; min and max depend on FREQ ))) @@ -555,14 +568,14 @@ (dd (input-list (@ (name ,name)) (input (@ (type "number") (min ,min) (max ,max))))))) - '((bysecond "By Second" 0 60) - (byminute "By Minute" 0 59) - (byhour "By Hour" 0 23) - (bymonthday "By Month Day" -31 31) ; except 0 - (byyearday "By Year Day" -366 366) ; except 0 - (byweekno "By Week Number" -53 53) ; except 0 - (bymonth "By Month" 1 12) - (bysetpos "By Set Position" -366 366) ; except 0 + '((bysecond ,(_ "By Second") 0 60) + (byminute ,(_ "By Minute") 0 59) + (byhour ,(_ "By Hour") 0 23) + (bymonthday ,(_ "By Month Day") -31 31) ; except 0 + (byyearday ,(_ "By Year Day") -366 366) ; except 0 + (byweekno ,(_ "By Week Number") -53 53) ; except 0 + (bymonth ,(_ "By Month") 1 12) + (bysetpos ,(_ "By Set Position") -366 366) ; except 0 ))) ;; (dt "By Week Day") @@ -573,7 +586,7 @@ ;; ,(week-day-select '()) ;; )) - (dt "Weekstart") + (dt ,(_ "Weekstart")) (dd ,(week-day-select '((name "wkst"))))))) ) @@ -588,32 +601,36 @@ (nav (@ (class "popup-control")) (button (@ (class "close-button") - (title "Stäng") + ;; Close this popup + (title ,(_ "Close")) (aria-label "Close")) "×") (button (@ (class "maximize-button") - (title "Fullskärm") + ;; Make this popup occupy the entire screen + (title ,(_ "Fullscreen")) ;; (aria-label "") ) "🗖") (button (@ (class "remove-button") - (title "Ta Bort")) + ;; Remove/Trash the event this popup represent + ;; Think garbage can + (title ,(_ "Remove"))) "🗑")) (tab-group (@ (class "window-body")) (vevent-description - (@ (data-label "📅") (data-title "Översikt") + (@ (data-label "📅") (data-title ,(_ "Overview")) (class "vevent"))) (vevent-edit - (@ (data-label "🖊") (data-title "Redigera"))) + (@ (data-label "🖊") (data-title ,(_ "Edit")))) ;; (vevent-edit-rrule ;; (@ (data-label "↺") (data-title "Upprepningar"))) (vevent-changelog - (@ (data-label "📒") (date-title "Changelog"))) + (@ (data-label "📒") (date-title ,(_ "Changelog")))) ,@(when (debug) '((vevent-dl - (@ (data-label "🐸") (data-title "Debug"))))))))) + (@ (data-label "🐸") (data-title ,(_ "Debug")))))))))) diff --git a/module/calp/html/view/calendar.scm b/module/calp/html/view/calendar.scm index cfbb1865..d4ad2977 100644 --- a/module/calp/html/view/calendar.scm +++ b/module/calp/html/view/calendar.scm @@ -29,6 +29,7 @@ :use-module ((base64) :select (base64encode)) :use-module (ice-9 format) + :use-module (calp translation) ) @@ -74,10 +75,10 @@ ,display))) (unless next-start - (scm-error 'misc-error "html-generate" "Next-start needs to be a procedure" #f #f)) + (scm-error 'misc-error "html-generate" (_ "Next-start needs to be a procedure") #f #f)) (unless prev-start - (scm-error 'misc-error "html-generate" "Prev-start needs to be a procedure" #f #f)) + (scm-error 'misc-error "html-generate" (_ "Prev-start needs to be a procedure") #f #f)) (xhtml-doc (@ (lang sv)) @@ -88,9 +89,11 @@ (meta (@ (name viewport) (content "width=device-width, initial-scale=0.5"))) (meta (@ (name description) - (content "Calendar for the dates between " - ,(date->string start-date) " and " - ,(date->string end-date)))) + (content ,(format #f (_ "Calendar for the dates between ~a and ~a") + ;; start date metainfo + (date->string start-date (_ "~Y-~m-~d")) + ;; end date metainfo + (date->string end-date (_ "~Y-~m-~d")))))) ;; NOTE this is only for the time actually part of this calendar. ;; overflowing times from pre-start and post-end is currently ignored here. (meta (@ (name start-time) @@ -151,10 +154,12 @@ window.default_calendar='~a';" ;; Page footer (footer (@ (style "grid-area: footer")) - (span "Page generated " ,(date->string (current-date))) - (span "Current time " (current-time (@ (interval 1)))) + (span ,(_ "Page generated ") + ;; Generation data + ,(date->string (current-date) (_ "~Y-~m-~d"))) + (span ,(_ "Current time ") (current-time (@ (interval 1)))) (span (a (@ (href ,(repo-url))) - "Source Code"))) + ,(_ "Source Code")))) ;; Small calendar and navigation (nav (@ (class "calnav") (style "grid-area: nav")) @@ -164,10 +169,12 @@ window.default_calendar='~a';" (start-of-week start-date) start-date) "/week/~1.html") - "veckovy") + ;; Button to view week + (_ "Week")) ,(btn href: (date->string (set (day start-date) 1) "/month/~1.html") - "månadsvy") + ;; button to view month + (_ "Month")) (today-button (a (@ (class "btn") @@ -176,7 +183,8 @@ window.default_calendar='~a';" [(month) "view=month"] [(week) "view=week"] [else ""])))) - "idag"))) + ;; Button to go to today + ,(_ "Today")))) (div (@ (id "jump-to")) ;; Firefox's accessability complain about each date @@ -196,9 +204,11 @@ window.default_calendar='~a';" ,(btn "➔")))) (details (@ (open) (style "grid-area: cal")) - (summary "Month overview") + (summary ,(_ "Month overview")) (div (@ (class "smallcall-head")) - ,(string-titlecase (date->string start-date "~B ~Y"))) + ,(string-titlecase (date->string start-date + ;; Header of small calendar + (_ "~B ~Y")))) ;; NOTE it might be a good idea to put the navigation buttons ;; earlier in the DOM-tree/tag order. At least Vimium's ;; @key{[[} keybind sometimes finds parts of events instead. @@ -223,17 +233,17 @@ window.default_calendar='~a';" (action "/search/text")) (input (@ (type "text") (name "q") - (placeholder "Sök"))) + ;; Search placeholder + (placeholder ,(_ "Search")))) (input (@ (type "submit") (value ">")))) ,(when (or (debug) (edit-mode)) `(details (@ (class "sliders")) - (summary "Option sliders") - + (summary ,(_ "Option sliders")) ,@(when (edit-mode) - `((label "Event blankspace") + `((label ,(_ "Event blankspace")) ,(slider-input variable: "editmode" min: 0 @@ -242,7 +252,7 @@ window.default_calendar='~a';" value: 1))) ,@(when (debug) - `((label "Fontsize") + `((label ,(_ "Fontsize")) ,(slider-input unit: "pt" min: 1 @@ -253,7 +263,7 @@ window.default_calendar='~a';" ;; List of calendars (details (@ (class "calendarlist")) - (summary "Calendar list") + (summary ,(_ "Calendar list")) (ul ,@(map (lambda (calendar) `(li (@ (data-calendar ,(base64encode (prop calendar 'NAME)))) @@ -279,7 +289,7 @@ window.default_calendar='~a';" ;; Events which started before our start point, ;; but "spill" into our time span. (section (@ (class "text-day")) - (header (h2 "Tidigare")) + (header (h2 ,(_ "Earlier"))) ;; TODO this group gets styles applied incorrectly. ;; Figure out way to merge it with the below call. ,@(stream->list @@ -287,8 +297,7 @@ window.default_calendar='~a';" (lambda (ev) (fmt-single-event ev `((id ,(html-id ev)) - (data-calendar ,(base64encode (or (prop (parent ev) 'NAME) - "unknown")))))) + (data-calendar ,(base64encode (or (prop (parent ev) 'NAME) "unknown")))))) (stream-take-while (compose (cut date/-time<? <> start-date) (extract 'DTSTART)) diff --git a/module/calp/html/view/calendar/week.scm b/module/calp/html/view/calendar/week.scm index 38e7501b..16337102 100644 --- a/module/calp/html/view/calendar/week.scm +++ b/module/calp/html/view/calendar/week.scm @@ -17,6 +17,7 @@ :select (make-block output-uid) ) ;; :use-module ((calp html components) ;; :select ()) + :use-module (calp translation) :use-module ((vcomponent util group) :select (group-stream get-groups-between)) :use-module (ice-9 format) @@ -31,7 +32,9 @@ (div (@ (class "days")) ;; Top left area (div (@ (class "week-indicator")) - (span (@ (style "font-size: 50%")) "v.") ; figure out if we want this... + (span (@ (style "font-size: 50%")) + ;; Week number prefix + ,(_ "v.")) ,@(->> (week-number start-date) number->string string->list (map (lambda (c) `(span ,(string c)))))) @@ -44,8 +47,10 @@ ,@(map (lambda (day-date) `(div (@ (class "meta")) (span (@ (class "daydate")) - ,(date->string day-date "~Y-~m-~d")) + ;; Week view header format + ,(date->string day-date (_ "~Y-~m-~d"))) (span (@ (class "dayname")) + ;; TODO translation here? ,(string-titlecase (date->string day-date "~a"))))) range) ,@(stream->list diff --git a/module/calp/html/view/search.scm b/module/calp/html/view/search.scm index 9b03151b..08436bc5 100644 --- a/module/calp/html/view/search.scm +++ b/module/calp/html/view/search.scm @@ -8,6 +8,7 @@ :select (xhtml-doc include-css)) :use-module ((calp html vcomponent) :select (compact-event-list)) + :use-module (calp translation) ) ;; Display the result of a search term, but doesn't do any searching @@ -24,25 +25,25 @@ errors has-query? search-term search-result page paginator) (xhtml-doc (@ (lang sv)) - (head (title "Search results") + (head (title ,(_ "Search results")) ,(include-css "/static/style.css")) (body - (a (@ (href ("/today"))) "Till Idag") - (h2 "Search term") + (a (@ (href ("/today"))) ,(_ "Show today")) + (h2 ,(_ "Search term")) (form (pre (textarea (@ (name "q") (rows 5) (spellcheck false) (style "width:100%")) ,(when has-query? (with-output-to-string (lambda () (pretty-print search-term)))))) - (label (@ (for "onlyfuture")) "limit to future occurences") + (label (@ (for "onlyfuture")) ,(_ "limit to future occurences")) (input (@ (name "onlyfuture") (id "onlyfuture") (type checkbox))) (input (@ (type submit)))) ,@(if errors - `((h2 "Error searching") + `((h2 ,(_ "Error searching")) (div (@ (class "error")) (pre ,errors))) - `((h2 "Result (page " ,page ")") + `((h2 ,(format #f (_ "Result (page ~a)") page)) (ul ,@(compact-event-list search-result)) (div (@ (class "paginator")) ,@(paginator->list diff --git a/module/calp/main.scm b/module/calp/main.scm index 7477e2e8..e5388ae0 100644 --- a/module/calp/main.scm +++ b/module/calp/main.scm @@ -24,29 +24,31 @@ :use-module (statprof) :use-module (calp repl) + :use-module (sxml simple) :use-module ((xdg basedir) :prefix xdg-) + :use-module (calp translation) + ) (define options `((statprof (value display-style) - (description "Run the program within Guile's built in statical " - "profiler. Display style is one of " - (b "flat") " or " (b "tree") ".")) + (description ,(xml->sxml (_ "<group>Run the program within Guile's built in statical +profiler. Display style is one of <b>flat</b> or <b>tree</b>.</group>")))) (repl (value address) (description - "Start a Guile repl which can be connected to, defaults to the unix socket " - (i "/run/user/${UID}/calp-${PID}") ", but it can be bound to any unix or " - "TCP socket. ((@ (vcomponent util instance) global-event-object)) " - "should contain all events." - (br) - (b "Should NOT be used in production."))) + ,(xml->sxml (_ "<group>Start a Guile repl which can be connected to, defaults to the +unix socket <i>/run/user/${UID}/calp-${PID}</i>, but it can be bound to any +unix or TCP socket. ((@ (vcomponent util instance) global-event-object)) should +contain all events. +<br/> +<b>Should NOT be used in production.</b></group>")))) (config (value #t) (description - "Path to alterantive configuration file to load instead of the default one. ")) + ,(_ "Path to alterantive configuration file to load instead of the default one."))) ;; Techical note: ;; Guile's getopt doesn't support repeating keys. Thereby the small jank, @@ -54,57 +56,54 @@ (option (single-char #\o) (value #t) (description - "Set configuration options, on the form " - (i "key") "=" (i "value") - " as if they were set in the config file. These options have " - "priority over those from the file. " - "Can " (i "not") " be given with an equal after --option." - (br) "Can be given multiple times.")) + ,(xml->sxml (_ "<group>Set configuration options, on the form <i>key</i>=<i>value</i> +as if they were set in the config file. These options have priority over those +from the file. Can <i>not</i> be given with an equal after --option. <br/>Can +be given multiple times.</group>")))) (version (single-char #\v) - (description "Display version, which is " ,(@ (calp) version) " btw.")) + (description ,(format #f (_ "Display version, which is ~a btw.") + (@ (calp) version)))) (update-zoneinfo) (help (single-char #\h) - (description "Print this help")) + (description ,(_ "Print this help"))) - (printconf (description "Print known configuration variables." - (br) (b "NOTE") ": " - "Only those configuration variables which are loaded " - "will be shown, more might be available")))) + (printconf (description ,(xml->sxml (_ "<group>Print known configuration variables. +<br/><b>NOTE</b>: +Only those configuration variables which are loaded will be shown, more might be +available</group>")))))) (define module-help - '(*TOP* (br) - (center (b "Calp")) (br) (br) - "Usage: " (b "calp") " [ " (i flags) " ] " (i mode) " [ " (i "mode flags") " ]" (br) - - (hr) - (center (b "Modes")) (br) (br) - - (p (b "html") " reads calendar files from disk, and writes them to static HTML files.") - - (p (b "terminal") " loads the calendars, and startrs an interactive terminal interface.") - - "[UNTESTED]" (br) - (p (b "import") "s an calendar object into the database.") - - (p (b "text") " formats and justifies what it's given on standard input, " - "and writes it to standard output. Similar to this text.") - - (p (b "ical") " loads the calendar database, and imideately " - "reserializes it back into ICAL format. " - "Useful for merging calendars.") - - (p (b "benchmark") " " (i "module") (br) - "Runs the procedure 'run-benchmark' from the module (calp benchmark " (i "module") ").") - - (p (b "server") " starts an HTTP server which dynamicly loads and displays event. The " - (i "/month/{date}.html") " & " (i "/week/{date}.html") " runs the same output code as " - (b "html") ". While the " (i "/calendar/{uid}.ics") " uses the same code as " (b "ical") ".") - - (hr) (br) - (center (b "Flags")) (br))) + (xml->sxml + (string-append + "<group><br/> +<center><b>" "Calp" "</b></center> +<br/><br/> +" (_ "Usage: <b>calp</b> [ <i>flags</i> ] <i>mode</i> [ <i>mode flags</i> ]") "<br/> +<hr/>" +;; Header for following list of modes of operation + "<center><b>" (_ "Modes") "</b></center> +<br/><br/>" + (_ "<p><b>html</b> reads calendar files from disk, and writes them to static HTML files.</p>") + (_ "<p><b>terminal</b> loads the calendars, and starts an interactive terminal interface.</p>") + (_ "[UNTESTED]<br/><p><b>import</b>s a calendar object into the database.</p>") + (_ "<p><b>text</b> formats and justifies what it's given on standard input, +and writes it to standard output. Similar to this text.</p>") + (_ "<p><b>ical</b> loads the calendar database, and immediately +re-serializes it back into iCAL format. Useful for merging calendars.</p>") + (_ "<p><b>benchmark</b> <i>module</i><br/>Runs the procedure 'run-benchmark' +from the module (calp benchmark <i>module</i>).</p>") + (_ "<p><b>server</b> starts an HTTP server which dynamically loads and +displays events. The <i>/month/{date}.html</i> & <i>/week/{date}.html</i> runs +the same output code as <b>html</b>. While the <i>/calendar/{uid}.ics</i> uses +the same code as <b>ical</b>.</p>") + "<hr/><br/>" + ;; Header for list of available flags. + ;; Actual list is auto generated elsewhere. + "<center><b>" (_ "Flags") "</b></center> +<br/></group>"))) (define (ornull a b) (if (null? a) @@ -122,7 +121,7 @@ altconfig (scm-error 'misc-error "wrapped-main" - "Configuration file ~a missing" + (_ "Configuration file ~a missing") (list altconfig) #f))] ;; altconfig could be placed in the list below. But I want to raise an error @@ -169,7 +168,10 @@ )) (lambda args (format (current-error-port) - "Failed loading config file ~a~%~s~%" + ;; Two arguments: + ;; Configuration file path, + ;; thrown error arguments + (_ "Failed loading config file ~a~%~s~%") config-file args ))) @@ -210,14 +212,14 @@ (throw 'return)) (when (option-ref opts 'version #f) - (format #t "Calp version ~a~%" (@ (calp) version)) + (format #t (_ "Calp version ~a~%") (@ (calp) version)) (throw 'return)) (when (option-ref opts 'update-zoneinfo #f) (let* ((locations (list "/usr/libexec/calp/tzget" (path-append (xdg-data-home) "tzget"))) (filename (or (find file-exists? locations) (scm-error 'missing-helper "wrapped-main" - "tzget not installed, please put it in one of ~a" + (_ "tzget not installed, please put it in one of ~a") (list locations) (list "tzget" locations)))) (pipe (open-input-pipe filename))) @@ -253,7 +255,7 @@ ((benchmark) (@ (calp entry-points benchmark) main)) (else => (lambda (s) (format (current-error-port) - "Unsupported mode of operation: ~a~%" + (_ "Unsupported mode of operation: ~a~%") s) (exit 1)))) ropt)) @@ -268,7 +270,7 @@ (define-public (main args) - ((@ (calp util time) report-time!) "Program start") + ((@ (calp util time) report-time!) (_ "Program start")) (with-throw-handler #t (lambda () (dynamic-wind (lambda () 'noop) diff --git a/module/calp/repl.scm b/module/calp/repl.scm index 47c35a40..6f2c7c0a 100644 --- a/module/calp/repl.scm +++ b/module/calp/repl.scm @@ -7,12 +7,13 @@ :use-module (ice-9 regex) :use-module ((calp util hooks) :select (shutdown-hook)) :use-module ((hnh util exceptions) :select (warning)) + :use-module (calp translation) ) (define-public (repl-start address) (define lst (string->list address)) (format (current-error-port) - "Starting REPL server at ~a~%" address) + (_ "Starting REPL server at ~a~%") address) (spawn-server (case (cond [(memv (car lst) '(#\. #\/)) 'UNIX] [(string-match "(\\d{1,3}\\.){3}\\d{1,3}(:\\d+)?" address) 'IPv4] @@ -22,17 +23,17 @@ [(UNIX) (add-hook! shutdown-hook (lambda () (catch 'system-error (lambda () (delete-file address)) (lambda (err proc fmt args data) - (warning (format #f "Failed to unlink ~a: ~?" - address fmt args)) + (warning (string-append (format #f (_ "Failed to unlink ~a") address) + (format #f ": ~?" fmt args))) err)))) (make-unix-domain-server-socket path: address)] [(IPv4) (apply (case-lambda - [() (error "Empty address?")] + [() (error (_ "Empty address?"))] [(address) (make-tcp-server-socket host: address)] [(address port) (make-tcp-server-socket host: address port: port)]) (string-split address #\:))] ;; currently impossible - [(IPv6) (error "How did you get here?")])) + [(IPv6) (error (_ "How did you get here?"))])) ;; TODO setup repl environment here diff --git a/module/calp/server/routes.scm b/module/calp/server/routes.scm index 88f641fb..d05451eb 100644 --- a/module/calp/server/routes.scm +++ b/module/calp/server/routes.scm @@ -33,7 +33,11 @@ :use-module (calp util config) :use-module (calp html view calendar) - :use-module ((calp html view search) :select (search-result-page))) + :use-module ((calp html view search) :select (search-result-page)) + + :use-module (calp translation) + + ) @@ -49,8 +53,13 @@ ;; start with /static. (define (directory-table prefix dir) `(table (@ (class "directory-table")) - (thead - (tr (th "") (th "Name") (th "Perm") (th "Size"))) + (thead + (tr (th "") + (th ,(_ "Name")) + ;; File permissions, should be about as long as three digits + (th ,(_ "Perm")) + ;; File size + (th ,(_ "Size")))) (tbody (tr (td "↩️") (td (@ (colspan 3)) (a (@ (href ,(-> (path-split dir) @@ -80,7 +89,7 @@ (scm-error 'misc-error "directory-table" - "Scandir argument invalid or not directory: ~s" + (_ "Scandir argument invalid or not directory: ~s") (list dir) '()))))))) @@ -122,7 +131,7 @@ (GET "/" () (return '((content-type text/html)) (sxml->html-string - '(body (a (@ (href "/today")) "Gå till idag") + '(body (a (@ (href "/today")) ,(_ "Go to Today")) (script "window.onload = function() { document.getElementsByTagName('a')[0].click();}"))))) @@ -175,7 +184,7 @@ (POST "/remove" (uid) (unless uid (return (build-response code: 400) - "uid required")) + (_ "uid required"))) (aif (get-event-by-uid global-event-object uid) (begin @@ -187,10 +196,10 @@ (set! (param (prop* it 'X-HNH-REMOVED) 'VALUE) "BOOLEAN") (unless ((@ (vcomponent formats vdir save-delete) save-event) it) (return (build-response code: 500) - "Saving event to disk failed.")) + (_ "Saving event to disk failed."))) (return (build-response code: 204))) (return (build-response code: 400) - (format #f "No event with UID '~a'" uid)))) + (format #f (_ "No event with UID '~a'") uid)))) ;; TODO this fails when dtstart is <date>. ;; @var{cal} should be the name of the calendar encoded in base64. @@ -198,7 +207,7 @@ (unless (and cal data) (return (build-response code: 400) - "Both 'cal' and 'data' required\r\n")) + (string-append (_ "Both 'cal' and 'data' required") "\r\n"))) ;; NOTE that this leaks which calendar exists, @@ -211,7 +220,8 @@ (unless calendar (return (build-response code: 400) - (format #f "No calendar with name [~a]\r\n" calendar-name))) + (format #f "~@?\r\n" (_ "No calendar with name [~a]") + calendar-name))) ;; Expected form of data (but in XML) is: ;; @example @@ -240,11 +250,13 @@ #f)) (lambda (err port . args) (return (build-response code: 400) - (format #f "XML parse error ~{~a~}\r\n" args))))))) + (format #f "~a ~{~a~}\r\n" + (_ "XML parse error") + args))))))) (unless (eq? 'VEVENT (type event)) (return (build-response code: 400) - "Object not a VEVENT\r\n")) + (string-append (_ "Object not a VEVENT") "\r\n"))) ;; NOTE add-event uses the given UID if one is given, ;; but generates its own if not. It might be a good idea @@ -320,7 +332,7 @@ ;; and "program parent" into different fields. (lambda () (sxml->xml ((@ (vcomponent formats xcal output) vcomponent->sxcal) it))))) (return (build-response code: 404) - (format #f "No component with UID=~a found." uid)))) + (format #f (_ "No component with UID=~a found.") uid)))) (GET "/calendar/:uid{.*}.ics" (uid) (aif (get-event-by-uid global-event-object uid) @@ -329,7 +341,7 @@ (lambda () (print-components-with-fake-parent (list it))))) (return (build-response code: 404) - (format #f "No component with UID=~a found." uid)))) + (format #f (_ "No component with UID=~a found.") uid)))) (GET "/search/text" (q) (return (build-response diff --git a/module/calp/server/server.scm b/module/calp/server/server.scm index fc185033..b9d5c6d3 100644 --- a/module/calp/server/server.scm +++ b/module/calp/server/server.scm @@ -1,4 +1,5 @@ (define-module (calp server server) + :use-module (hnh util) :use-module (web server) :use-module ((calp server routes) :select (make-make-routes)) diff --git a/module/calp/terminal.scm b/module/calp/terminal.scm index c83e76ce..d91dc584 100644 --- a/module/calp/terminal.scm +++ b/module/calp/terminal.scm @@ -26,6 +26,7 @@ #:use-module (oop goops) #:use-module (oop goops describe) + #:use-module (calp translation) #:autoload (vcomponent util instance) (global-event-object) @@ -74,7 +75,7 @@ " │ " (if (prop ev 'LOCATION) "" "\x1b[1;30m") (trim-to-width - (or (prop ev 'LOCATION) "INGEN LOKAL") location-width) + (or (prop ev 'LOCATION) (_ "NO LOCATION")) location-width) STR-RESET "\n"))) events @@ -125,7 +126,8 @@ (cls) - (display "== Day View ==\n") + (display (_ "== Day View ==")) + (newline) (display-calendar-header! (current-page this)) @@ -138,22 +140,35 @@ ;; display highlighted event (unless (null? events) (let ((ev (list-ref events (active-element this)))) - (format #t "~a~%~% ~a~%~%~a\x1b[1mStart:\x1b[m ~a \x1b[1mSlut:\x1b[m ~a~%~%~a~%" - (prop ev '-X-HNH-FILENAME) - (prop ev 'SUMMARY) - (or (and=> (prop ev 'LOCATION) - (cut string-append "\x1b[1mPlats:\x1b[m " <> "\n")) "") - ;; NOTE RFC 5545 says that DTSTART and DTEND MUST - ;; have the same type. However we believe that is - ;; another story. + (format #t "~a" (prop ev '-X-HNH-FILENAME)) + (format #t "~%~%") + (format #t " ~a" (prop ev 'SUMMARY)) + (format #t "~%~%") + (awhen (prop ev 'LOCATION) + (format #t + "\x1b[1m~a:\x1b[m ~a~%" + (_ "Location") + it)) + ;; NOTE RFC 5545 says that DTSTART and DTEND MUST + ;; have the same type. However we believe that is + ;; another story. + (format #t "\x1b[1m~a:\x1b[m ~a " + (_ "Start") (let ((start (prop ev 'DTSTART))) (if (datetime? start) - (datetime->string (prop ev 'DTSTART) "~Y-~m-~d ~H:~M:~S") - (date->string start))) - (let ((end (prop ev 'DTEND))) - (if (datetime? end) - (datetime->string (prop ev 'DTEND) "~Y-~m-~d ~H:~M:~S") - (date->string end))) + (datetime->string (prop ev 'DTSTART) + ;; Event start date-time terminal view + (_ "~Y-~m-~d ~H:~M:~S")) + (date->string start)))) + (format #t "\x1b[1m~a:\x1b[m ~a~%~%" + (_ "End") + (let ((start (prop ev 'DTSTART))) + (if (datetime? start) + (datetime->string (prop ev 'DTSTART) + ;; Event end date-time terminal view + (_ "~Y-~m-~d ~H:~M:~S")) + (date->string start)))) + (format #t "~a~%" (unlines (take-to (flow-text (or (prop ev 'DESCRIPTION) "") #:width (min 70 width)) (- height 8 5 (length events) 5))))))) @@ -194,14 +209,14 @@ (active-element this) 0)) ((#\/) (set-cursor-pos 0 (1- height)) - (let ((search-term (get-line "quick search: "))) + (let ((search-term (get-line (_ "quick search: ")))) `(push ,(search-view (format #f "(regexp-exec (make-regexp \"~a\" regexp/icase) (prop event 'SUMMARY))" search-term) (get-event-set this))))) ((#\() (set-cursor-pos 0 (1- height)) - (let ((search-term (get-line "search: "))) + (let ((search-term (get-line (_ "search: ")))) `(push ,(search-view search-term (get-event-set this))))) (else (next-method)))) @@ -247,7 +262,7 @@ (cls) - (display "== Search View ==\n") + (display (_ "== Search View ==\n")) ;; display search term (format #t "~y" (search-term this)) @@ -303,7 +318,7 @@ 'DTSTART))))) ((#\h left) (set! (current-page this) = ((lambda (old) (max 0 (1- old)))))) ((#\l right) - (display "\n loading...\n") + (format #t "~% ~a~%" (_ "loading...")) (set! (current-page this) (next-page (slot-ref this 'search-result) (current-page this)))) diff --git a/module/calp/translation.scm b/module/calp/translation.scm new file mode 100644 index 00000000..c0392d95 --- /dev/null +++ b/module/calp/translation.scm @@ -0,0 +1,20 @@ +(define-module (calp translation) + :use-module (ice-9 i18n) + :use-module (ice-9 regex) + :use-module (ice-9 match) + :export (_ yes-no-check)) + +(bindtextdomain "calp" "/home/hugo/code/calp/localization/") + +(define (_ . msg) + ;; NOTE this doesn't squeese repeated whitespace + (string-map (match-lambda + (#\newline #\space) + (c c)) + (gettext (string-join msg) "calp"))) + +(define* (yes-no-check string #:optional (locale %global-locale)) + (cond ((string-match (locale-yes-regexp locale) string) 'yes) + ((string-match (locale-no-regexp locale) string) 'no) + (else #f))) + diff --git a/module/calp/util/config.scm b/module/calp/util/config.scm index 4862bbda..3bc55d92 100644 --- a/module/calp/util/config.scm +++ b/module/calp/util/config.scm @@ -9,6 +9,7 @@ :use-module (srfi srfi-1) :use-module (ice-9 format) ; for format-procedure :use-module (ice-9 curried-definitions) ; for ensure + :use-module (calp translation) :export (define-config) ) @@ -41,7 +42,7 @@ (set! (it name) value) (scm-error 'configuration-error "define-config" - "No configuration slot named ~s, when defining ~s" + (_ "No configuration slot named ~s, when defining ~s") (list key name) #f))) (set-config! name (get-config name default-value))) @@ -59,7 +60,9 @@ (or (it value) (scm-error 'configuration-error "set-config!" - "Pre-property failed when setting ~s to ~s" + ;; first slot is property name, second is new + ;; property value. + (_ "Pre-property failed when setting ~s to ~s") (list name value) #f)) value)) @@ -74,7 +77,7 @@ (when (eq? v %uniq) (scm-error 'configuration-error "get-config" - "No configuration item named ~s" + (_ "No configuration item named ~s") (list key) #f)) v) (hashq-ref config-values key default))) @@ -125,7 +128,7 @@ (hash-map->list list config-values))) `(*TOP* - (header "Configuration variables") + (header ,(_ "Configuration variables")) (dl ,@(concatenate (for (module values) in groups @@ -137,7 +140,8 @@ `((dt ,key) (dd (p (@ (inline)) ,(or (description key) ""))) - (dt "V:") + ;; Configuration variable value indicator + (dt ,(_ "V:")) (dd ,(if (procedure? value) (format-procedure value) `(scheme ,value)) diff --git a/module/calp/util/exceptions.scm b/module/calp/util/exceptions.scm index 1268f9f5..0588840e 100644 --- a/module/calp/util/exceptions.scm +++ b/module/calp/util/exceptions.scm @@ -1,7 +1,8 @@ (define-module (calp util exceptions) :use-module (calp util config) + :use-module (calp translation) :use-module (hnh util exceptions)) (define-config warnings-are-errors #f - description: "Crash on warnings." + description: (_ "Crash on warnings.") post: warnings-are-errors) diff --git a/module/datetime/instance.scm b/module/datetime/instance.scm index 5ce312f2..2b060204 100644 --- a/module/datetime/instance.scm +++ b/module/datetime/instance.scm @@ -5,10 +5,11 @@ :use-module ((hnh util path) :select (path-append)) :use-module (datetime zic) :use-module ((xdg basedir) :prefix xdg-) + :use-module (calp translation) :export (zoneinfo)) (define-config tz-list '() - description: "List of default zoneinfo files to be parsed") + description: (_ "List of default zoneinfo files to be parsed")) ;; TODO see (vcomponent uil instance), this has a similar problem with early load ;; Takes a list of zoneinfo files relative @@ -24,7 +25,7 @@ (() (define tz-list (get-config 'tz-list)) (if (null? tz-list) - (warning "Default zoneinfo only available when tz-dir and tz-list are configured") + (warning (_ "Default zoneinfo only available when tz-dir and tz-list are configured")) (self tz-list))) ((file-list) (provide 'zoneinfo) diff --git a/module/datetime/timespec.scm b/module/datetime/timespec.scm index d3a84671..099634b6 100644 --- a/module/datetime/timespec.scm +++ b/module/datetime/timespec.scm @@ -11,6 +11,7 @@ :use-module (datetime) :use-module (srfi srfi-1) :use-module (srfi srfi-9 gnu) + :use-module (calp translation) ) @@ -33,7 +34,7 @@ (define-public (timespec-add . specs) (unless (apply eqv? (map timespec-type specs)) - (warning "Adding timespecs of differing types")) + (warning (_ "Adding timespecs of differing types"))) (reduce (lambda (spec done) (cond diff --git a/module/datetime/zic.scm b/module/datetime/zic.scm index b0630f3e..e2600d4f 100644 --- a/module/datetime/zic.scm +++ b/module/datetime/zic.scm @@ -22,6 +22,7 @@ :use-module (srfi srfi-9 gnu) :use-module ((vcomponent recurrence internal) :select (byday make-recur-rule bymonthday)) + :use-module (calp translation) ) @@ -166,7 +167,7 @@ day: (string->number day)) time: (timespec-time timespec) tz: (case (timespec-type timespec) - [(#\s) (warning "what even is \"Standard time\"‽") ""] + [(#\s) (warning (_ "what even is \"Standard time\"‽")) ""] [(#\w) #f] ;; Since we might represent times before UTC existed ;; this is a bit of a lie. But it should work. @@ -263,7 +264,7 @@ ;; They were removed since they were unused, uneeded, and was ;; technical dept. (scm-error 'misc-error "parse-zic-file" - "Invalid key ~s. Note that leap seconds and expries rules aren't yet implemented." + (_ "Invalid key ~s. Note that leap seconds and expries rules aren't yet implemented.") (list type) #f)] ))])))))) @@ -301,7 +302,7 @@ (target (link-target link)) (target-item (hash-ref zones target #f))) (if (not target-item) - (warning "Unresolved link, target missing ~a -> ~a" name target) + (warning (_ "Unresolved link, target missing ~a -> ~a") name target) (hash-set! zones name target-item)))) (car it))) @@ -341,7 +342,7 @@ (set (day d) base-day)))])) tz: (case (timespec-type (rule-at rule)) ((#\w) #f) - ((#\s) (warning "what even is \"Standard time\"‽") #f) + ((#\s) (warning (_ "what even is \"Standard time\"‽")) #f) ((#\u #\g #\z) 'UTC)))) (let ((timespec (rule-at rule))) @@ -363,7 +364,7 @@ (case to ((maximum) #f) ((minimum) (scm-error 'misc-error "rule->rrule" - "Check your input" + (_ "Check your input") #f #f)) ((only) (datetime @@ -388,7 +389,7 @@ ;; Sun>=8 (let* (((<> wday base-day) (rule-on rule))) (when (eq? '< <>) - (warning "Counting backward for RRULES unsupported")) + (warning (_ "Counting backward for RRULES unsupported"))) ;; NOTE this only realy works when base-day = 7n + 1, n ∈ N ;; something like Sun>=5 is hard to fix, since we can only ;; say which sunday in the month we want (first sunday, @@ -406,11 +407,15 @@ idx (+ idx 2))] [(#\z) ;; NOTE No zones seem to currently use %z formatting. - (warning "%z not yet implemented") + ;; '%z' is NOT a format string, but information about another format string. + (warning (_ "%z not yet implemented")) fmt-string] [else (scm-error 'misc-error "zone-format" - "Invalid format char ~s in ~s at position ~a" + ;; first slot is the errornous character, + ;; second is the whole string, third is the index + ;; of the faulty character. + (_ "Invalid format char ~s in ~s at position ~a") (list (string-index fmt-string (1+ idx)) fmt-string (1+ idx)) diff --git a/module/hnh/util/language.scm b/module/hnh/util/language.scm new file mode 100644 index 00000000..9b61483c --- /dev/null +++ b/module/hnh/util/language.scm @@ -0,0 +1,14 @@ +(define-module (hnh util language) + :export (resolve-language)) + + +;; Locale objects, such as %global-locale, doesn't provide a way to access the language name, +;; This is for procedures which want to handle their translations manually. +(define (resolve-language) + "Returns a two character symbol representing the \"current\" language. e.g. en" + (string->symbol + (string-take + (or (getenv "LC_MESSAGES") + (getenv "LC_ALL") + "en") + 2))) diff --git a/module/text/numbers.scm b/module/text/numbers.scm index ba44a495..168d267e 100644 --- a/module/text/numbers.scm +++ b/module/text/numbers.scm @@ -1,9 +1,35 @@ +(define-module (text numbers) + :export (number->string-cardinal + number->string-ordinal + each-string)) -;; TODO this is bad, but this file is replaced once translations are merged -(eval-when (load) - (throw 'do-not-load-me - "Import (text numbers <langugage>) instead") - ) +(define (get mod-symb proc-symb) + (module-ref (resolve-interface `(text numbers ,mod-symb)) + proc-symb)) + +;; "sv_SE.UTF-8" +(define (resolve-language) + (string->symbol + (string-take + (or (getenv "LC_MESSAGES") + (getenv "LC_ALL") + "en") + 2))) + +(define* (number->string-cardinal + n #:optional (language (resolve-language))) + ((get language 'number->string-cardinal) n)) + +(define* (number->string-ordinal + n #:optional (language (resolve-language))) + ((get language 'number->string-ordinal) n)) + +;; TODO change API to allow language, and stop having random extra +;; arguments for implementations. +(define* (each-string count . args) + (define language (resolve-language)) + (apply (get language 'each-string) + count args)) ;; scheme@(guile-user)> (number->string-cardinal 123) ;; $10 = "hundratjugotre" diff --git a/module/vcomponent/datetime/output.scm b/module/vcomponent/datetime/output.scm index 2b528423..fe909ebb 100644 --- a/module/vcomponent/datetime/output.scm +++ b/module/vcomponent/datetime/output.scm @@ -3,31 +3,52 @@ :use-module (datetime) :use-module (vcomponent base) :use-module (text util) + :use-module (calp translation) + :use-module ((vcomponent recurrence display) :select (format-recurrence-rule)) ) ;; ev → sxml +;; TODO translation (define-public (format-recurrence-rule ev) - `("Upprepas " - ,((@ (vcomponent recurrence display) format-recurrence-rule) - (prop ev 'RRULE)) + ;; [FRR] + ;; Part of the sentance "Repeated [every two weeks], except on ~a, ~a & ~a" + ;; See everything tagged [FRR] + `(,(_ "Repeated ") + ,(format-recurrence-rule (prop ev 'RRULE)) ,@(awhen (prop* ev 'EXDATE) (list - ", undantaget " + ;; See [FRR] + (_ ", except on ") (add-enumeration-punctuation (map (lambda (d) + ;; TODO show year if different from current year (if (date? d) - ;; NOTE possibly show year? - (date->string d "~e ~b") + ;; [FRR] Exception date without time + (date->string d (_ "~e ~b")) ;; NOTE only show time when it's different than the start time? ;; or possibly only when FREQ is hourly or lower. (if (memv ((@ (vcomponent recurrence internal) freq) - (prop ev 'RRULE)) - '(HOURLY MINUTELY SECONDLY)) - (datetime->string d "~e ~b ~k:~M") - (datetime->string d "~e ~b")))) + (prop ev 'RRULE)) + '(HOURLY MINUTELY SECONDLY)) + ;; [FRR] Exception date with time + (datetime->string d (_ "~e ~b ~k:~M")) + ;; [FRR] Exception date without time + (datetime->string d (_ "~e ~b"))))) (map value it))))) ".")) +(define-public (format-summary ev str) + ((get-config 'summary-filter) ev str)) + +;; NOTE this should have information about context (html/term/...) +(define-public (format-description ev str) + (catch #t (lambda () ((get-config 'description-filter) ev str)) + (lambda (err . args) + ;; Warning message for failure to format description. + ;; First argument is name of warning/error, + ;; second is error arguments + (warning (_ "~a on formatting description, ~s") err args) + str))) ;; Takes an event, and returns a pretty string for the time interval ;; the event occupies. @@ -37,9 +58,9 @@ (cond [(prop ev 'DTEND) => (lambda (e) (if (date= e (date+ s (date day: 1))) - "~Y-~m-~d" ; start = end, only return one value - (values "~Y-~m-~d" - "~Y-~m-~d")))] + (_ "~Y-~m-~d") ; start = end, only return one value + (values (_ "~Y-~m-~d") + (_ "~Y-~m-~d"))))] ;; no end value, just return start [else (date->string s)]))] [else ; guaranteed datetime @@ -47,6 +68,9 @@ (e (prop ev 'DTEND))) (if e (let ((fmt-str (if (date= (get-date s) (get-date e)) - "~H:~M" "~Y-~m-~d ~H:~M"))) + (_ "~H:~M") + ;; Note the non-breaking space + (_ "~Y-~m-~d ~H:~M")))) (values fmt-str fmt-str)) - "~Y-~m-~d ~H:~M"))])) + ;; Note the non-breaking space + (_ "~Y-~m-~d ~H:~M")))])) diff --git a/module/vcomponent/formats/common/types.scm b/module/vcomponent/formats/common/types.scm index 97980e1a..9e18f1eb 100644 --- a/module/vcomponent/formats/common/types.scm +++ b/module/vcomponent/formats/common/types.scm @@ -5,13 +5,14 @@ :use-module (datetime) :use-module (srfi srfi-9 gnu) :use-module (datetime timespec) + :use-module (calp translation) ) ;; BINARY (define (parse-binary props value) ;; p 30 (unless (string=? "BASE64" (hashq-ref props 'ENCODING)) - (warning "Binary field not marked ENCODING=BASE64")) + (warning (_ "Binary field not marked ENCODING=BASE64"))) ;; For icalendar no extra whitespace is allowed in a ;; binary field (except for line wrapping). This differs @@ -23,7 +24,7 @@ (cond [(string=? "TRUE" value) #t] [(string=? "FALSE" value) #f] - [else (warning "~a invalid boolean" value)])) + [else (warning (_ "~a invalid boolean") value)])) ;; CAL-ADDRESS ⇒ uri @@ -56,7 +57,7 @@ (define (parse-integer props value) (let ((n (string->number value))) (unless (integer? n) - (warning "Non integer as integer")) + (warning (_ "Non integer as integer"))) n)) ;; PERIOD @@ -87,7 +88,7 @@ (case (cadr rem) [(#\n #\N) (loop (cddr rem) (cons #\newline str) done)] [(#\; #\, #\\) => (lambda (c) (loop (cddr rem) (cons c str) done))] - [else => (lambda (c) (warning "Non-escapable character: ~a" c) + [else => (lambda (c) (warning (_ "Non-escapable character: ~a") c) (loop (cddr rem) str done))])] [(#\,) (loop (cdr rem) '() (cons (reverse-list->string str) done))] @@ -136,5 +137,5 @@ (define-public (get-parser type) (or (hashq-ref type-parsers type #f) - (scm-error 'misc-error "get-parser" "No parser for type ~a" + (scm-error 'misc-error "get-parser" (_ "No parser for type ~a") (list type) #f))) diff --git a/module/vcomponent/formats/ical/output.scm b/module/vcomponent/formats/ical/output.scm index fba8bffc..489cdc00 100644 --- a/module/vcomponent/formats/ical/output.scm +++ b/module/vcomponent/formats/ical/output.scm @@ -15,6 +15,7 @@ :use-module (vcomponent geo) :use-module (vcomponent formats ical types) :use-module (vcomponent recurrence) + :use-module (calp translation) :autoload (vcomponent util instance) (global-event-object) ) @@ -90,7 +91,7 @@ (get-writer 'TEXT)] [else - (warning "Unknown key ~a" key) + (warning (_ "Unknown key ~a") key) (get-writer 'TEXT)])) (catch #t #; 'wrong-type-arg diff --git a/module/vcomponent/formats/ical/parse.scm b/module/vcomponent/formats/ical/parse.scm index 08f31ae7..7f6c89cc 100644 --- a/module/vcomponent/formats/ical/parse.scm +++ b/module/vcomponent/formats/ical/parse.scm @@ -10,6 +10,7 @@ :use-module (vcomponent base) :use-module (vcomponent geo) :use-module (vcomponent formats common types) + :use-module (calp translation) ) (define string->symbol @@ -122,7 +123,7 @@ (let ((vv (parser params value))) (when (list? vv) (scm-error 'parse-error "enum-parser" - "List in enum field" + (_ "List in enum field") #f #f)) (let ((v (string->symbol vv))) (unless (memv v enum) @@ -158,7 +159,7 @@ (lambda (params value) (let ((v ((get-parser 'TEXT) params value))) (unless (= 1 (length v)) - (warning "List in non-list field: ~s" v)) + (warning (_ "List in non-list field: ~s") v)) (string-join v ",")))] ;; TEXT, but allow a list @@ -196,7 +197,7 @@ [(memv key '(REQUEST-STATUS)) (scm-error 'parse-error "build-vline" - "TODO Implement REQUEST-STATUS" + (_ "TODO Implement REQUEST-STATUS") #f #f)] [(memv key '(ACTION)) @@ -231,7 +232,7 @@ (compose car (get-parser 'TEXT))] [else - (warning "Unknown key ~a" key) + (warning (_ "Unknown key ~a") key) (compose car (get-parser 'TEXT))]))) ;; If we produced a list create multiple VLINES from it. @@ -278,9 +279,15 @@ (lambda (fmt . args) (let ((linedata (get-metadata head*))) (format - #f "WARNING parse error around ~a + #f + ;; arguments: + ;; linedata + ;; ~? + ;; source line + ;; source file + (_ "WARNING parse error around ~a ~? - line ~a ~a~%" + line ~a ~a~%") (get-string linedata) fmt args (get-line linedata) @@ -326,10 +333,16 @@ (lambda (err proc fmt fmt-args data) (let ((linedata (get-metadata head*))) (display (format - #f "ERROR parse error around ~a + #f + ;; arguments + ;; linedata + ;; ~? + ;; source line + ;; source file + (_ "ERROR parse error around ~a ~? line ~a ~a - Defaulting to string~%" + Defaulting to string~%") (get-string linedata) fmt fmt-args (get-line linedata) diff --git a/module/vcomponent/formats/ical/types.scm b/module/vcomponent/formats/ical/types.scm index 39b3b1e3..67f9f633 100644 --- a/module/vcomponent/formats/ical/types.scm +++ b/module/vcomponent/formats/ical/types.scm @@ -4,7 +4,9 @@ :use-module (hnh util exceptions) :use-module (base64) :use-module (datetime) - :use-module (datetime timespec)) + :use-module (datetime timespec) + :use-module (calp translation) + ) ;; TODO shouldn't these really take vline:s? @@ -35,7 +37,7 @@ ;; TODO (define (write-period _ value) - (warning "PERIOD writer not yet implemented") + (warning (_ "PERIOD writer not yet implemented")) (with-output-to-string (lambda () (write value)))) @@ -92,4 +94,4 @@ (define-public (get-writer type) (or (hashq-ref type-writers type #f) - (error "No writer for type" type))) + (error (_ "No writer for type") type))) diff --git a/module/vcomponent/formats/vdir/parse.scm b/module/vcomponent/formats/vdir/parse.scm index 272674ed..b21a5f2b 100644 --- a/module/vcomponent/formats/vdir/parse.scm +++ b/module/vcomponent/formats/vdir/parse.scm @@ -15,6 +15,7 @@ :use-module ((hnh util path) :select (path-append)) :use-module (hnh util exceptions) :use-module (vcomponent base) + :use-module (calp translation) :use-module (vcomponent formats ical parse) ) @@ -62,7 +63,7 @@ ;; by RECURRENCE-ID. As far as I can tell this goes against ;; the standard. Section 3.8.4.4. (case (length events) - [(0) (warning "No events in component~%~a" + [(0) (warning (_ "No events in component~%~a") (prop item '-X-HNH-FILENAME))] [(1) (add-child! calendar (car events))] diff --git a/module/vcomponent/formats/vdir/save-delete.scm b/module/vcomponent/formats/vdir/save-delete.scm index 96354ce8..01d34f9f 100644 --- a/module/vcomponent/formats/vdir/save-delete.scm +++ b/module/vcomponent/formats/vdir/save-delete.scm @@ -24,13 +24,13 @@ (unless calendar (scm-error 'wrong-type-arg "save-event" - "Can only save events belonging to calendars, event uid = ~s" + (_ "Can only save events belonging to calendars, event uid = ~s") (list (prop event 'UID)) #f)) (unless (eq? 'vdir (prop calendar '-X-HNH-SOURCETYPE)) (scm-error 'wrong-type-arg "save-event" - "Can only save events belonging to vdir calendars. Calendar is of type ~s" + (_ "Can only save events belonging to vdir calendars. Calendar is of type ~s") (list (prop calendar '-X-HNH-SOURCETYPE)) #f)) @@ -50,7 +50,7 @@ (define calendar (parent event)) (unless (eq? 'vdir (prop calendar '-X-HNH-SOURCETYPE)) (scm-error 'wrong-type-arg "remove-event" - "Can only remove events belonging to vdir calendars. Calendar is of type ~s" + (_ "Can only remove events belonging to vdir calendars. Calendar is of type ~s") (list (prop calendar '-X-HNH-SOURCETYPE)) #f)) (delete-file (prop event '-X-HNH-FILENAME))) diff --git a/module/vcomponent/formats/xcal/output.scm b/module/vcomponent/formats/xcal/output.scm index 81fab41c..26018d92 100644 --- a/module/vcomponent/formats/xcal/output.scm +++ b/module/vcomponent/formats/xcal/output.scm @@ -7,6 +7,7 @@ :use-module (ice-9 match) :use-module (datetime) :use-module (srfi srfi-1) + :use-module (calp translation) ) @@ -69,7 +70,7 @@ (get-writer 'TEXT)] [else - (warning "Unknown key ~a" key) + (warning (_ "Unknown key ~a") key) (get-writer 'TEXT)])) (writer ((@@ (vcomponent base) get-vline-parameters) vline) (value vline))) diff --git a/module/vcomponent/formats/xcal/parse.scm b/module/vcomponent/formats/xcal/parse.scm index b21e72b5..d9020858 100644 --- a/module/vcomponent/formats/xcal/parse.scm +++ b/module/vcomponent/formats/xcal/parse.scm @@ -9,6 +9,7 @@ :use-module (vcomponent formats common types) :use-module (datetime) :use-module (srfi srfi-1) + :use-module (calp translation) ) ;; symbol, ht, (list a) -> non-list @@ -82,7 +83,7 @@ bymonth bysetpos) (string->number value)) (else (scm-error 'key-error "handle-value" - "Invalid type ~a, with value ~a" + (_ "Invalid type ~a, with value ~a") (list type value) #f)))))) @@ -155,7 +156,7 @@ (case tag-name [(request-status) ;; TODO - (warning "Request status not yet implemented") + (warning (_ "Request status not yet implemented")) #f] ((transp) (parse-enum diff --git a/module/vcomponent/formats/xcal/types.scm b/module/vcomponent/formats/xcal/types.scm index 05fbc8c6..8f13d3d1 100644 --- a/module/vcomponent/formats/xcal/types.scm +++ b/module/vcomponent/formats/xcal/types.scm @@ -2,6 +2,7 @@ :use-module (hnh util) :use-module (vcomponent formats ical types) :use-module (datetime) + :use-module (calp translation) ) (define (write-boolean _ v) @@ -51,4 +52,4 @@ (define-public (get-writer type) (or (hashq-ref sxml-writers type #f) - (error "No writer for type" type))) + (error (_ "No writer for type") type))) diff --git a/module/vcomponent/recurrence/display.scm b/module/vcomponent/recurrence/display.scm index f5ce1c57..8a9f33e6 100644 --- a/module/vcomponent/recurrence/display.scm +++ b/module/vcomponent/recurrence/display.scm @@ -1,146 +1,10 @@ -;;; Commentary: -;; Pretty print a recurrence rule (in Swedish). Is currently missing a -;; number of ;; edge cases, and even more concerning limited events. -;; NOTE It would be preferable if this could share as much logic as possible -;; with the "real" generator. -;;; Code: - (define-module (vcomponent recurrence display) - :use-module (hnh util) - :use-module (vcomponent recurrence internal) - :use-module (text util) - :use-module (text numbers sv) - :use-module ((datetime) :select (time time->string - datetime->string - week-day-name - locale-month - )) - ) - - -(define (rrule-month->string n) - (locale-month n)) - -;; TODO this currently only groups on offsets, but not on days. -;; So 1MO, 1TU becomes "första måndagen och tisdagen", which is good -;; but 1MO, -1MO doesn't become "första och sista måndagen". -;; TODO also, grouping of -dagen. e.g. "första mån- och tisdagen" -(define (format-byday-list lst) - (let* ((groups (group-by car lst))) - (intersperse - " samt " - (map (lambda (group) - ;; TODO sort week days - (case (car group) - [(#f) - (list "varje " - (add-enumeration-punctuation - (map (lambda (d) (list (week-day-name (cdr d)))) - (cadr group) - )))] - [else - (list (number->string-ordinal - (car group) - a-form?: #t) - " " - (add-enumeration-punctuation - (map (lambda (d) (list (week-day-name (cdr d)) "en")) - (cadr group))))]) - ) - groups)))) - -(define* (format-bymonth-day lst optional: (final-delim "&")) - (list "den " - (add-enumeration-punctuation - (map number->string-ordinal lst) - final-delim))) - - -(define-public (format-recurrence-rule rrule) - (string-trim - (string-flatten - (list - (case (freq rrule) - [(YEARLY) - (list (awhen (byday rrule) (list " " (format-byday-list it))) - (awhen (bymonthday rrule) (list " " (format-bymonth-day it "eller"))) - (awhen (byyearday rrule) - (list " dag " (add-enumeration-punctuation it))) - (awhen (bymonth rrule) - ;; only `i' here if we have output something else beforehand - (list (when (or (byday rrule) - (bymonthday rrule) - (byyearday rrule)) - " i ") - (add-enumeration-punctuation - (map rrule-month->string it)))) - (awhen (byweekno rrule) - (map (lambda (v) (format #f " v.~a" v)) it)) - )] - [(MONTHLY) - (list - (awhen (byday rrule) (list (format-byday-list it))) - (awhen (bymonthday rrule) (cons " " (format-bymonth-day it))))] - [else '()]) - - ;; TODO my parser adds an implicit interval to every object - ;; this might be wrong - (cond [(and (eq? 'DAILY (freq rrule)) (= 1 (interval rrule))) - " dagligen"] - [(and (eq? 'YEARLY (freq rrule)) (= 1 (interval rrule))) - ", årligen"] - [(and (eq? 'MINUTELY (freq rrule)) - (zero? (modulo (interval rrule) 15))) - (list " " - (each-string (/ (interval rrule) 15)) - " kvart")] - [else - (list - " " - (each-string (interval rrule) (eq? 'YEARLY (freq rrule))) - " " - (case (freq rrule) - ;; p.44 RFC 5545 - [(SECONDLY) "sekund"] - [(MINUTELY) "minut"] - [(HOURLY) "timme"] - [(DAILY) "dag"] - - ;; day offsets CAN ONLY be present when FREQ is - ;; either MONTHLY or YEARLY - [(WEEKLY) (aif (byday rrule) - (add-enumeration-punctuation - (map (compose week-day-name cdr) it)) - "vecka")] - [(MONTHLY) "månad"] - [(YEARLY) "år"] - [else "ERROR"] - ))]) - - (cond [(and (byminute rrule) - (byhour rrule)) - (list - " kl. " - (add-enumeration-punctuation - (map (lambda (pair) - (time->string - (time hour: (car pair) - minute: (cadr pair)) - "~H:~M")) - (cross-product (byhour rrule) - (byminute rrule)))))] - [(byhour rrule) - => (lambda (hours) - (list " kl. " (add-enumeration-punctuation hours)))] - [else '()]) - - (awhen (until rrule) - (format #f ", till och med ~a" - (datetime->string - ;; TODO ordinal on ~d? - it "den ~d ~B, ~Y kl. ~k:~M") - )) - (cond [(not (count rrule)) ""] - [(= 1 (count rrule)) (list ", totalt " (count rrule) " gång")] - [(count rrule) (list ", totalt " (count rrule) " gånger")] - [else "ERROR"]))))) + :use-module (vcomponent recurrence display common) + :use-module (hnh util language) + :re-export (rrule-month->string) + :export (format-recurrence-rule)) + +(define* (format-recurrence-rule rrule #:optional (language (resolve-language))) + ((module-ref (resolve-interface `(vcomponent recurrence display ,language)) + 'format-recurrence-rule) + rrule)) diff --git a/module/vcomponent/recurrence/display/common.scm b/module/vcomponent/recurrence/display/common.scm new file mode 100644 index 00000000..c9d0f5e1 --- /dev/null +++ b/module/vcomponent/recurrence/display/common.scm @@ -0,0 +1,6 @@ +(define-module (vcomponent recurrence display common) + :use-module ((datetime) :select (locale-month)) + :export (rrule-month->string)) + +(define (rrule-month->string n) + (locale-month n)) diff --git a/module/vcomponent/recurrence/display/en.scm b/module/vcomponent/recurrence/display/en.scm new file mode 100644 index 00000000..be9bdf53 --- /dev/null +++ b/module/vcomponent/recurrence/display/en.scm @@ -0,0 +1,131 @@ +(define-module (vcomponent recurrence display en) + :use-module (hnh util) + :use-module (vcomponent recurrence internal) + :use-module (text util) + :use-module (text numbers) + :use-module (vcomponent recurrence display common) + :use-module ((datetime) :select (time time->string + datetime->string + week-day-name))) + + + +;; TODO this currently only groups on offsets, but not on days. +;; So 1MO, 1TU becomes "första måndagen och tisdagen", which is good +;; but 1MO, -1MO doesn't become "första och sista måndagen". +;; TODO also, grouping of -dagen. e.g. "första mån- och tisdagen" +(define (format-byday-list lst) + (let* ((groups (group-by car lst))) + (intersperse + " as well as " + (map (lambda (group) + ;; TODO sort week days + (case (car group) + [(#f) + (list "every " + (add-enumeration-punctuation + (map (lambda (d) (list (week-day-name (cdr d)))) + (cadr group) + )))] + [else + (list (number->string-ordinal (car group)) " " + (add-enumeration-punctuation + (map (lambda (d) (list (week-day-name (cdr d)) "en")) + (cadr group))))]) + ) + groups)))) + +(define (format-bymonth-day lst) + (list "the " + (add-enumeration-punctuation + (map number->string-ordinal lst)))) + + +(define-public (format-recurrence-rule rrule) + (string-trim + (string-flatten + (list + (case (freq rrule) + [(YEARLY) + (list (awhen (byday rrule) (list " " (format-byday-list it))) + (awhen (bymonthday rrule) (list " " (format-bymonth-day it))) + (awhen (byyearday rrule) + (list " day " (add-enumeration-punctuation it))) + (awhen (bymonth rrule) + ;; only `i' here if we have output something else beforehand + (list (when (or (byday rrule) + (bymonthday rrule) + (byyearday rrule)) + " in ") + (add-enumeration-punctuation + (map rrule-month->string it)))) + (awhen (byweekno rrule) + (map (lambda (v) (format #f " v.~a" v)) it)) + )] + [(MONTHLY) + (list + (awhen (byday rrule) (list (format-byday-list it))) + (awhen (bymonthday rrule) (format-bymonth-day it)))] + [else '()]) + + ;; TODO my parser adds an implicit interval to every object + ;; this might be wrong + (cond [(and (eq? 'DAILY (freq rrule)) (= 1 (interval rrule))) + " daily"] + [(and (eq? 'YEARLY (freq rrule)) (= 1 (interval rrule))) + ", yearly"] + [(and (eq? 'MINUTELY (freq rrule)) + (zero? (modulo (interval rrule) 15))) + (list " " + (each-string (/ (interval rrule) 15)) + " 15 minutes")] + [else + (list + " " + (each-string (interval rrule) (eq? 'YEARLY (freq rrule))) + " " + (case (freq rrule) + ;; p.44 RFC 5545 + [(SECONDLY) "second"] + [(MINUTELY) "minute"] + [(HOURLY) "hour"] + [(DAILY) "day"] + + ;; day offsets CAN ONLY be present when FREQ is + ;; either MONTHLY or YEARLY + [(WEEKLY) (aif (byday rrule) + (add-enumeration-punctuation + (map (compose week-day-name cdr) it)) + "week")] + [(MONTHLY) "month"] + [(YEARLY) "year"] + [else "ERROR"] + ))]) + + (cond [(and (byminute rrule) + (byhour rrule)) + (list + " at " + (add-enumeration-punctuation + (map (lambda (pair) + (time->string + (time hour: (car pair) + minute: (cdr pair)) + "~H:~M")) + (cross-product (byhour rrule) + (byminute rrule)))))] + [(byhour rrule) + => (lambda (hours) + (list " at " (add-enumeration-punctuation hours)))] + [else '()]) + + (awhen (until rrule) + (format #f ", until ~a" + (datetime->string + ;; TODO ordinal on ~d? + it "~B ~d, ~Y at ~k:~M") + )) + (cond [(not (count rrule)) ""] + [(= 1 (count rrule)) (list ", " (count rrule) " time in total")] + [(count rrule) (list ", " (count rrule) " times in total")] + [else "ERROR"]))))) diff --git a/module/vcomponent/recurrence/display/sv.scm b/module/vcomponent/recurrence/display/sv.scm new file mode 100644 index 00000000..fe580474 --- /dev/null +++ b/module/vcomponent/recurrence/display/sv.scm @@ -0,0 +1,139 @@ +;;; Commentary: +;; Pretty print a recurrence rule (in Swedish). Is currently missing a +;; number of ;; edge cases, and even more concerning limited events. +;; NOTE It would be preferable if this could share as much logic as possible +;; with the "real" generator. +;;; Code: + +(define-module (vcomponent recurrence display sv) + :use-module (hnh util) + :use-module (vcomponent recurrence internal) + :use-module (text util) + :use-module (text numbers sv) + :use-module (vcomponent recurrence display common) + :use-module ((datetime) :select (time time->string + datetime->string + week-day-name))) + +;; TODO this currently only groups on offsets, but not on days. +;; So 1MO, 1TU becomes "första måndagen och tisdagen", which is good +;; but 1MO, -1MO doesn't become "första och sista måndagen". +;; TODO also, grouping of -dagen. e.g. "första mån- och tisdagen" +(define (format-byday-list lst) + (let* ((groups (group-by car lst))) + (intersperse + " samt " + (map (lambda (group) + ;; TODO sort week days + (case (car group) + [(#f) + (list "varje " + (add-enumeration-punctuation + (map (lambda (d) (list (week-day-name (cdr d)))) + (cadr group) + )))] + [else + (list (number->string-ordinal + (car group) + a-form?: #t) + " " + (add-enumeration-punctuation + (map (lambda (d) (list (week-day-name (cdr d)) "en")) + (cadr group))))]) + ) + groups)))) + +(define* (format-bymonth-day lst optional: (final-delim "&")) + (list "den " + (add-enumeration-punctuation + (map number->string-ordinal lst) + final-delim))) + +(define-public (format-recurrence-rule rrule) + (string-trim + (string-flatten + (list + (case (freq rrule) + [(YEARLY) + (list (awhen (byday rrule) (list " " (format-byday-list it))) + (awhen (bymonthday rrule) (list " " (format-bymonth-day it "eller"))) + (awhen (byyearday rrule) + (list " dag " (add-enumeration-punctuation it))) + (awhen (bymonth rrule) + ;; only `i' here if we have output something else beforehand + (list (when (or (byday rrule) + (bymonthday rrule) + (byyearday rrule)) + " i ") + (add-enumeration-punctuation + (map rrule-month->string it)))) + (awhen (byweekno rrule) + (map (lambda (v) (format #f " v.~a" v)) it)) + )] + [(MONTHLY) + (list + (awhen (byday rrule) (list (format-byday-list it))) + (awhen (bymonthday rrule) (cons " " (format-bymonth-day it))))] + [else '()]) + + ;; TODO my parser adds an implicit interval to every object + ;; this might be wrong + (cond [(and (eq? 'DAILY (freq rrule)) (= 1 (interval rrule))) + " dagligen"] + [(and (eq? 'YEARLY (freq rrule)) (= 1 (interval rrule))) + ", årligen"] + [(and (eq? 'MINUTELY (freq rrule)) + (zero? (modulo (interval rrule) 15))) + (list " " + (each-string (/ (interval rrule) 15)) + " kvart")] + [else + (list + " " + (each-string (interval rrule) (eq? 'YEARLY (freq rrule))) + " " + (case (freq rrule) + ;; p.44 RFC 5545 + [(SECONDLY) "sekund"] + [(MINUTELY) "minut"] + [(HOURLY) "timme"] + [(DAILY) "dag"] + + ;; day offsets CAN ONLY be present when FREQ is + ;; either MONTHLY or YEARLY + [(WEEKLY) (aif (byday rrule) + (add-enumeration-punctuation + (map (compose week-day-name cdr) it)) + "vecka")] + [(MONTHLY) "månad"] + [(YEARLY) "år"] + [else "ERROR"] + ))]) + + (cond [(and (byminute rrule) + (byhour rrule)) + (list + " kl. " + (add-enumeration-punctuation + (map (lambda (pair) + (time->string + (time hour: (car pair) + minute: (cadr pair)) + "~H:~M")) + (cross-product (byhour rrule) + (byminute rrule)))))] + [(byhour rrule) + => (lambda (hours) + (list " kl. " (add-enumeration-punctuation hours)))] + [else '()]) + + (awhen (until rrule) + (format #f ", till och med ~a" + (datetime->string + ;; TODO ordinal on ~d? + it "den ~d ~B, ~Y kl. ~k:~M") + )) + (cond [(not (count rrule)) ""] + [(= 1 (count rrule)) (list ", totalt " (count rrule) " gång")] + [(count rrule) (list ", totalt " (count rrule) " gånger")] + [else "ERROR"]))))) diff --git a/module/vcomponent/util/instance.scm b/module/vcomponent/util/instance.scm index 6e1e765f..d17b672a 100644 --- a/module/vcomponent/util/instance.scm +++ b/module/vcomponent/util/instance.scm @@ -2,6 +2,7 @@ :use-module (hnh util) :use-module ((calp util config) :select (get-config)) :use-module ((oop goops) :select (make)) + :use-module (calp translation) :export (global-event-object) ) @@ -18,5 +19,5 @@ (define-public (reload) (let ((new-value (make (@@ (vcomponent util instance methods) <events>) calendar-files: (get-config 'calendar-files)))) - (display "Reload done\n" (current-error-port)) + (format (current-error-port) (_ "Reload done~%")) (set! global-event-object new-value))) diff --git a/module/vcomponent/util/instance/methods.scm b/module/vcomponent/util/instance/methods.scm index 1edad44b..57d12f6b 100644 --- a/module/vcomponent/util/instance/methods.scm +++ b/module/vcomponent/util/instance/methods.scm @@ -12,6 +12,8 @@ :use-module ((vcomponent datetime) :select (ev-time<?)) :use-module (oop goops) + :use-module (calp translation) + :export (add-event remove-event @@ -70,7 +72,7 @@ (define-method (initialize (this <events>) args) (next-method) - (format (current-error-port) "Building <events> from~%") + (format (current-error-port) (_ "Building <events> from~%")) (for calendar in (slot-ref this 'calendar-files) (format (current-error-port) " - ~a~%" calendar)) @@ -185,13 +187,13 @@ ;; save-event sets -X-HNH-FILENAME from the UID. This is fine ;; since the two events are guaranteed to have the same UID. (unless ((@ (vcomponent formats vdir save-delete) save-event) event) - (throw 'misc-error "Saving event to disk failed.")) + (throw 'misc-error (_ "Saving event to disk failed."))) (unless (eq? calendar (parent old-event)) ;; change to a new calendar (format (current-error-port) - "Unlinking old event from ~a~%" + (_ "Unlinking old event from ~a~%") (prop old-event '-X-HNH-FILENAME)) ;; NOTE that this may fail, leading to a duplicate event being ;; created (since we save beforehand). This is just a minor problem @@ -201,7 +203,7 @@ (format (current-error-port) - "Event updated ~a~%" (prop event 'UID)))] + (_ "Event updated ~a~%") (prop event 'UID)))] [else (add-event this calendar event) @@ -211,7 +213,7 @@ ;; NOTE Posibly defer save to a later point. ;; That would allow better asyncronous preformance. (unless ((@ (vcomponent formats vdir save-delete) save-event) event) - (throw 'misc-error "Saving event to disk failed.")) + (throw 'misc-error (_ "Saving event to disk failed."))) (format (current-error-port) - "Event inserted ~a~%" (prop event 'UID))])) + (_ "Event inserted ~a~%") (prop event 'UID))])) diff --git a/module/vcomponent/util/parse-cal-path.scm b/module/vcomponent/util/parse-cal-path.scm index df3fbf75..4baa647e 100644 --- a/module/vcomponent/util/parse-cal-path.scm +++ b/module/vcomponent/util/parse-cal-path.scm @@ -2,6 +2,7 @@ :use-module (hnh util) :use-module ((calp util time) :select (report-time!)) :use-module (vcomponent base) + :use-module (calp translation) :use-module ((vcomponent formats ical parse) :select (parse-calendar)) :use-module ((vcomponent formats vdir parse) @@ -19,14 +20,14 @@ (set! (prop comp '-X-HNH-SOURCETYPE) 'file) comp) ] [(directory) - (report-time! "Parsing ~a" path) + (report-time! (_ "Parsing ~a") path) (let ((comp (parse-vdir path))) (set! (prop comp '-X-HNH-SOURCETYPE) 'vdir (prop comp '-X-HNH-DIRECTORY) path) comp)] [(block-special char-special fifo socket unknown symlink) => (lambda (t) (scm-error 'misc-error "parse-cal-path" - "Can't parse file of type ~s" + (_ "Can't parse file of type ~s") (list t) #f))])) diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 00000000..6cf4a14e --- /dev/null +++ b/po/.gitignore @@ -0,0 +1 @@ +new.po diff --git a/po/sv.po b/po/sv.po new file mode 100644 index 00000000..21b73a7c --- /dev/null +++ b/po/sv.po @@ -0,0 +1,1211 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: 1.0\n" + "Report-Msgid-Bugs-To: \n" + "POT-Creation-Date: 2022-02-21 16:45+0100\n" + "PO-Revision-Date: 2022-02-21 16:50+0100\n" + "Last-Translator: Hugo Hörnquist <hugo@hornquist.se>\n" + "Language-Team: No Team <LL@li.org>\n" + "Language: Swedish\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n" + +#: module/calp/entry-points/benchmark.scm:20 +msgid "Output is by default supressed, since many fields contain way to " + "much data to read. This turns it on again." +msgstr "Som standard är utskrifter avstängda, eftersom många fällt inehåller " + "aldelles för mycket data för att läsas. Det här slår på det igen." + +#: module/calp/entry-points/benchmark.scm:22 +#: module/calp/entry-points/ical.scm:17 module/calp/entry-points/import.scm:24 +#: module/calp/entry-points/terminal.scm:15 +#: module/calp/entry-points/convert.scm:19 module/calp/entry-points/html.scm:60 +#: module/calp/entry-points/server.scm:30 module/calp/entry-points/html.scm:61 +#: module/calp/entry-points/import.scm:25 +#: module/calp/entry-points/server.scm:31 +msgid "Print this help." +msgstr "Visar den här hjälpen." + +#: module/calp/entry-points/convert.scm:17 +msgid "Input file" +msgstr "Indatafil" + +#: module/calp/entry-points/tidsrapport.scm:173 +#: module/calp/entry-points/convert.scm:18 +msgid "Output file" +msgstr "Utdatafil" + +#: module/calp/entry-points/html.scm:32 module/calp/entry-points/html.scm:33 +msgid "Start date of output." +msgstr "Startdatum för utdatan." + +#: module/calp/entry-points/html.scm:99 module/calp/entry-points/html.scm:115 +#, scheme-format +msgid "Writing to [~a]~%" +msgstr "Skriver till [~a]~%" + +#: module/calp/entry-points/html.scm:167 module/calp/entry-points/html.scm:183 +#, scheme-format +msgid "Unknown html style: ~a" +msgstr "Okänd html-stil: ~a" + +#: module/calp/entry-points/html.scm:169 module/calp/entry-points/html.scm:185 +msgid "all done" +msgstr "Allt klart" + +#: module/calp/entry-points/ical.scm:15 +msgid "Returns all elements between these two dates." +msgstr "Returnerar alla element mellan de två datumen." + +#: module/calp/entry-points/import.scm:20 +#: module/calp/entry-points/import.scm:21 +msgid "Name of calendar to import into" +msgstr "Namn på kalendar att importera till" + +#: module/calp/entry-points/import.scm:22 +#: module/calp/entry-points/import.scm:23 +msgid "ics file to import" +msgstr "ics-fil att importera" + +#: module/calp/entry-points/import.scm:43 +#: module/calp/entry-points/import.scm:44 +#, scheme-format +msgid "No calendar named ~s~%" +msgstr "Ingen kallender vid namn ~s~%" + +#: module/calp/entry-points/import.scm:48 +#: module/calp/entry-points/import.scm:49 +#, scheme-format +msgid "About to import the following ~a events into ~a~%" +msgstr "På väg att importera ~a händelser till ~a~%" + +#: module/calp/entry-points/import.scm:54 +#: module/calp/entry-points/import.scm:55 +msgid "Continue? [Y/n] " +msgstr "Fortsätt? [J/n]" + +#. numbers as single-char doesn't work. +#: module/calp/entry-points/server.scm:26 +#: module/calp/entry-points/server.scm:27 +msgid "Use IPv6." +msgstr "Använd IPv6." + +#: module/calp/entry-points/server.scm:27 +#: module/calp/entry-points/server.scm:28 +msgid "Use IPv4." +msgstr "Använd IPv4." + +#: module/calp/entry-points/server.scm:28 +#: module/calp/entry-points/server.scm:29 +msgid "Reload events on SIGUSR1" +msgstr "Ladda om händelser vid SIGUSR1 " + +#: module/calp/entry-points/server.scm:34 +#: module/calp/entry-points/server.scm:35 +msgid "Port to which the web server should bind." +msgstr "Port till vilken webservern ska binda." + +#: module/calp/entry-points/server.scm:63 +#: module/calp/entry-points/server.scm:64 +#, scheme-format +msgid "Listening for SIGUSR1~%" +msgstr "Vakar för SIGUSR1~%" + +#: module/calp/entry-points/server.scm:68 +#: module/calp/entry-points/server.scm:69 +#, scheme-format +msgid "Received SIGUSR1, reloading calendars~%" +msgstr "Mottog SIGUSR1, laddar om kallendar~%" + +#. Arguments are +#. IP-address which we bind to +#. Port which we listen to +#. PID of this process +#. PWD of this process +#: module/calp/entry-points/server.scm:78 +#: module/calp/entry-points/server.scm:79 +#, scheme-format +msgid "Starting server on ~a:~a~%I'm ~a, runing from ~a~%" +msgstr "Startar server på ~a:~a~%Jag är ~a, körandes från ~a~%" + +#: module/calp/entry-points/terminal.scm:14 +msgid "Which date to start on." +msgstr "Vilket datum att börja på." + +#: module/calp/entry-points/text.scm:14 +msgid "Width of written text, defaults to 70 chars." +msgstr "Brädd på den \"tryckta\" texten; 70 tecken som standard." + +#: module/calp/entry-points/text.scm:18 +msgid "Prints this help." +msgstr "Visar den här hjälpen." + +#: module/calp/entry-points/tidsrapport.scm:171 +msgid "Input pdf file" +msgstr "PDF-fil att arbeta på" + +#: module/calp/entry-points/tidsrapport.scm:176 +msgid "Static data to fill fields with" +msgstr "Statisk data att fylla fälten med" + +#: module/calp/entry-points/tidsrapport.scm:183 +msgid "Search term for dynamic filling. Supports basic globbing" +msgstr "Sökterm för dynamisk ifyllning. Stödjer grundläggande \"globbar\"" + +#: module/calp/entry-points/tidsrapport.scm:209 +msgid "Template required" +msgstr "Mall krävs" + +#: module/calp/entry-points/tidsrapport.scm:237 +msgid "Needs list, not pair" +msgstr "Behöver lista, inte par" + +#: module/calp/entry-points/tidsrapport.scm:239 +msgid "Need more days" +msgstr "Behöver fler dagar" + +#: module/calp/entry-points/tidsrapport.scm:255 +msgid "Groups required in template" +msgstr "Grupper krävs i mallen" + +#. Week number prefix +#: module/calp/html/view/calendar/week.scm:36 +#: module/calp/html/view/calendar/week.scm:37 +msgid "v." +msgstr "v." + +#: module/calp/html/view/calendar.scm:76 module/calp/html/view/calendar.scm:78 +msgid "Next-start needs to be a procedure" +msgstr "Next-start måste vara en procedur" + +#: module/calp/html/view/calendar.scm:79 module/calp/html/view/calendar.scm:81 +msgid "Prev-start needs to be a procedure" +msgstr "Prev-start måste vara en procedur" + +#: module/calp/html/view/calendar.scm:90 module/calp/html/view/calendar.scm:92 +#, scheme-format +msgid "Calendar for the dates between ~a and ~a" +msgstr "Kallender för tidsintervallet ~a till ~a" + +#: module/calp/html/view/calendar.scm:154 +#: module/calp/html/view/calendar.scm:157 +msgid "Page generated " +msgstr "Sidan genererad " + +#: module/calp/html/view/calendar.scm:157 +#: module/calp/html/view/calendar.scm:160 +msgid "Current time " +msgstr "Nuvarande tid " + +#: module/calp/html/view/calendar.scm:159 +#: module/calp/html/view/calendar.scm:162 +msgid "Source Code" +msgstr "Källkod" + +#. Button to view week +#: module/calp/html/view/calendar.scm:170 +#: module/calp/html/view/calendar.scm:173 +msgid "Week" +msgstr "Veckovy" + +#. button to view month +#: module/calp/html/view/calendar.scm:174 +#: module/calp/html/view/calendar.scm:177 +msgid "Month" +msgstr "Månadsvy" + +#. Button to go to today +#: module/calp/html/view/calendar.scm:184 +#: module/calp/html/view/calendar.scm:187 +msgid "Today" +msgstr "Idag" + +#: module/calp/html/view/calendar.scm:204 +#: module/calp/html/view/calendar.scm:207 +msgid "Month overview" +msgstr "Månaden i översikt" + +#. Header of small calendar +#: module/calp/html/view/calendar.scm:208 +#: module/calp/html/view/calendar.scm:211 +#, scheme-format +msgid "~B ~Y" +msgstr "~B ~Y" + +#. Search placeholder +#: module/calp/html/view/calendar.scm:234 +#: module/calp/html/view/calendar.scm:237 +msgid "Search" +msgstr "Sök" + +#: module/calp/html/view/calendar.scm:240 +#: module/calp/html/view/calendar.scm:243 +msgid "Option sliders" +msgstr "Inställningsreglage" + +#: module/calp/html/view/calendar.scm:243 +#: module/calp/html/view/calendar.scm:246 +msgid "Event blankspace" +msgstr "Händelsetomrum" + +#: module/calp/html/view/calendar.scm:252 +#: module/calp/html/view/calendar.scm:255 +msgid "Fontsize" +msgstr "Typsnittsstorlek" + +#: module/calp/html/view/calendar.scm:263 +#: module/calp/html/view/calendar.scm:266 +msgid "Calendar list" +msgstr "Kallenderlista" + +#: module/calp/html/view/calendar.scm:289 +#: module/calp/html/view/calendar.scm:292 +msgid "Earlier" +msgstr "Tidigare" + +#: module/calp/html/view/search.scm:28 +msgid "Search results" +msgstr "Sökresultat" + +#: module/calp/html/view/search.scm:31 +msgid "Show today" +msgstr "Till idag" + +#: module/calp/html/view/search.scm:32 +msgid "Search term" +msgstr "Sökterm" + +#: module/calp/html/view/search.scm:39 +msgid "limit to future occurences" +msgstr "Begränsda till framtida instanser" + +#: module/calp/html/view/search.scm:43 +msgid "Error searching" +msgstr "Misslyckades söka" + +#: module/calp/html/view/search.scm:46 +#, scheme-format +msgid "Result (page ~a)" +msgstr "Resultat (sida ~a)" + +#: module/calp/html/components.scm:61 +msgid "Only give one of onclick, href and submit." +msgstr "onlick, href, och submit är ömsesidigt uteslutande." + +#: module/calp/html/config.scm:9 +msgid "Places the generated thingy in debug mode" +msgstr "Placerar den genererade mojängen i debug-läge" + +#: module/calp/html/config.scm:17 +msgid "Makes the document editable" +msgstr "Gör dokumentet redigerbart" + +#: module/calp/html/util.scm:34 module/calp/html/util.scm:35 +#, scheme-format +msgid "Error calculating foreground color?~%~s~%" +msgstr "Misslyckades beräkna förgrundsfärg?~%~s~%" + +#. Compact event list date + time +#: module/calp/html/vcomponent.scm:50 module/calp/html/vcomponent.scm:73 +msgid "~Y-~m-~d ~H:~M" +msgstr "~Y-~m-~d ~H:~M" + +#. Button for viewing calendar, accompanied by a calendar icon +#: module/calp/html/vcomponent.scm:55 module/calp/html/vcomponent.scm:78 +msgid "View" +msgstr "Visa" + +#: module/calp/html/vcomponent.scm:126 module/calp/html/vcomponent.scm:149 +msgid "Location: " +msgstr "Plats: " + +#: module/calp/html/vcomponent.scm:203 module/calp/html/vcomponent.scm:226 +msgid "Last modified" +msgstr "Senast ändrad" + +#. Last modified datetime +#: module/calp/html/vcomponent.scm:206 module/calp/html/vcomponent.scm:229 +msgid "~1 ~H:~M" +msgstr "~1 ~H:~M" + +#: module/calp/html/vcomponent.scm:303 module/calp/html/vcomponent.scm:520 +#: module/calp/html/vcomponent.scm:326 module/calp/html/vcomponent.scm:543 +msgid "Recurrences" +msgstr "Upprepningar" + +#. NOTE flytta "muffarna" utanför +#: module/calp/html/vcomponent.scm:375 module/calp/html/vcomponent.scm:398 +msgid "- Choose a Calendar -" +msgstr "- Välj en kallender -" + +#: module/calp/html/vcomponent.scm:385 module/calp/html/vcomponent.scm:408 +msgid "Summary" +msgstr "Sammanfattning" + +#: module/calp/html/vcomponent.scm:394 module/calp/html/vcomponent.scm:417 +msgid "Start time" +msgstr "Starttid" + +#: module/calp/html/vcomponent.scm:400 module/calp/html/vcomponent.scm:423 +msgid "End time" +msgstr "Sluttid" + +#: module/calp/html/vcomponent.scm:406 module/calp/html/vcomponent.scm:429 +msgid "Whole day?" +msgstr "Heldag?" + +#: module/calp/html/vcomponent.scm:411 module/calp/html/vcomponent.scm:434 +msgid "Recurring?" +msgstr "Upprepande?" + +#: module/calp/html/vcomponent.scm:419 module/calp/html/vcomponent.scm:420 +#: module/calp/terminal.scm:149 module/calp/terminal.scm:146 +#: module/calp/html/vcomponent.scm:442 module/calp/html/vcomponent.scm:443 +msgid "Location" +msgstr "Plats" + +#: module/calp/html/vcomponent.scm:428 module/calp/html/vcomponent.scm:429 +#: module/calp/html/vcomponent.scm:451 module/calp/html/vcomponent.scm:452 +msgid "Description" +msgstr "Beskrivning" + +#: module/calp/html/vcomponent.scm:436 module/calp/html/vcomponent.scm:459 +msgid "Categories" +msgstr "Kattegorier" + +#: module/calp/html/vcomponent.scm:441 module/calp/html/vcomponent.scm:464 +msgid "Category" +msgstr "Kattegori" + +#: module/calp/html/vcomponent.scm:522 module/calp/html/vcomponent.scm:545 +msgid "Frequency" +msgstr "Frekvens" + +#: module/calp/html/vcomponent.scm:528 module/calp/html/vcomponent.scm:551 +msgid "Until" +msgstr "Till och med" + +#: module/calp/html/vcomponent.scm:531 module/calp/html/vcomponent.scm:554 +msgid "Conut" +msgstr "Antal" + +#: module/calp/html/vcomponent.scm:534 module/calp/html/vcomponent.scm:557 +msgid "Interval" +msgstr "Intervall" + +#: module/calp/html/vcomponent.scm:548 module/calp/html/vcomponent.scm:571 +msgid "By Second" +msgstr "Per sekund" + +#: module/calp/html/vcomponent.scm:549 module/calp/html/vcomponent.scm:572 +msgid "By Minute" +msgstr "Per minut" + +#: module/calp/html/vcomponent.scm:550 module/calp/html/vcomponent.scm:573 +msgid "By Hour" +msgstr "Per timme" + +#: module/calp/html/vcomponent.scm:551 module/calp/html/vcomponent.scm:574 +msgid "By Month Day" +msgstr "Per månadsdag" + +#. except 0 +#: module/calp/html/vcomponent.scm:552 module/calp/html/vcomponent.scm:575 +msgid "By Year Day" +msgstr "Per årsdag" + +#. except 0 +#: module/calp/html/vcomponent.scm:553 module/calp/html/vcomponent.scm:576 +msgid "By Week Number" +msgstr "Per veckonummer" + +#. except 0 +#: module/calp/html/vcomponent.scm:554 module/calp/html/vcomponent.scm:577 +msgid "By Month" +msgstr "Per månad" + +#: module/calp/html/vcomponent.scm:555 module/calp/html/vcomponent.scm:578 +msgid "By Set Position" +msgstr "Per fix-position" + +#. (dt "By Week Day") +#. (dd (input-list (@ (name "byweekday")) +#. (input (@ (type number) +#. (min -53) (max 53) ; except 0 +#. )) +#. ,(week-day-select '()) +#. )) +#: module/calp/html/vcomponent.scm:566 module/calp/html/vcomponent.scm:589 +msgid "Weekstart" +msgstr "Veckobörjan" + +#. Close this popup +#: module/calp/html/vcomponent.scm:582 module/calp/html/vcomponent.scm:605 +msgid "Close" +msgstr "Stäng" + +#. Make this popup occupy the entire screen +#: module/calp/html/vcomponent.scm:587 module/calp/html/vcomponent.scm:610 +msgid "Fullscreen" +msgstr "Fullskärm" + +#. Remove/Trash the event this popup represent +#. Think garbage can +#: module/calp/html/vcomponent.scm:594 module/calp/html/vcomponent.scm:617 +msgid "Remove" +msgstr "Ta bort" + +#: module/calp/html/vcomponent.scm:599 module/calp/html/vcomponent.scm:622 +msgid "Overview" +msgstr "Översikt" + +#: module/calp/html/vcomponent.scm:603 module/calp/html/vcomponent.scm:626 +msgid "Edit" +msgstr "Redigera" + +#: module/calp/html/vcomponent.scm:609 module/calp/html/vcomponent.scm:632 +msgid "Changelog" +msgstr "Händelseförlopp" + +#: module/calp/html/vcomponent.scm:613 module/calp/html/vcomponent.scm:636 +msgid "Debug" +msgstr "Debug" + +#: module/calp/server/routes.scm:53 module/calp/server/routes.scm:58 +msgid "Name" +msgstr "Namn" + +#. File permissions, should be about as long as three digits +#: module/calp/server/routes.scm:55 module/calp/server/routes.scm:60 +msgid "Perm" +msgstr "Mod" + +#: module/calp/server/routes.scm:69 +#, scheme-format +msgid "Scandir argument invalid or not directory: ~a" +msgstr "Scandir:s argument ogilgit eller inte katalog: ~a" + +#: module/calp/server/routes.scm:103 module/calp/server/routes.scm:134 +msgid "Go to Today" +msgstr "Gå till idag" + +#: module/calp/server/routes.scm:156 module/calp/server/routes.scm:187 +msgid "uid required" +msgstr "uid krävs" + +#: module/calp/server/routes.scm:168 module/calp/server/routes.scm:264 +#: module/calp/server/routes.scm:297 module/calp/server/routes.scm:199 +#: module/vcomponent/util/instance/methods.scm:190 +#: module/vcomponent/util/instance/methods.scm:216 +msgid "Saving event to disk failed." +msgstr "Misslyckades spara händelse till disk." + +#: module/calp/server/routes.scm:171 module/calp/server/routes.scm:202 +#, scheme-format +msgid "No event with UID '~a'" +msgstr "Ingen händelse med UID '~a'" + +#: module/calp/server/routes.scm:179 module/calp/server/routes.scm:210 +msgid "Both 'cal' and 'data' required" +msgstr "Både 'cal' och 'data' obligatoriska" + +#: module/calp/server/routes.scm:192 module/calp/server/routes.scm:223 +#, scheme-format +msgid "No calendar with name [~a]" +msgstr "Ingen kallender heter [~a]" + +#: module/calp/server/routes.scm:223 module/calp/server/routes.scm:254 +msgid "XML parse error" +msgstr "XML inläsningsfel" + +#: module/calp/server/routes.scm:228 module/calp/server/routes.scm:259 +msgid "Object not a VEVENT" +msgstr "Objektet är inte ett VEVENT" + +#. unlinks (removes) a single event, argument is a file name +#: module/calp/server/routes.scm:271 +#: module/vcomponent/util/instance/methods.scm:196 +#, scheme-format +msgid "Unlinking old event from ~a~%" +msgstr "Tar bort den gamla händelsen från ~a~%" + +#: module/calp/server/routes.scm:281 +#: module/vcomponent/util/instance/methods.scm:206 +#, scheme-format +msgid "Event updated ~a~%" +msgstr "Händelse uppdaterad ~a~%" + +#: module/calp/server/routes.scm:300 +#: module/vcomponent/util/instance/methods.scm:219 +#, scheme-format +msgid "Event inserted ~a~%" +msgstr "Händelse infogad ~a~%" + +#: module/calp/server/routes.scm:352 module/calp/server/routes.scm:361 +#: module/calp/server/routes.scm:335 module/calp/server/routes.scm:344 +#, scheme-format +msgid "No component with UID=~a found." +msgstr "Hittade ingen komponent med UID=~a." + +#: module/calp/util/config.scm:42 +msgid "Missing config protperty slot " +msgstr "Skanat konfigurationsegenskapsfält" + +#: module/calp/util/config.scm:57 +msgid "Pre crashed for" +msgstr "Pre krashade för" + +#: module/calp/util/config.scm:68 +msgid "Missing config" +msgstr "Saknad konfiguration" + +#: module/calp/util/config.scm:116 module/calp/util/config.scm:131 +msgid "Configuration variables" +msgstr "Konfigurationsvariabler" + +#. Configuration variable value indicator +#: module/calp/util/config.scm:129 module/calp/util/config.scm:144 +msgid "V:" +msgstr "V:" + +#: module/calp/util/exceptions.scm:7 +msgid "Crash on warnings." +msgstr "Krasha på varningar" + +#: module/calp/terminal.scm:78 module/calp/terminal.scm:75 +msgid "NO LOCATION" +msgstr "INGEN PLATS" + +#: module/calp/terminal.scm:129 module/calp/terminal.scm:126 +msgid "== Day View ==\n" +msgstr "== Dagsvy ==\n" + +#: module/calp/terminal.scm:155 module/calp/terminal.scm:152 +msgid "Start" +msgstr "Start" + +#. Event start date-time terminal view +#. Event end date-time terminal view +#. Event start date-time terminal view +#. Event end date-time terminal view +#: module/calp/terminal.scm:160 module/calp/terminal.scm:168 +#: module/calp/terminal.scm:157 module/calp/terminal.scm:165 +msgid "~Y-~m-~d ~H:~M:~S" +msgstr "~Y-~m-~d ~H:~M:~S" + +#: module/calp/terminal.scm:163 module/calp/terminal.scm:160 +msgid "End" +msgstr "Slut" + +#: module/calp/terminal.scm:211 module/calp/terminal.scm:208 +msgid "quick search: " +msgstr "Snabbsök: " + +#: module/calp/terminal.scm:218 module/calp/terminal.scm:215 +msgid "search: " +msgstr "sök: " + +#: module/calp/terminal.scm:264 module/calp/terminal.scm:261 +msgid "== Search View ==\n" +msgstr "== Sökvy ==\n" + +#: module/calp/repl.scm:16 +#, scheme-format +msgid "Starting REPL server at ~a~%" +msgstr "Startar REPL-server på ~a~%" + +#: module/calp/repl.scm:26 +#, scheme-format +msgid "Failed to unlink ~a" +msgstr "Misslyckades med att avlänka ~a" + +#: module/calp/repl.scm:30 module/calp/repl.scm:31 +msgid "Empty address?" +msgstr "Tom address?" + +#. currently impossible +#: module/calp/repl.scm:35 module/calp/repl.scm:36 +msgid "How did you get here?" +msgstr "Hur hamnade du här?" + +#: module/calp/main.scm:51 +msgid "Path to alterantive configuration file to load instead of the " + "default one." +msgstr "Sökväg till alternativ konfigurationsfil." + +#: module/calp/main.scm:65 +#, scheme-format +msgid "Display version, which is ~a btw." +msgstr "Visar version, vilket är ~a helt apropå." + +#: module/calp/main.scm:71 +msgid "Print this help" +msgstr "Visar den här hjälpen." + +#: module/calp/main.scm:123 module/calp/main.scm:124 +#, scheme-format +msgid "Configuration file ~a missing" +msgstr "Konfigurationsfilen ~a saknas" + +#. Two arguments: +#. Configuration file path, +#. thrown error arguments +#: module/calp/main.scm:171 module/calp/main.scm:174 +#, scheme-format +msgid "Failed loading config file ~a~%~s~%" +msgstr "Misslyckades med att ladda konfigurationsfilen ~a~%~s~%" + +#: module/calp/main.scm:212 module/calp/main.scm:215 +#, scheme-format +msgid "Calp version ~a~%" +msgstr "Calp version ~a~%" + +#: module/calp/main.scm:218 module/calp/main.scm:222 +#, scheme-format +msgid "tzget not installed, please put it in one of ~a" +msgstr "tzget är inte intstalleratt, vänligen placera programmet i en av ~a" + +#: module/calp/main.scm:250 module/calp/main.scm:258 +#, scheme-format +msgid "Unsupported mode of operation: ~a~%" +msgstr "Orimligt subbkomando: ~a~%" + +#: module/calp/main.scm:265 module/calp/main.scm:273 +msgid "Program start" +msgstr "Programstart" + +#: module/datetime/instance.scm:28 +msgid "Default zoneinfo only available when tz-dir and tz-list are " + "configured" +msgstr "Standardzoninfo endast tillgängligt när tz-dir och tz-list är satta" + +#: module/datetime/zic.scm:166 module/datetime/zic.scm:339 +#: module/datetime/zic.scm:170 module/datetime/zic.scm:345 +msgid "what even is \"Standard time\"‽" +msgstr "Vad är ens \"Standardtid\"‽" + +#. NOTE an earlier version of the code the parsers for those. +#. They were removed since they were unused, uneeded, and was +#. technical dept. +#: module/datetime/zic.scm:262 +#, scheme-format +msgid "Invalid key ~a. Note that leap seconds and\n" + "expries rules aren't yet implemented." +msgstr "Ogiltig nyckel ~a. Notera att skottsekunder och utgångsreglerännu " + "inte är implementerade." + +#: module/datetime/zic.scm:299 module/datetime/zic.scm:305 +#, scheme-format +msgid "Unresolved link, target missing ~a -> ~a" +msgstr "Ohanterad länk, saknar mål ~a -> ~a" + +#: module/datetime/zic.scm:360 module/datetime/zic.scm:367 +msgid "Check your input" +msgstr "Kontrollera din input" + +#: module/datetime/zic.scm:384 module/datetime/zic.scm:392 +msgid "Counting backward for RRULES unsupported" +msgstr "Att räkna baklänges stdöjs inte för RRULES" + +#. NOTE No zones seem to currently use %z formatting. +#. '%z' is NOT a format string, but information about another format string. +#: module/datetime/zic.scm:403 module/datetime/zic.scm:411 +msgid "%z not yet implemented" +msgstr "%z ännu ej implementerat" + +#: module/datetime/zic.scm:406 +msgid "Invalid format char" +msgstr "Ogiltigt formatteringstecken" + +#: module/datetime/timespec.scm:37 +msgid "Adding timespecs of differing types" +msgstr "Lägger till tidsspecifikationer av olika typer" + +#. Warning message for failure to format description. +#. First argument is name of warning/error, +#. second is error arguments +#: module/vcomponent/datetime/output.scm:51 +#: module/vcomponent/datetime/output.scm:58 +#: module/vcomponent/datetime/output.scm:50 +#, scheme-format +msgid "~a on formatting description, ~s" +msgstr "~a vid formattering av beskrivning, ~s" + +#: module/vcomponent/formats/common/types.scm:15 +msgid "Binary field not marked ENCODING=BASE64" +msgstr "Binärdatefält ej markerat ENCODING=BASE64" + +#: module/vcomponent/formats/common/types.scm:27 +#, scheme-format +msgid "~a invalid boolean" +msgstr "~a är inte en giltig boolean" + +#: module/vcomponent/formats/common/types.scm:60 +msgid "Non integer as integer" +msgstr "Icke-heltag som heltal" + +#: module/vcomponent/formats/common/types.scm:91 +#, scheme-format +msgid "Non-escapable character: ~a" +msgstr "Tecken utan specialtolkning: ~a" + +#: module/vcomponent/formats/common/types.scm:140 +msgid "No parser for type" +msgstr "Ingen inläsare för typ" + +#: module/vcomponent/formats/ical/output.scm:94 +#: module/vcomponent/formats/ical/parse.scm:230 +#: module/vcomponent/formats/xcal/output.scm:73 +#: module/vcomponent/formats/ical/parse.scm:235 +#, scheme-format +msgid "Unknown key ~a" +msgstr "Okänd nyckel ~a" + +#: module/vcomponent/formats/ical/parse.scm:124 +#: module/vcomponent/formats/ical/parse.scm:126 +msgid "List in enum field" +msgstr "Lista in uppräkningsinstansfält" + +#: module/vcomponent/formats/ical/parse.scm:159 +#: module/vcomponent/formats/ical/parse.scm:162 +#, scheme-format +msgid "List in non-list field: ~s" +msgstr "Lista i fält för icke-lista: ~s" + +#: module/vcomponent/formats/ical/parse.scm:196 +#: module/vcomponent/formats/ical/parse.scm:200 +msgid "TODO Implement REQUEST-STATUS" +msgstr "TODO implementera REQUEST-STATUS" + +#. arguments: +#. linedata +#. ~? +#. source line +#. source file +#: module/vcomponent/formats/ical/parse.scm:283 +#: module/vcomponent/formats/ical/parse.scm:288 +#, scheme-format +msgid "WARNING parse error around ~a\n" + " ~?\n" + " line ~a ~a~%" +msgstr "VARNING inläsningsfel runt ~a ~? rad ~a ~a~%" + +#. arguments +#. linedata +#. ~? +#. source line +#. source file +#: module/vcomponent/formats/ical/parse.scm:337 +#: module/vcomponent/formats/ical/parse.scm:342 +#, scheme-format +msgid "ERROR parse error around ~a\n" + " ~?\n" + " line ~a ~a\n" + " Defaulting to string~%" +msgstr "FEL inläsningsfel runt ~a ~? rad ~a ~a Faller tilbaka på sträng~%" + +#: module/vcomponent/formats/ical/types.scm:40 +msgid "PERIOD writer not yet implemented" +msgstr "PERIOD-formaterrare ännu ej implementerad" + +#: module/vcomponent/formats/ical/types.scm:97 +#: module/vcomponent/formats/xcal/types.scm:55 +msgid "No writer for type" +msgstr "Ingen formatterare för typ" + +#: module/vcomponent/formats/vdir/parse.scm:62 +#: module/vcomponent/formats/vdir/parse.scm:66 +#, scheme-format +msgid "No events in component~%~a" +msgstr "Inga händelser i komponenten~%~a" + +#: module/vcomponent/formats/xcal/parse.scm:87 +#: module/vcomponent/formats/xcal/parse.scm:86 +#, scheme-format +msgid "Invalid type ~a, with value ~a" +msgstr "Ogiltig typ ~a, med värde ~a" + +#. TODO +#: module/vcomponent/formats/xcal/parse.scm:157 +#: module/vcomponent/formats/xcal/parse.scm:159 +msgid "Request status not yet implemented" +msgstr "Statusbegäran ännu ej implementerad" + +#: module/vcomponent/util/instance/methods.scm:55 +#: module/vcomponent/util/instance/methods.scm:75 +#, scheme-format +msgid "Building <events> from~%" +msgstr "Bygger <events> från~%" + +#: module/vcomponent/util/instance.scm:22 +#, scheme-format +msgid "Reload done~%" +msgstr "Omladdning färdig~%" + +#: module/vcomponent/util/parse-cal-path.scm:23 +#, scheme-format +msgid "Parsing ~a" +msgstr "Läser ~a" + +#: module/vcomponent/util/parse-cal-path.scm:29 +msgid "Can't parse file of type " +msgstr "Kan inte läsa fil av typen " + +msgid "Can't give week together with day or time" +msgstr "Kan inte ge vecka tillsamans med dag eller tid" + +#: module/calp/html/vcomponent.scm:512 module/calp/html/vcomponent.scm:535 +msgid "Last Modified" +msgstr "Senast ändrad" + +#: module/calp/main.scm:84 +msgid "Usage: <b>calp</b> [ <i>flags</i> ] <i>mode</i> [ <i>mode flags</i> ]" +msgstr "Användning: <b>calp</b> [ <i>flaggor</i> ] <i>subkommando</i> " + "[ <i>Subkommandosflaggor</i> ]" + +#. Header for following list of modes of operation +#: module/calp/main.scm:87 +msgid "Modes" +msgstr "Subkommandon" + +#: module/calp/main.scm:89 +msgid "<p><b>html</b> reads calendar files from disk, and writes them to " + "static HTML files.</p>" +msgstr "<p><b>html</b> läser kalenderfiler från disk, och matar ur sig " + "statiska HTML-filer.</p>" + +#: module/calp/main.scm:90 +msgid "<p><b>terminal</b> loads the calendars, and starts an interactive " + "terminal interface.</p>" +msgstr "<p><b>terminal</b> laddar kalendrar, och startar ett en interaktiv " + "miljö i terminalen.</p>" + +#: module/calp/main.scm:94 +msgid "<p><b>ical</b> loads the calendar database, and immediately\n" + "re-serializes it back into iCAL format. Useful for merging calendars." + "</p>" +msgstr "<p><b>ical</b> laddar kallenderdatabasen, och återserialiserar den " + "omedelbart tillbaka till iCAL. Användbart för att slå samman " + "kallendrar.</p>" + +#. Header for list of available flags. +#. Actual list is auto generated elsewhere. +#: module/calp/main.scm:105 +msgid "Flags" +msgstr "Flaggor" + +#: module/calp/terminal.scm:320 module/calp/terminal.scm:317 +msgid "loading..." +msgstr "laddar..." + +#: module/datetime/instance.scm:12 +msgid "List of default zoneinfo files to be parsed" +msgstr "Lista av standardzoninfofiler att ladda" + +#: module/calp/main.scm:98 +msgid "<p><b>server</b> starts an HTTP server which dynamically loads and\n" + "displays events. The <i>/month/{date}.html</i> & <i>/week/{date}." + "html</i> runs\n" + "the same output code as <b>html</b>. While the <i>/calendar/{uid}." + "ics</i> uses\n" + "the same code as <b>ical</b>.</p>" +msgstr "<p><b>server</b> startar en HTTP-server vilken dynamiskt laddar och " + "visar händelser. Addresserna <i>/month/{date}.html</i> & <i>/" + "week/{date}.html</i> kör samma utskriftskod som <b>html</b>. Emedans " + "<i>/calendar/{uid}.ics</i> delar kod med <b>ical</b>.</p>" + +#: module/calp/entry-points/text.scm:16 +msgid "<group>Read from <i>file</i> instead of standard input.</group>" +msgstr "<group>Läs fran <i>file</i> istället för standard-input.</group>" + +#: module/calp/entry-points/tidsrapport.scm:179 +msgid "<group>Map between real field names and human readable names.<br/>\n" + "If data is given, but not trans, then data is assumed to be in a " + "correct format</group>" +msgstr "<group>Översättningstabell mellan verkliga fältnamn och mänskligt " + "läsbara namn.<br/>Om data är given, men inte översättingstabellen, " + "så antas datan redan ha korrekta nycklar</group>" + +#: module/calp/entry-points/convert.scm:14 +msgid "<group>Input format (otherwise infered from <i>infile</i>)</group>" +msgstr "<group>Inputformat (härleds annars från <i>infile</i>)</group>" + +#: module/calp/entry-points/convert.scm:16 +msgid "<group>Output format (otherwise infered from <i>outfile</i>)</group>" +msgstr "<group>Utadataformat (härleds annars från <i>outfile</i>)</group>" + +#: module/calp/entry-points/html.scm:35 module/calp/entry-points/html.scm:36 +msgid "<group>How many pages should be rendered.\n" + "If --style=<b>week</b> and --from=<b>2020-04-27</b>;\n" + "then --count=<b>4</b> would render the four pages\n" + "2020-04-27, 2020-05-04, 2020-05-11, and 2020-05-25.\n" + "Defaults to 12 to give a whole year when --style=<b>month</b></group>" +msgstr "<group>Antal sidor att rendera.Om --style=<b>week</b> och --" + "from=<b>2020-04-27</b> kommer--count=<b>4</b> rendera de fyra " + "sidorna2020-04-27, 2020-05-04, 2020-05-11, och 2020-05-25.Antar " + "värdet 12 för att ge ett helt år då --style=<b>month</b></group>" + +#: module/calp/entry-points/html.scm:42 module/calp/entry-points/html.scm:43 +msgid "<group>Directory where html files should end up. Default to <b>./" + "html</b></group>" +msgstr "<group>Katalog där html-filer ska placeras. Har standardvärder <b>./" + "html</b></group>" + +#: module/calp/entry-points/html.scm:46 module/calp/entry-points/html.scm:47 +msgid "<group>How the body of the HTML page should be layed out.\n" + "<br/><b>week</b>\n" + "gives a horizontally scrolling page with 7 elements, where each has " + "events\n" + "graphically laid out hour by hour.\n" + "<br/><b>table</b>\n" + "gives a month in overview as a table. Each block contains the events " + "for the\n" + "given day, in order of start time. They are however not graphically " + "sized.\n" + "<br/><b>wide</b>\n" + "is the same as week, but gives a full month.</group>" +msgstr "<group>Hur HTML-sidans komponenter ska placeras.<br/><b>week</b> ger " + "en horisentellt skrollande sida med 7 element, där varje element har " + "händelser grafiskt utlaggda.<br/><b>table</b> ger en månadsöversikt " + "som en tabell, där varje cell visar den dagens händelser i ordning, " + "dock inte grafiskt skalade.<br/><b>wide</b> Motsvarande som week, " + "men för en hel månad</group>" + +#: module/calp/entry-points/html.scm:57 module/calp/entry-points/html.scm:58 +msgid "<group>Creates a standalone document instead of an HTML fragment\n" + "for embedding in a larger page. Currently only applies to the " + "<i>small</i> style</group>" +msgstr "<group>Skapar ett fristående dokument istället för ett HTML-fragment " + "för inbäddning i andra sidor. Fungerar för nuvarande bara med stilen " + "<i>small</i></group>" + +#: module/calp/entry-points/server.scm:19 +#: module/calp/entry-points/server.scm:20 +msgid "<group>Bind to TCP port, defaults to <i>8080</i>.\n" + "<br/>Can also be set through the config variable\n" + "<i>port</i>.</group>" +msgstr "<group>Bind till en TCP-port, och blir <i>8080</i> om osatt.<br/>Kan " + "även sättas genom konfigurationsfältet <i>port</i>.</group>" + +#: module/calp/entry-points/server.scm:23 +#: module/calp/entry-points/server.scm:24 +msgid "<group>Address to use, defaults to <i>0.0.0.0</i> for IPv4,\n" + "and <i>[::]</i> for IPv6</group>" +msgstr "<group>Address att använda, utgår från <i>0.0.0.0</i> för IPv4, samt " + "<i>[::]</i> för IPv6</group>" + +#: module/calp/main.scm:38 +msgid "<group>Run the program within Guile's built in statical\n" + "profiler. Display style is one of <b>flat</b> or <b>tree</b>.</group>" +msgstr "<group>Kör programmet i Guiles inbyggda statiska profilerare.\n" + "Visningsstil är <b>flat</b> eller <b>tree</b>.</group>" + +#: module/calp/main.scm:42 +msgid "<group>Start a Guile repl which can be connected to, defaults to " + "the\n" + "unix socket <i>/run/user/${UID}/calp-${PID}</i>, but it can be bound " + "to any\n" + "unix or TCP socket. ((@ (vcomponent util instance) global-event-" + "object)) should\n" + "contain all events.\n" + "<br/>\n" + "<b>Should NOT be used in production.</b></group>" +msgstr "<group>Startar en Guile-REPL för anslutningar. Binder som standard " + "till unix-sockeln <i>/run/user/${UID}/calp-${PID}</i>, man kan " + "bindas till valfri unix eller TCP sockel. ((@ (vcomponent util " + "instance) global-event-object)) bör inehålla alla händelser<br/" + "><b>Ska INTE användas i produktion.</b></group>" + +#: module/calp/main.scm:59 +msgid "<group>Set configuration options, on the form <i>key</i>=<i>value</" + "i>\n" + "as if they were set in the config file. These options have priority " + "over those\n" + "from the file. Can <i>not</i> be given with an equal after --" + "option. <br/>Can\n" + "be given multiple times.</group>" +msgstr "<group>Sätter konfigurationsvariabler, på formen <i>nyckel</" + "i>=<i>värde</i>, motsvarande om de hade satts i konfigurationsfilen. " + "Dock har de här prioritet över dem från filen.\n" + "Kan <i>inte</i> ges med ett lika-med-tecken efter --option.<br/>Kan " + "ges flera gånger.</group>" + +#: module/calp/main.scm:73 +msgid "<group>Print known configuration variables.\n" + "<br/><b>NOTE</b>:\n" + "Only those configuration variables which are loaded will be shown, " + "more might be\n" + "available</group>" +msgstr "<group>Visar kända konfigurationsvariabler.<br/><b>NOTERA</b>:\n" + "Endast de konfigurationsvariablerna vilka har laddats in kommer " + "visas, fler kan vara finnas</group>" + +#: module/calp/main.scm:91 +msgid "[UNTESTED]<br/><p><b>import</b>s a calendar object into the database." + "</p>" +msgstr "[OTESTAD]<br/><p><b>import</b>erar ett kallenderobjekt till " + "databasen.</p>" + +#: module/calp/main.scm:92 +msgid "<p><b>text</b> formats and justifies what it's given on standard " + "input,\n" + "and writes it to standard output. Similar to this text.</p>" +msgstr "<p><b>text</b> formatterar och justerar det den får på standard-" + "input, och skriver det till standard-output, på motsvarande stil som " + "den här texten.</p>" + +#: module/calp/main.scm:96 +msgid "<p><b>benchmark</b> <i>module</i><br/>Runs the procedure 'run-" + "benchmark'\n" + "from the module (calp benchmark <i>module</i>).</p>" +msgstr "<p><b>benchmark</b> <i>modul</i><br/>Kör proceduren 'run-benchmark' " + "från modulen (calp benchmark <i>modul</i>).</p>" + +#. Week view header format +#. start date metainfo +#. end date metainfo +#. Generation data +#. Compact event list date only +#. Header for sidebar day +#. Week view header format +#. start date metainfo +#. end date metainfo +#. Generation data +#. Compact event list date only +#. Header for sidebar day +#. start = end, only return one value +#: module/calp/html/view/calendar/week.scm:50 +#: module/calp/html/view/calendar.scm:92 module/calp/html/view/calendar.scm:94 +#: module/calp/html/view/calendar.scm:156 module/calp/html/vcomponent.scm:52 +#: module/calp/html/vcomponent.scm:218 module/vcomponent/datetime/output.scm:69 +#: module/vcomponent/datetime/output.scm:70 +#: module/vcomponent/datetime/output.scm:71 +#: module/calp/html/view/calendar/week.scm:51 +#: module/calp/html/view/calendar.scm:96 module/calp/html/view/calendar.scm:159 +#: module/calp/html/vcomponent.scm:75 module/calp/html/vcomponent.scm:241 +#: module/vcomponent/datetime/output.scm:61 +#: module/vcomponent/datetime/output.scm:62 +#: module/vcomponent/datetime/output.scm:63 +msgid "~Y-~m-~d" +msgstr "~Y-~m-~d" + +#. [FRR] +#. Part of the sentance "Repeated [every two weeks], except on ~a, ~a & ~a" +#. See everything tagged [FRR] +#: module/vcomponent/datetime/output.scm:24 +#: module/vcomponent/datetime/output.scm:16 +msgid "Repeated " +msgstr "Upprepas " + +#. See [FRR] +#: module/vcomponent/datetime/output.scm:29 +#: module/vcomponent/datetime/output.scm:21 +msgid ", except on " +msgstr ", undantaget " + +#. [FRR] Exception date without time +#: module/vcomponent/datetime/output.scm:35 +#: module/vcomponent/datetime/output.scm:44 +#: module/vcomponent/datetime/output.scm:27 +#: module/vcomponent/datetime/output.scm:36 +#, scheme-format +msgid "~e ~b" +msgstr "~e ~b" + +#. [FRR] Exception date with time +#: module/vcomponent/datetime/output.scm:42 +#: module/vcomponent/datetime/output.scm:34 +msgid "~e ~b ~k:~M" +msgstr "~e ~b ~k:~M" + +#: module/vcomponent/datetime/output.scm:79 +#: module/vcomponent/datetime/output.scm:71 +msgid "~H:~M" +msgstr "~H:~M" + +#. Note the non-breaking space +#: module/vcomponent/datetime/output.scm:81 +#: module/vcomponent/datetime/output.scm:84 +#: module/vcomponent/datetime/output.scm:73 +#: module/vcomponent/datetime/output.scm:76 +msgid "~Y-~m-~d ~H:~M" +msgstr "~Y-~m-~d ~H:~M" + +#: module/calp/html/components.scm:62 +#, scheme-format +msgid "href and onclick are mutually exclusive. href = ~s, onclick = ~s." +msgstr "" + +#. File size +#: module/calp/server/routes.scm:62 +msgid "Size" +msgstr "" + +#: module/calp/server/routes.scm:92 +#, scheme-format +msgid "Scandir argument invalid or not directory: ~s" +msgstr "" + +#: module/calp/util/config.scm:45 +#, scheme-format +msgid "No configuration slot named ~s, when defining ~s" +msgstr "" + +#. first slot is property name, second is new +#. property value. +#: module/calp/util/config.scm:65 +#, scheme-format +msgid "Pre-property failed when setting ~s to ~s" +msgstr "" + +#: module/calp/util/config.scm:80 +#, scheme-format +msgid "No configuration item named ~s" +msgstr "" + +#: module/datetime/zic.scm:267 +#, scheme-format +msgid "Invalid key ~s. Note that leap seconds and expries rules aren't yet " + "implemented." +msgstr "" + +#. first slot is the errornous character, +#. second is the whole string, third is the index +#. of the faulty character. +#: module/datetime/zic.scm:418 +#, scheme-format +msgid "Invalid format char ~s in ~s at position ~a" +msgstr "" + +#: module/vcomponent/formats/common/types.scm:140 +#, scheme-format +msgid "No parser for type ~a" +msgstr "" + +#: module/vcomponent/formats/vdir/save-delete.scm:27 +#, scheme-format +msgid "Can only save events belonging to calendars, event uid = ~s" +msgstr "" + +#: module/vcomponent/formats/vdir/save-delete.scm:33 +#, scheme-format +msgid "Can only save events belonging to vdir calendars. Calendar is of " + "type ~s" +msgstr "" + +#: module/vcomponent/formats/vdir/save-delete.scm:53 +#, scheme-format +msgid "Can only remove events belonging to vdir calendars. Calendar is of " + "type ~s" +msgstr "" + +#: module/vcomponent/util/parse-cal-path.scm:30 +#, scheme-format +msgid "Can't parse file of type ~s" +msgstr "" |