aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile18
-rw-r--r--module/calp/entry-points/benchmark.scm8
-rw-r--r--module/calp/entry-points/convert.scm13
-rw-r--r--module/calp/entry-points/html.scm51
-rw-r--r--module/calp/entry-points/ical.scm8
-rw-r--r--module/calp/entry-points/import.scm38
-rw-r--r--module/calp/entry-points/server.scm38
-rw-r--r--module/calp/entry-points/terminal.scm7
-rw-r--r--module/calp/entry-points/text.scm15
-rw-r--r--module/calp/entry-points/tidsrapport.scm24
-rw-r--r--module/calp/html/caltable.scm4
-rw-r--r--module/calp/html/components.scm3
-rw-r--r--module/calp/html/config.scm5
-rw-r--r--module/calp/html/util.scm5
-rw-r--r--module/calp/html/vcomponent.scm103
-rw-r--r--module/calp/html/view/calendar.scm53
-rw-r--r--module/calp/html/view/calendar/week.scm9
-rw-r--r--module/calp/html/view/search.scm13
-rw-r--r--module/calp/main.scm118
-rw-r--r--module/calp/repl.scm11
-rw-r--r--module/calp/server/routes.scm40
-rw-r--r--module/calp/server/server.scm1
-rw-r--r--module/calp/terminal.scm55
-rw-r--r--module/calp/translation.scm20
-rw-r--r--module/calp/util/config.scm14
-rw-r--r--module/calp/util/exceptions.scm3
-rw-r--r--module/datetime/instance.scm5
-rw-r--r--module/datetime/timespec.scm3
-rw-r--r--module/datetime/zic.scm21
-rw-r--r--module/hnh/util/language.scm14
-rw-r--r--module/text/numbers.scm36
-rw-r--r--module/vcomponent/datetime/output.scm54
-rw-r--r--module/vcomponent/formats/common/types.scm11
-rw-r--r--module/vcomponent/formats/ical/output.scm3
-rw-r--r--module/vcomponent/formats/ical/parse.scm29
-rw-r--r--module/vcomponent/formats/ical/types.scm8
-rw-r--r--module/vcomponent/formats/vdir/parse.scm3
-rw-r--r--module/vcomponent/formats/vdir/save-delete.scm6
-rw-r--r--module/vcomponent/formats/xcal/output.scm3
-rw-r--r--module/vcomponent/formats/xcal/parse.scm5
-rw-r--r--module/vcomponent/formats/xcal/types.scm3
-rw-r--r--module/vcomponent/recurrence/display.scm154
-rw-r--r--module/vcomponent/recurrence/display/common.scm6
-rw-r--r--module/vcomponent/recurrence/display/en.scm131
-rw-r--r--module/vcomponent/recurrence/display/sv.scm139
-rw-r--r--module/vcomponent/util/instance.scm3
-rw-r--r--module/vcomponent/util/instance/methods.scm14
-rw-r--r--module/vcomponent/util/parse-cal-path.scm5
-rw-r--r--po/.gitignore1
-rw-r--r--po/sv.po1211
51 files changed, 2059 insertions, 487 deletions
diff --git a/.gitignore b/.gitignore
index 4f433a8c..b92e0bcf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
/html
coverage
obj-*
+localization
diff --git a/Makefile b/Makefile
index b7cea836..22ea4c08 100644
--- a/Makefile
+++ b/Makefile
@@ -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> &amp; <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> &amp; <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> &amp; <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 ""