guix-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

04/08: website: Add custom xgettext to extract from nested sexps for i18


From: Florian Pelz
Subject: 04/08: website: Add custom xgettext to extract from nested sexps for i18n.
Date: Wed, 15 Jul 2020 01:47:17 -0400 (EDT)

pelzflorian pushed a commit to branch wip-i18n
in repository guix-artwork.

commit b771811e96513153d9ad68ac869e7953f03ccbc3
Author: Florian Pelz <pelzflorian@pelzflorian.de>
AuthorDate: Thu Jul 2 19:17:38 2020 +0200

    website: Add custom xgettext to extract from nested sexps for i18n.
    
    * website/po/POTFILES: New file.  List apps files here.
    * website/po/LINGUAS: New file.  List en_US lingua.
    * website/po/ietf-tags.scm: New file.  Add association for en_US lingua.
    * website/scripts/sexp-xgettext.scm: New file for generating a POT file.
    (<keyword-spec>, <po-entry>, <construct-fold-state>): New record types.
    (combine-duplicate-po-entries, complex-keyword-spec?, parse-scheme-file,
    po-equal?, write-po-entry, update-ecomments-string!, update-file-name!,
    update-old-line-number!, update-line-number!, incr-line-number!,
    incr-line-number-for-each-nl!, current-ref, make-simple-po-entry,
    matching-keyword, nth-exp, more-than-one-exp?, token->string-symbol-or-keyw,
    complex-marked-list->po-entries, construct-po-entries, tag,
    construct-msgid-and-po-entries, scheme-file->po-entries): New procedures.
    (%keyword-specs, %options, %comments-line, %ecomments-string, %file-name,
    %old-line-number, %line-number, %files-from-port, %source-files,
    %output-po-entries, %output-port): New variables.
    * website/sexp-xgettext.scm: New file with module for looking up
    translations.
    (%complex-keywords, %simple-keywords, %plural-numbers, %linguas):
    New variables.
    (<construct-fold-state>, <deconstruct-fold-state>): New record types.
    (set-complex-keywords!, set-simple-keywords!, gettext-keyword?, tag,
    sexp->msgid, deconstruct): New procedures.
    (sgettext, spgettext, sngettext, snpgettext): New macro helpers.
    * website/apps/i18n.scm: New file.
    (G_, N_, C_, NC_, ietf-tags-file-contents): New syntax to use for i18n.
    (%current-ietf-tag, %current-lang, %current-lingua): New variables.
    (builder->localized-builder, builders->localized-builders,
    localized-root-path, first-value): New utility procedures.
    (<asset>, <page>): New imports from Haunt.
    * website/haunt.scm: Wrap each builder to build the locale set in LC_ALL.
    * website/.guix.scm: Make Haunt build directory writable so Haunt can
    overwrite duplicate assets.  Convert PO files to MO files and build for
    each lingua.
    * website/README: Adapt build instructions for i18n.
    * website/i18n-howto: New file with usage instructions.
---
 website/.guix.scm                                  |   77 +-
 website/README                                     |    8 +-
 website/apps/base/data.scm                         |  231 ++--
 website/apps/base/templates/about.scm              |  165 ++-
 website/apps/base/templates/components.scm         |  121 +-
 website/apps/base/templates/contact.scm            |   20 +-
 website/apps/base/templates/contribute.scm         |  388 +++---
 website/apps/base/templates/donate.scm             |  317 +++--
 website/apps/base/templates/graphics.scm           |   87 +-
 website/apps/base/templates/help.scm               |  132 +-
 website/apps/base/templates/home.scm               |  245 ++--
 website/apps/base/templates/irc.scm                |   48 +-
 website/apps/base/templates/menu.scm               |   17 +-
 website/apps/base/templates/security.scm           |  104 +-
 website/apps/base/templates/theme.scm              |   51 +-
 website/apps/base/utils.scm                        |   44 +-
 website/apps/blog/templates/components.scm         |   16 +-
 website/apps/blog/templates/feed.scm               |    3 +-
 website/apps/blog/templates/post-list.scm          |   23 +-
 website/apps/blog/templates/post.scm               |   14 +-
 website/apps/blog/templates/tag.scm                |   27 +-
 website/apps/blog/utils.scm                        |    5 +-
 website/apps/download/data.scm                     |   46 +-
 website/apps/download/templates/components.scm     |   12 +-
 .../apps/download/templates/download-latest.scm    |   73 +-
 website/apps/download/templates/download.scm       |   69 +-
 website/apps/i18n.scm                              |  132 ++
 website/apps/media/data.scm                        |   61 +-
 website/apps/media/templates/components.scm        |    5 +-
 website/apps/media/templates/screenshot.scm        |   14 +-
 .../apps/media/templates/screenshots-overview.scm  |   16 +-
 website/apps/media/templates/video-list.scm        |   15 +-
 website/apps/media/templates/video.scm             |   19 +-
 website/apps/packages/templates/components.scm     |   80 +-
 website/apps/packages/templates/detailed-index.scm |   47 +-
 .../packages/templates/detailed-package-list.scm   |   23 +-
 website/apps/packages/templates/index.scm          |   45 +-
 website/apps/packages/templates/package-list.scm   |   21 +-
 website/apps/packages/templates/package.scm        |   56 +-
 website/haunt.scm                                  |   19 +-
 website/i18n-howto.txt                             |   86 ++
 website/po/LINGUAS                                 |    3 +
 website/po/POTFILES                                |   37 +
 website/po/guix-website.pot                        | 1403 ++++++++++++++++++++
 website/po/ietf-tags.scm                           |    9 +
 website/scripts/sexp-xgettext.scm                  |  830 ++++++++++++
 website/sexp-xgettext.scm                          |  530 ++++++++
 47 files changed, 4631 insertions(+), 1163 deletions(-)

diff --git a/website/.guix.scm b/website/.guix.scm
index fcd2570..393c2b2 100644
--- a/website/.guix.scm
+++ b/website/.guix.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix web site
 ;;; Copyright © 2017, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
 ;;;
 ;;; This file is part of the GNU Guix web site.
 ;;;
@@ -18,6 +19,17 @@
 
 ;; Run 'guix build -f .guix.scm' to build the web site.
 
+(define this-directory
+  (dirname (current-filename)))
+
+;; Make sure po/LINGUAS will be found in the current working
+;; directory.
+(chdir this-directory)
+
+;; We need %linguas from the (sexp-xgettext) module.
+;; Therefore, we add its path to the load path.
+(set! %load-path (cons this-directory %load-path))
+
 (use-modules (guix) (gnu)
              (gnu packages guile)
              (gnu packages guile-xyz)
@@ -28,10 +40,10 @@
              (guix channels)
              (srfi srfi-1)
              (srfi srfi-9)
-             (ice-9 match))
-
-(define this-directory
-  (dirname (current-filename)))
+             (ice-9 match)
+             (ice-9 rdelim)
+             (ice-9 regex)
+             (sexp-xgettext))
 
 (define source
   (local-file this-directory "guix-web-site"
@@ -120,11 +132,39 @@
                                     ":"))))
             (close-pipe pipe))
 
+          ;; Make the copy writable so Haunt can overwrite duplicate assets.
+          (invoke #+(file-append (specification->package "coreutils")
+                                 "/bin/chmod")
+                  "--recursive" "u+w" ".")
+
+          ;; For translations, create MO files from PO files.
+          (for-each
+           (lambda (lingua)
+             (let* ((msgfmt #+(file-append
+                               (specification->package "gettext-minimal")
+                               "/bin/msgfmt"))
+                    (lingua-file (string-append "po/" lingua ".po"))
+                    (lang (car (string-split lingua #\_)))
+                    (lang-file (string-append "po/" lang ".po")))
+               (define (create-mo filename)
+                 (begin
+                   (invoke msgfmt filename)
+                   (mkdir-p (string-append lingua "/LC_MESSAGES"))
+                   (rename-file "messages.mo"
+                                (string-append lingua "/LC_MESSAGES/"
+                                               "guix-website.mo"))))
+               (cond
+                ((file-exists? lingua-file)
+                 (create-mo lingua-file))
+                ((file-exists? lang-file)
+                 (create-mo lang-file))
+                (else #t))))
+           (list #$@%linguas))
+
           ;; So we can read/write UTF-8 files.
           (setenv "GUIX_LOCPATH"
                   #+(file-append (specification->package "glibc-utf8-locales")
                                  "/lib/locale"))
-          (setenv "LC_ALL" "en_US.utf8")
 
           ;; Use a sane default.
           (setenv "XDG_CACHE_HOME" "/tmp/.cache")
@@ -133,14 +173,25 @@
           ;; this script was run.
           (setenv "GUIX_WEB_SITE_ROOT_PATH" #$root-path)
 
-          (format #t "Running 'haunt build'...~%")
-          (invoke #+(file-append haunt-with-latest-guile "/bin/haunt")
-                  "build")
-
-          (mkdir-p #$output)
-          (copy-recursively "/tmp/gnu.org/software/guix" #$output
-                            #:log (%make-void-port "w"))
-          (symlink "guix.html" (string-append #$output "/index.html"))))))
+          ;; Build the website for each translation.
+          (for-each
+           (lambda (lingua)
+             (begin
+               (setenv "LC_ALL" (string-append lingua ".utf8"))
+               (format #t "Running 'haunt build' for lingua ~a...~%" lingua)
+               (invoke #+(file-append haunt-with-latest-guile
+                                      "/bin/haunt")
+                       "build")
+               (mkdir-p #$output)
+               (copy-recursively "/tmp/gnu.org/software/guix" #$output
+                                 #:log (%make-void-port "w"))
+               (let ((tag (assoc-ref
+                           (call-with-input-file "po/ietf-tags.scm"
+                             (lambda (port) (read port)))
+                           lingua)))
+                 (symlink "guix.html"
+                          (string-append #$output "/" tag "/index.html")))))
+           (list #$@%linguas))))))
 
 (computed-file "guix-web-site" build
                #:guile (specification->package "guile")
diff --git a/website/README b/website/README
index 3a9d509..64b09c1 100644
--- a/website/README
+++ b/website/README
@@ -24,14 +24,18 @@ commands:
 
 #+BEGIN_EXAMPLE
 $ cd path/to/guix-artwork/website
-$ GUIX_WEB_SITE_LOCAL=yes haunt build
+$ export GUILE_LOAD_PATH=$(guix build 
guile-syntax-highlight)/share/guile/site/3.0:$GUILE_LOAD_PATH
+$ LC_ALL=en_US.utf8 GUIX_WEB_SITE_LOCAL=yes haunt build
 $ haunt serve
 #+END_EXAMPLE
 
-Then, visit http://localhost:8080/guix.html in a web browser.
+Then, visit http://localhost:8080/en/guix.html in a web browser.
 
 You can stop the server pressing ~Ctrl + C~ twice.
 
+See also the file i18n-howto.txt for information on working with
+translations.
+
 * Deploying
 
 Like the pages of many GNU websites, this website is managed through
diff --git a/website/apps/base/data.scm b/website/apps/base/data.scm
index 887ac52..268a6b8 100644
--- a/website/apps/base/data.scm
+++ b/website/apps/base/data.scm
@@ -1,10 +1,15 @@
 ;;; GNU Guix web site
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
 ;;; Initially written by sirgazil who waives all
 ;;; copyright interest on this file.
 
 (define-module (apps base data)
+  #:use-module (apps base templates components)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
+  #:use-module (srfi srfi-1)
+  #:use-module (sexp-xgettext)
   #:export (contact-media))
 
 
@@ -16,159 +21,197 @@
   (list
    ;; The first three will be featured in the home page.
    (contact
-    #:name "IRC Channel"
+    #:name (G_ "IRC Channel")
     #:description
-    '(p
-      "Join the " (code "#guix") " channel on the Freenode IRC network to chat
-      with the community about GNU Guix or to get help in
-      real-time.")
+    (G_
+     `(p
+       "Join the " (code "#guix") " channel on the Freenode IRC network to chat
+       with the community about GNU Guix or to get help in
+       real-time."))
     #:url (guix-url "contact/irc/")
     #:log guix-irc-log-url)
 
    (contact
-    #:name "Info Mailing List"
+    #:name (G_ "Info Mailing List")
     #:description
-    '(p "Subscribe to the " (code "info-guix") " low-traffic mailing
+    (G_
+     `(p
+       "Subscribe to the " (code "info-guix") " low-traffic mailing
 list to receive important announcements sent by the project maintainers (in
-English).")
+English)."))
     #:url "https://lists.gnu.org/mailman/listinfo/info-guix";
     #:log "https://lists.gnu.org/archive/html/info-guix";)
 
    (contact
-    #:name "Help Mailing List"
+    #:name (G_ "Help Mailing List")
     #:description
-    `(("de"
-       "Melden Sie sich bei der „Help“-Mailingliste an, um per E-Mail
+    ;; Compute an association list from language code to blurb.
+    ;; If possible, look up translated blurbs from the PO file.
+    ;; Fall back to old hard-coded translations.
+    (let ((original '(G_
+                      "Subscribe to the Help mailing list to get support
+from the GNU Guix community via email.  You can post messages in English
+though we also accept other languages."))
+          (lang-code '(C_ "unique lingua code like en or zh-cn" "en")))
+      (sort
+       (delete-duplicates
+        (append
+         (delete ;delete untranslated blurbs other than "en"
+          (cons original lang-code)
+          (map-in-order
+           (lambda (lingua)
+             (begin
+               (setlocale LC_ALL (string-append lingua ".utf8"))
+               (let ((out (list (gettext (string-append
+                                          (cadr lang-code) ;msgctxt
+                                          (string #\eot) ;separates msgctxt
+                                          (caddr lang-code))) ;msgid
+                                (gettext (cadr original)))))
+                 (setlocale LC_ALL "")
+                 (if (string-index (car out) #\eot) ;if untranslated
+                     (list (caddr lang-code) (cadr original)) ;use original
+                     out)))) ;else use what has been looked up via gettext
+           %linguas)
+          (lambda (to-delete b) (and ;delete where text is equal to original
+                                 (string=? (cadar to-delete) (cadr b))
+                                 ;; but language code is different
+                                 (not (string=? (cadddr to-delete) (car b))))))
+         `(("de"
+            "Melden Sie sich bei der „Help“-Mailingliste an, um per E-Mail
 gemeinschaftlichen Rat zu Guix System und Guix zu bekommen.  Sie können
 Nachrichten auch auf deutsch verfassen.")
-      ("en"
-       "Subscribe to the Help mailing list to get support from the
-GNU Guix community via email.  You can post messages in English though we
-also accept other languages.")
-      ("eo"
-       "Subskribu al la retmesaĝolisto \"Help\" por demandi helpon pri
+           ("eo"
+            "Subskribu al la retmesaĝolisto \"Help\" por demandi helpon pri
 GNU Guix al la grupo.  Vi povas skribi esperantlingve.")
-      ("es"
-       "Suscríbete a la lista de correo electrónico \"Help\" por pedir
+           ("es"
+            "Suscríbete a la lista de correo electrónico \"Help\" por pedir
 ayuda con Guix.  Puedes escribir mensajes en Español.")
-      ("fr"
-       "Abonnez-vous à la liste de diffusion « Help » pour obtenir l'aide
+           ("fr"
+            "Abonnez-vous à la liste de diffusion « Help » pour obtenir l'aide
 de la communauté sur GNU Guix par courrier électronique.  Vous
 pouvez envoyer des messages en français.")
-      ("hu"
-       "Iratkozzon fel a „Help“ levelezőlistára, hogy segítséget kaphasson
+           ("hu"
+            "Iratkozzon fel a „Help“ levelezőlistára, hogy segítséget kaphasson
 e-mailben a Guix System és a GNU Guix közösségtől. Magyarul is küldhet
 üzeneteket.")
-      ("it"
-       "Iscrivetevi alla mailing list 'Help' per essere aiutati da altri
+           ("it"
+            "Iscrivetevi alla mailing list 'Help' per essere aiutati da altri
 utenti di Guix e Guix System.  Potete scrivere sulla mailing list anche in
 italiano.")
-      ("ja"
-       "メールでGNU GuixとGuix Systemのコミュニティからサポートを受けるには、
+           ("ja"
+            "メールでGNU GuixとGuix Systemのコミュニティからサポートを受けるには、
 「Help」のメーリングリストに登録してください。
 メッセージ内容は日本語でも問題ございませんが、多言語でも受け付けております。")
-      ("nb"
-       "Meld deg på diskusjonslisten «Help» for å få råd og tips fra
+           ("nb"
+            "Meld deg på diskusjonslisten «Help» for å få råd og tips fra
 andre Guix System- og GNU Guix-brukere via e-post.  Du kan legge inn
 meldinger på norsk.")
-      ("nl"
-       "Abonneer je op de discussielijst \"Help\" om hulp te vragen
+           ("nl"
+            "Abonneer je op de discussielijst \"Help\" om hulp te vragen
 van de Guix System- en GNU Guix-gemeenschap.  Je kunt berichten sturen in
 het Nederlands.")
-      ("ru"
-       "Подпишитесь на список рассылки «Help», чтобы получить помощь от
+           ("ru"
+            "Подпишитесь на список рассылки «Help», чтобы получить помощь от
 сообщества Guix System и GNU Guix по электронной почте.  Вы можете писать на 
русском
 языке.")
-      ("zh-Hant"
-       "訂閱「Help」郵件群組以電郵從Guix System及GNU Guix社群取得支援。你可以使用
-正體、繁體中文發送訊息。"))
-
+           ("zh-Hant"
+            "訂閱「Help」郵件群組以電郵從Guix System及GNU Guix社群取得支援。你可以使用
+正體、繁體中文發送訊息。")))
+        (lambda (a b) (string=? (car a) (car b))))
+       (lambda (a b) (string<? (car a) (car b)))))
     #:url "https://lists.gnu.org/mailman/listinfo/help-guix";
     #:log "https://lists.gnu.org/archive/html/help-guix";)
 
    (contact
-    #:name "Bug Reporting"
+    #:name (G_ "Bug Reporting")
     #:description
-    '(p
-      "If you found a bug in Guix, check whether the bug is
-      already in the "
-      (a (@ (href "https://issues.guix.gnu.org";))
-        "bug database")
-      ". If it is not, please "
-      (a (@ (href "mailto:bug-guix@gnu.org";)) "report it."))
+    (G_
+     `(p
+       "If you found a bug in Guix, check whether the bug is
+       already in the "
+       ,(G_ `(a (@ (href "https://issues.guix.gnu.org";))
+                "bug database"))
+       ". If it is not, please "
+       ,(G_ `(a (@ (href "mailto:bug-guix@gnu.org";)) "report it."))))
     #:url "https://lists.gnu.org/mailman/listinfo/bug-guix";
     #:log "https://issues.guix.gnu.org/";)
 
    (contact
-    #:name "Development Mailing List"
+    #:name (G_ "Development Mailing List")
     #:description
-    '(p
-      "Discussion about the development of GNU Guix. "
-      (a (@ (href 
"https://lists.gnu.org/archive/html/bug-guix/2013-07/msg00039.html";))
-        " Until July 2013")
-      ", the bug-Guix mailing list filled that role. ")
+    (G_
+     `(p
+       "Discussion about the development of GNU Guix. "
+       ,(G_ `(a (@ (href 
"https://lists.gnu.org/archive/html/bug-guix/2013-07/msg00039.html";))
+                " Until July 2013"))
+       ", the bug-Guix mailing list filled that role. "))
     #:url "https://lists.gnu.org/mailman/listinfo/guix-devel";
     #:log "https://lists.gnu.org/archive/html/guix-devel";)
 
    (contact
-    #:name "Patches Mailing List"
+    #:name (G_ "Patches Mailing List")
     #:description
-    `(p
-      "Submission of patches.  Every message sent to this mailing list
-      leads to a new entry in our "
-      (a (@ (href "https://issues.guix.gnu.org";))
-        "patch tracking tool")
-      ".  See "
-      (a (@ (href "https://debbugs.gnu.org/Advanced.html";)) "this page")
-      " for more information on how to use it; see "
-      (a (@ (href ,(manual-url "Submitting-Patches.html")))
-         "the manual")
-      " for more information on how to submit a patch.  "
-      (a (@ (href 
"https://lists.gnu.org/archive/html/guix-devel/2017-02/msg00627.html";))
-        "Until February 2017")
-      ", the guix-devel mailing list filled that role.")
+    (G_
+     `(p
+       "Submission of patches.  Every message sent to this mailing list
+       leads to a new entry in our "
+       ,(G_ `(a (@ (href "https://issues.guix.gnu.org";))
+                "patch tracking tool"))
+       ".  See "
+       ,(G_ `(a (@ (href "https://debbugs.gnu.org/Advanced.html";)) "this 
page"))
+       " for more information on how to use it; see "
+       ,(G_ (manual-href "the manual" (G_ "en") (G_ 
"Submitting-Patches.html")))
+       " for more information on how to submit a patch.  "
+       ,(G_
+         `(a (@ (href 
"https://lists.gnu.org/archive/html/guix-devel/2017-02/msg00627.html";))
+             "Until February 2017"))
+       ", the guix-devel mailing list filled that role."))
     #:url "https://lists.gnu.org/mailman/listinfo/guix-patches";
     #:log "https://issues.guix.gnu.org";)
 
    (contact
-    #:name "Science Mailing List"
+    #:name (G_ "Science Mailing List")
     #:description
-    '(p
-      "Discussions about using GNU Guix for scientific purposes: "
-      "reproducible research, high-performance computing (HPC), and more.")
+    (G_
+     '(p
+       "Discussions about using GNU Guix for scientific purposes: "
+       "reproducible research, high-performance computing (HPC), and more."))
     #:url "https://lists.gnu.org/mailman/listinfo/guix-science";
     #:log "https://lists.gnu.org/archive/html/guix-science";)
 
    (contact
-    #:name "Commits Mailing List"
+    #:name (G_ "Commits Mailing List")
     #:description
-    `(p
-      "Notifications of commits made to the "
-      (a (@ (href ,(guix-url "contribute/"))) "Git repositories")
-      ".")
+    (G_
+     `(p
+       "Notifications of commits made to the "
+       ,(G_ `(a (@ (href ,(guix-url "contribute/"))) "Git repositories"))
+       "."))
     #:url "https://lists.gnu.org/mailman/listinfo/guix-commits";
     #:log "https://lists.gnu.org/archive/html/guix-commits";)
 
    (contact
-    #:name "Security Mailing List"
+    #:name (G_ "Security Mailing List")
     #:description
-    `(p
-      "This is a private mailing list that anyone can post to to "
-      (a (@ (href ,(guix-url "security/"))) "report security issues")
-      " in Guix itself or in "
-      "the " (a (@ (href ,(guix-url "packages/"))) "packages")
-      " it provides.  Posting here allows Guix developers to address
-      the problem before it is widely publicized.")
+    (G_
+     `(p
+       "This is a private mailing list that anyone can post to to "
+       ,(G_ `(a (@ (href ,(guix-url "security/"))) "report security issues"))
+       " in Guix itself or in "
+       "the " ,(G_ `(a (@ (href ,(guix-url "packages/"))) "packages"))
+       " it provides.  Posting here allows Guix developers to address
+       the problem before it is widely publicized."))
     #:url "https://lists.gnu.org/mailman/listinfo/guix-security";
     #:log "")
 
    (contact
-    #:name "Sysadmin Mailing List"
+    #:name (G_ "Sysadmin Mailing List")
     #:description
-    '(p
-      "Private mailing list for the "
-      (a (@ (href "https://ci.guix.gnu.org/";)) "build farm")
-      " system administration.")
+    (G_
+     `(p
+       "Private mailing list for the "
+       ,(G_ `(a (@ (href "https://ci.guix.gnu.org/";)) "build farm"))
+       " system administration."))
     #:url "https://lists.gnu.org/mailman/listinfo/guix-sysadmin";
     #:log "")
 
@@ -176,22 +219,22 @@ het Nederlands.")
    ;; Non-Guix lists.
 
    (contact
-    #:name "GNU System Discuss Mailing List"
+    #:name (G_ "GNU System Discuss Mailing List")
     #:description
-    '(p "Discussion about the development of the broader GNU system.")
+    (G_ '(p "Discussion about the development of the broader GNU system."))
     #:url "https://lists.gnu.org/mailman/listinfo/gnu-system-discuss";
     #:log "https://lists.gnu.org/archive/html/gnu-system-discuss/";)
 
    (contact
-    #:name "GNU/Linux-libre Mailing List"
+    #:name (G_ "GNU/Linux-libre Mailing List")
     #:description
-    '(p "Workgroup for fully free GNU/Linux distributions.")
+    (G_ '(p "Workgroup for fully free GNU/Linux distributions."))
     #:url "https://lists.nongnu.org/mailman/listinfo/gnu-linux-libre";
     #:log "https://lists.nongnu.org/archive/html/gnu-linux-libre/";)
 
    (contact
-    #:name "GNU Info Mailing List"
+    #:name (G_ "GNU Info Mailing List")
     #:description
-    '(p "GNU software announcements.")
+    (G_ '(p "GNU software announcements."))
     #:url "https://lists.gnu.org/mailman/listinfo/info-gnu";
     #:log "https://lists.gnu.org/archive/html/info-gnu/";)))
diff --git a/website/apps/base/templates/about.scm 
b/website/apps/base/templates/about.scm
index e7531b4..427b908 100644
--- a/website/apps/base/templates/about.scm
+++ b/website/apps/base/templates/about.scm
@@ -3,106 +3,125 @@
 ;;; copyright interest on this file.
 
 (define-module (apps base templates about)
+  #:use-module (apps base templates components)
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (about-t))
 
 
 (define (about-t)
   "Return the About page in SHTML."
   (theme
-   #:title '("About")
+   #:title (C_ "webpage title" '("About"))
    #:description
-   "Guix is an advanced distribution of the GNU operating system.
+   (G_ "Guix is an advanced distribution of the GNU operating system.
     Guix is technology that respects the freedom of computer users.
     You are free to run the system for any purpose, study how it
-    works, improve it, and share it with the whole world."
+    works, improve it, and share it with the whole world.")
    #:keywords
-   (list "GNU" "Linux" "Unix" "Free software" "Libre software"
-        "Operating system" "GNU Hurd" "GNU Guix package manager")
-   #:active-menu-item "About"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager") #\|)
+   #:active-menu-item (C_ "website menu" "About")
    #:css (list
          (guix-url "static/base/css/page.css"))
-   #:crumbs (list (crumb "About" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "About") "./"))
    #:content
    `(main
      (section
       (@ (class "page centered-block limit-width"))
-      (h2 "About the Project")
+      ,(G_ `(h2 "About the Project"))
 
-      (p
-       "The " (em "GNU Guix") " package and system manager is a "
-       (a (@ (href ,(gnu-url "philosophy/free-sw.html")))
-         "free software")
-       " project developed by volunteers around the world under the
-       umbrella of the " (a (@ (href ,(gnu-url))) "GNU Project") ". ")
+      ,(G_
+        `(p
+          "The " ,(G_ `(em "GNU Guix")) " package and system manager is a "
+          ,(G_ `(a (@ (href ,(gnu-url "philosophy/free-sw.html")))
+                   "free software"))
+          " project developed by volunteers around the world under the
+            umbrella of the "
+          ,(G_ `(a (@ (href ,(gnu-url))) "GNU Project")) ". "))
 
-      (p
-       "Guix System is an advanced distribution of the "
-       (a (@ (href ,(gnu-url))) "GNU operating system")
-       ".  It uses the "
-       (a (@ (href ,(gnu-url "software/linux-libre"))) "Linux-libre")
-       " kernel, and support for "
-       (a (@ (href ,(gnu-url "software/hurd"))) "the Hurd")
-       " is being worked on.  As a GNU distribution, it is committed
-       to respecting and enhancing "
-       (a (@ (href ,(gnu-url "philosophy/free-sw.html")))
-         "the freedom of its users")
-       ".  As such, it adheres to the "
-       (a (@ (href ,(gnu-url 
"distros/free-system-distribution-guidelines.html")))
-         "GNU Free System Distribution Guidelines") ".")
+      ,(G_
+        `(p
+          "Guix System is an advanced distribution of the "
+          ,(G_ `(a (@ (href ,(gnu-url))) "GNU operating system"))
+          ".  It uses the "
+          ,(G_ `(a (@ (href ,(gnu-url "software/linux-libre"))) "Linux-libre"))
+          " kernel, and support for "
+          ,(G_ `(a (@ (href ,(gnu-url "software/hurd"))) "the Hurd"))
+          " is being worked on.  As a GNU distribution, it is committed
+            to respecting and enhancing "
+          ,(G_ `(a (@ (href ,(gnu-url "philosophy/free-sw.html")))
+                   "the freedom of its users"))
+          ".  As such, it adheres to the "
+          ,(G_ `(a (@ (href ,(gnu-url 
"distros/free-system-distribution-guidelines.html")))
+                   "GNU Free System Distribution Guidelines")) "."))
 
-      (p
-       "GNU Guix provides "
-       (a (@ (href ,(manual-url "Features.html")))
-         "state-of-the-art package management features")
-       " such as transactional upgrades and roll-backs, reproducible
-       build environments, unprivileged package management, and
-       per-user profiles.  It uses low-level mechanisms from the "
-       (a (@ (href "https://nixos.org/nix/";)) "Nix")
-       " package manager, but packages are "
-       (a (@ (href ,(manual-url "Defining-Packages.html"))) "defined")
-       " as native "
-       (a (@ (href ,(gnu-url "software/guile"))) "Guile")
-       " modules, using extensions to the "
-       (a (@ (href "http://schemers.org";)) "Scheme")
-       " language—which makes it nicely hackable.")
+      ;; TRANSLATORS: Features and Defining Packages are section names
+      ;; in the English (en) manual.
+      ,(G_
+        `(p
+          "GNU Guix provides "
+          ,(G_ (manual-href "state-of-the-art package management features"
+                            (G_ "en")
+                            (G_ "Features.html")))
+          " such as transactional upgrades and roll-backs, reproducible
+            build environments, unprivileged package management, and
+            per-user profiles.  It uses low-level mechanisms from the "
+          ,(G_ `(a (@ (href "https://nixos.org/nix/";)) "Nix"))
+          " package manager, but packages are "
+          ,(G_ (manual-href "defined" (G_ "en") (G_ "Defining-Packages.html")))
+          " as native "
+          ,(G_ `(a (@ (href ,(gnu-url "software/guile"))) "Guile"))
+          " modules, using extensions to the "
+          ,(G_ `(a (@ (href "http://schemers.org";)) "Scheme"))
+          " language—which makes it nicely hackable."))
 
-      (p
-       "Guix takes that a step further by additionally supporting stateless,
-       reproducible "
-       (a (@ (href ,(manual-url "Using-the-Configuration-System.html")))
-         "operating system configurations")
-       ". This time the whole system is hackable in Scheme, from the "
-       (a (@ (href ,(manual-url "Initial-RAM-Disk.html")))
-         "initial RAM disk")
-       " to the "
-       (a (@ (href ,(gnu-url "software/shepherd")))
-         "initialization system")
-       ", and to the "
-       (a (@ (href ,(manual-url "Defining-Services.html")))
-         "system services")
-       ".")
+      ;; TRANSLATORS: Using the Configuration System, Initial RAM Disk
+      ;; and Defining Services are section names in the English (en)
+      ;; manual.
+      ,(G_
+        `(p
+          "Guix takes that a step further by additionally supporting stateless,
+           reproducible "
+          ,(G_ (manual-href "operating system configurations"
+                            (G_ "en")
+                            (G_ "Using-the-Configuration-System.html")))
+          ". This time the whole system is hackable in Scheme, from the "
+          ,(G_ (manual-href "initial RAM disk"
+                            (G_ "en")
+                            (G_ "Initial-RAM-Disk.html")))
+          " to the "
+          ,(G_ `(a (@ (href ,(gnu-url "software/shepherd")))
+                   "initialization system"))
+          ", and to the "
+          ,(G_ (manual-href "system services"
+                            (G_ "en")
+                            (G_ "Defining-Services.html")))
+          "."))
 
 
-      (h3 (@ (id "mantainer")) "Maintainers")
+      ,(G_ `(h3 (@ (id "mantainer")) "Maintainers"))
 
-      (p
-       "Guix is currently maintained by Ludovic Courtès, Marius Bakke, Maxim
+      ,(G_
+        `(p
+          "Guix is currently maintained by Ludovic Courtès, Marius Bakke, Maxim
 Cournoyer, Tobias Geerinckx-Rice and Mathieu Othacehe.  Please use the "
-       (a (@ (href ,(guix-url "contact/"))) "mailing lists")
-       " for contact.  For sensitive issues, you can reach them "
-       "using the " (code "guix-maintainers@gnu.org") " private alias.")
+          ,(G_ `(a (@ (href ,(guix-url "contact/"))) "mailing lists"))
+          " for contact.  For sensitive issues, you can reach them "
+          "using the " (code "guix-maintainers@gnu.org") " private alias."))
 
 
-      (h3 (@ (id "license")) "Licensing")
+      ,(G_ `(h3 (@ (id "license")) "Licensing"))
 
-      (p
-       "Guix is free software; you can redistribute it and/or modify
-       it under the terms of the "
-       (a (@ (rel "license") (href ,(gnu-url "licenses/gpl.html")))
-         "GNU General Public License")
-       " as published by the Free Software Foundation; either
-       version\xa03 of the License, or (at your option) any later
-       version. ")))))
+      ,(G_
+        `(p
+          "Guix is free software; you can redistribute it and/or modify
+          it under the terms of the "
+          ,(G_ `(a (@ (rel "license") (href ,(gnu-url "licenses/gpl.html")))
+                   "GNU General Public License"))
+          " as published by the Free Software Foundation; either
+          version\xa03 of the License, or (at your option) any later
+          version. "))))))
diff --git a/website/apps/base/templates/components.scm 
b/website/apps/base/templates/components.scm
index 3252dc7..f33de1a 100644
--- a/website/apps/base/templates/components.scm
+++ b/website/apps/base/templates/components.scm
@@ -1,4 +1,5 @@
 ;;; GNU Guix web site
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
 ;;; Initially written by sirgazil who waives all
 ;;; copyright interest on this file.
 
@@ -12,6 +13,7 @@
   #:use-module (apps aux web)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (srfi srfi-1)
   #:use-module (ice-9 match)
   #:export (breadcrumbs
@@ -23,6 +25,8 @@
            link-more
            link-subtle
            link-yellow
+            manual-href
+            manual-link-yellow
            navbar
            page-indicator
             page-selector))
@@ -40,9 +44,9 @@
      (apps base types)."
   `(nav
     (@ (class "breadcrumbs"))
-    (h2 (@ (class "a11y-offset")) "Your location:")
+    ,(G_ `(h2 (@ (class "a11y-offset")) "Your location:"))
 
-    (a (@ (class "crumb") (href ,(guix-url))) "Home") (span " → ")
+    ,(G_ `(a (@ (class "crumb") (href ,(guix-url))) "Home")) (span " → ")
     ,@(separate (crumbs->shtml crumbs) '(span " → "))))
 
 
@@ -120,8 +124,10 @@
        (sxml->string*
         (match (contact-description contact)
           ((and multilingual (((? string?) (? string?)) ...))
-           (match (assoc "en" multilingual)
-             (("en" blurb) blurb)))
+           (let ((code %current-lang))
+             (match (assoc code multilingual)
+               ((code blurb) blurb)
+               (else (assoc "en" multilingual)))))
           (blurb
            blurb)))
        30)
@@ -144,7 +150,7 @@
     ,(if (string=? (contact-log contact) "")
         ""
         `(small
-          " (" (a (@ (href ,(contact-log contact))) "archive") ") "))
+           " (" ,(G_ `(a (@ (href ,(contact-log contact))) "archive")) ") "))
 
     ;; The description can be a list of language/blurb pairs.
     ,(match (contact-description contact)
@@ -215,6 +221,51 @@
   `(a (@ (class "link-yellow") (href ,url)) ,label))
 
 
+
+
+(define (manual-href label manual-lang _1 subpath _2)
+  "Return an HTML a element with its href attribute pointing to the
+manual.  It can be marked for translation as:
+
+  (G_ (manual-href \"some-text\" (G_ \"en\") (G_ \"Some-section.html\")))
+
+   LABEL (string)
+     The content of the a element.
+
+   MANUAL-LANG (string)
+     The normalized language for the Guix manual as produced by
+'doc/build.scm' in the Guix source tree, i.e. \"en\" for the English
+manual.
+
+   SUBPATH (string)
+     The same as in the manual-url procedure."
+  ;; The _ arguments are placeholders for args added by G_, cf. i18n-howto.txt.
+  `(a (@ (href ,(manual-url subpath #:language manual-lang))) label))
+
+(define* (manual-link-yellow label manual-lang _1 #:optional (subpath "") _2)
+  "Return a link-yellow component pointing to the manual.  It can be
+used like this:
+
+  (manual-link-yellow \"some-text\" (G_ \"en\") \"Package-Management.html\")
+
+   LABEL (string)
+     The label of the link-yellow.
+
+   MANUAL-LANG (string)
+     The normalized language for the Guix manual as produced by
+'doc/build.scm' in the Guix source tree, i.e. \"en\" for the English
+manual.
+
+   SUBPATH (string)
+     The same as in the manual-url procedure."
+  ;; The _ arguments are placeholders for args added by G_, cf. i18n-howto.txt.
+  (link-yellow
+   #:label label
+   #:url (manual-url subpath #:language manual-lang)))
+
+
+
+
 (define* (menu-dropdown #:key (label "Item") (active-item "") (url "#") (items 
'()))
   "Return an SHTML li element representing a dropdown for the navbar.
 
@@ -283,43 +334,45 @@
     (h1
      (a
       (@ (class "branding") (href ,(guix-url)))
-      (span (@ (class "a11y-offset")) "Guix")))
+      ,(C_ "website menu" `(span (@ (class "a11y-offset")) "Guix"))))
 
     ;; Menu.
     (nav (@ (class "menu"))
-     (h2 (@ (class "a11y-offset")) "Website menu:")
+     ,(G_ `(h2 (@ (class "a11y-offset")) "website menu:"))
      (ul
-      ,(menu-item #:label "Overview" #:active-item active-item #:url 
(guix-url))
+      ,(C_ "website menu" (menu-item #:label "Overview" #:active-item 
active-item #:url (guix-url)))
 
-      ,(menu-dropdown #:label "Download"
+      ,(menu-dropdown #:label (C_ "website menu" "Download")
                       #:active-item active-item
                       #:items
                       (list
-                       (menu-item #:label "Stable"
-                                  #:active-item active-item
-                                  #:url (guix-url "download/"))
-                       (menu-item #:label "Latest"
-                                  #:active-item active-item
-                                  #:url (guix-url "download/latest/"))))
-      ,(menu-item #:label "Packages" #:active-item active-item #:url (guix-url 
"packages/"))
-      ,(menu-item #:label "Blog" #:active-item active-item #:url (guix-url 
"blog/"))
-
-      ,(menu-dropdown #:label "Media" #:active-item active-item
+                       (C_ "website menu"
+                           (menu-item #:label "Stable"
+                                      #:active-item active-item
+                                      #:url (guix-url "download/")))
+                       (C_ "website menu"
+                           (menu-item #:label "Latest"
+                                      #:active-item active-item
+                                      #:url (guix-url "download/latest/")))))
+      ,(C_ "website menu" (menu-item #:label "Packages" #:active-item 
active-item #:url (guix-url "packages/")))
+      ,(C_ "website menu" (menu-item #:label "Blog" #:active-item active-item 
#:url (guix-url "blog/")))
+
+      ,(menu-dropdown #:label (C_ "website menu" "Media") #:active-item 
active-item
         #:items
         (list
-         (menu-item #:label "Videos" #:active-item active-item #:url (guix-url 
"videos/"))
-         (menu-item #:label "Screenshots" #:active-item active-item #:url 
(guix-url "screenshots/"))))
+         (C_ "website menu" (menu-item #:label "Videos" #:active-item 
active-item #:url (guix-url "videos/")))
+         (C_ "website menu" (menu-item #:label "Screenshots" #:active-item 
active-item #:url (guix-url "screenshots/")))))
 
-      ,(menu-item #:label "Help" #:active-item active-item #:url (guix-url 
"help/"))
-      ,(menu-item #:label "Donate" #:active-item active-item #:url (guix-url 
"donate/"))
+      ,(C_ "website menu" (menu-item #:label "Help" #:active-item active-item 
#:url (guix-url "help/")))
+      ,(C_ "website menu" (menu-item #:label "Donate" #:active-item 
active-item #:url (guix-url "donate/")))
 
-      ,(menu-dropdown #:label "About" #:active-item active-item #:url 
(guix-url "about/")
+      ,(menu-dropdown #:label (C_ "website menu" "About") #:active-item 
active-item #:url (guix-url "about/")
        #:items
        (list
-        (menu-item #:label "Contact" #:active-item active-item #:url (guix-url 
"contact/"))
-        (menu-item #:label "Contribute" #:active-item active-item #:url 
(guix-url "contribute/"))
-        (menu-item #:label "Security" #:active-item active-item #:url 
(guix-url "security/"))
-        (menu-item #:label "Graphics" #:active-item active-item #:url 
(guix-url "graphics/"))))))
+         (C_ "website menu" (menu-item #:label "Contact" #:active-item 
active-item #:url (guix-url "contact/")))
+         (C_ "website menu" (menu-item #:label "Contribute" #:active-item 
active-item #:url (guix-url "contribute/")))
+         (C_ "website menu" (menu-item #:label "Security" #:active-item 
active-item #:url (guix-url "security/")))
+         (C_ "website menu" (menu-item #:label "Graphics" #:active-item 
active-item #:url (guix-url "graphics/")))))))
 
     ;; Menu button.
     (a
@@ -337,10 +390,10 @@
    TOTAL-PAGES (number)
      The total number of pages that should be displayed."
   (if (> total-pages 1)
-      `(span
-       (@ (class "page-number-indicator"))
-       " (Page " ,(number->string page-number)
-       " of " ,(number->string total-pages) ")")
+      (G_ `(span
+            (@ (class "page-number-indicator"))
+            " (Page " ,(number->string page-number)
+            " of " ,(number->string total-pages) ")"))
       ""))
 
 
@@ -361,8 +414,8 @@
     (@ (class "page-selector"))
     (h3
      (@ (class "a11y-offset"))
-     ,(string-append "Page " (number->string active-page) " of "
-                    (number->string pages) ". Go to another page: "))
+     ,(G_ (string-append "Page " (number->string active-page) " of "
+                         (number->string pages) ". Go to another page: ")))
     ,(if (> pages 1)
         (map
          (lambda (page-number)
diff --git a/website/apps/base/templates/contact.scm 
b/website/apps/base/templates/contact.scm
index 2085e22..7ecd354 100644
--- a/website/apps/base/templates/contact.scm
+++ b/website/apps/base/templates/contact.scm
@@ -7,31 +7,33 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (contact-t))
 
 
 (define (contact-t context)
   "Return the Contact page in SHTML with the data in CONTEXT."
   (theme
-   #:title '("Contact")
+   #:title (C_ "webpage title" '("Contact"))
    #:description
-   "A list of channels to communicate with GNU Guix users
-   and developers about anything you want."
+   (G_ "A list of channels to communicate with GNU Guix users
+   and developers about anything you want.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Community" "Mailing lists" "IRC channels" "Bug reports" "Help")
-   #:active-menu-item "About"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Community|Mailing lists|IRC \
+channels|Bug reports|Help") #\|)
+   #:active-menu-item (C_ "website menu" "About")
    #:css (list
          (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/buttons.css")
          (guix-url "static/base/css/contact.css"))
-   #:crumbs (list (crumb "Contact" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Contact") "./"))
    #:content
    `(main
      (section
       (@ (class "page centered-block limit-width"))
-      (h2 "Contact")
+      ,(G_ `(h2 "Contact"))
 
       ,@(map
         contact->shtml
diff --git a/website/apps/base/templates/contribute.scm 
b/website/apps/base/templates/contribute.scm
index d0fea8c..7734268 100644
--- a/website/apps/base/templates/contribute.scm
+++ b/website/apps/base/templates/contribute.scm
@@ -7,252 +7,294 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (contribute-t))
 
 
 (define (contribute-t)
   "Return the Contribute page in SHTML."
   (theme
-   #:title '("Contribute")
+   #:title (C_ "webpage title" '("Contribute"))
    #:description
-   "Check all the ways you can contribute to make GNU Guix
-   better, and join the world-wide community of volunteers."
+   (G_ "Check all the ways you can contribute to make GNU Guix
+   better, and join the world-wide community of volunteers.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Volunteer" "Development" "Translation" "I18N" "L10N"
-     "Artwork")
-   #:active-menu-item "About"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Volunteer|Development|\
+Translation|I18N|L10N|Artwork") #\|)
+   #:active-menu-item (C_ "website menu" "About")
    #:css (list
          (guix-url "static/base/css/page.css")
          (guix-url "static/base/css/item-preview.css"))
-   #:crumbs (list (crumb "Contribute" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Contribute") "./"))
    #:content
    `(main
      (section
       (@ (class "page centered-block limit-width"))
-      (h2 "Contribute")
+      ,(G_ `(h2 "Contribute"))
 
-      (p
-       "GNU Guix is a large project developed
-       mostly by volunteers from all around the world. You are welcome
-       to join us in the "
-       (a (@ (href "https://lists.gnu.org/mailman/listinfo/guix-devel";))
-         "development mailing list")
-       " or in the "
-       (a (@ (href ,(guix-url "contact/irc/"))) "#guix channel")
-       " in IRC Freenode. Tell us how would you like to help, and we
-       will do our best to guide you. ")
+      ,(G_
+        `(p
+          "GNU Guix is a large project developed
+           mostly by volunteers from all around the world. You are welcome
+           to join us in the "
+          ,(G_
+            `(a (@ (href "https://lists.gnu.org/mailman/listinfo/guix-devel";))
+                "development mailing list"))
+          " or in the "
+          ,(G_
+            `(a (@ (href ,(guix-url "contact/irc/"))) "#guix channel"))
+          " in IRC Freenode. Tell us how would you like to help, and we
+          will do our best to guide you. "))
 
-      (p
-       "We want to provide a warm, friendly, and harassment-free environment,
-       so that anyone can contribute to the best of their abilities.  To this
-       end our project uses a “Contributor Covenant”, which was adapted from "
-       (a (@ (href "https://contributor-covenant.org/";))
-          "https://contributor-covenant.org/";)
-       ".  You can find the full pledge in the "
-       (a (@ (href "//git.savannah.gnu.org/cgit/guix.git/tree/CODE-OF-CONDUCT")
-            (class "mono"))
-          "CODE-OF-CONDUCT") " file.")
+      ,(G_
+        `(p
+          "We want to provide a warm, friendly, and harassment-free 
environment,
+           so that anyone can contribute to the best of their abilities.  To
+           this end our project uses a “Contributor Covenant”, which was 
adapted
+           from "
+          ,(G_ ((lambda (url)
+                  `(a (@ (href ,url)) url))
+                "https://contributor-covenant.org/";))
+          ".  You can find the full pledge in the "
+          ,(G_
+            `(a (@ (href 
"//git.savannah.gnu.org/cgit/guix.git/tree/CODE-OF-CONDUCT")
+                   (class "mono"))
+                "CODE-OF-CONDUCT"))
+          " file."))
 
       (div
        (@ (class "centered-text"))
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "pms")) "Project Management")
-       (p
-        "We use "
-        (a (@ (href "https://savannah.gnu.org/";)) "Savannah")
-        " as the central point for development, maintenance and
-         distribution of the Guix System Distribution and GNU Guix.")
-       (p
-        "The source files for all the components of the project,
-         including software, web site, documentation, and artwork, are
-         available in "
-        (a (@ (href "https://savannah.gnu.org/git/?group=guix";))
-           "Git repositories")
-        " at Savannah. ")
+        ,(G_ `(h3 (@ (id "pms")) "Project Management"))
+        ,(G_
+          `(p
+            "We use "
+            ,(G_ `(a (@ (href "https://savannah.gnu.org/";)) "Savannah"))
+            " as the central point for development, maintenance and
+            distribution of the Guix System Distribution and GNU Guix."))
+        ,(G_
+          `(p
+            "The source files for all the components of the project,
+            including software, web site, documentation, and artwork, are
+            available in "
+            ,(G_ `(a (@ (href "https://savannah.gnu.org/git/?group=guix";))
+                     "Git repositories"))
+            " at Savannah. "))
        (p
         ,(link-more
-          #:label "Access Savannah"
+           #:label (G_ "Access Savannah")
           #:url "https://savannah.gnu.org/projects/guix";)))
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "art")) "Art")
-       (p
-        "We are always looking for artists to help us design and
-         improve user interfaces, and create multimedia material for
-         documentation, presentations, and promotional items. ")
-       (p
-        "The artwork used in the different components of the project
-         is available in the "
-        (a (@ (href "//git.savannah.gnu.org/cgit/guix/guix-artwork.git"))
-           "guix-artwork")
-        " repository. ")
+        ,(G_ `(h3 (@ (id "art")) "Art"))
+        ,(G_
+          `(p
+            "We are always looking for artists to help us design and
+            improve user interfaces, and create multimedia material for
+            documentation, presentations, and promotional items. "))
+        ,(G_
+          `(p
+            "The artwork used in the different components of the project
+            is available in the "
+            ,(G_
+              `(a (@ (href 
"//git.savannah.gnu.org/cgit/guix/guix-artwork.git"))
+                  "guix-artwork"))
+            " repository. "))
        (p
         ,(link-more
-          #:label "Contribute"
+           #:label (G_ "Contribute")
           #:url "https://lists.gnu.org/mailman/listinfo/guix-devel";)))
 
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "documentation")) "Documentation")
-       (p
-        "You can read the "
-        (a (@ (href ,(guix-url "help/"))) "project documentation")
-        " already available in the system and in the website, and
-         help us identify any errors or omissions. Creating new
-         manuals, tutorials, and blog entries will also help users and
-         developers discover what we do. ")
-       (p
-        "Helping improve the documentation of the "
-        (a (@ (href ,(guix-url "packages/"))) "packaged software")
-        " is another way to contribute. ")
+        ,(G_ `(h3 (@ (id "documentation")) "Documentation"))
+        ,(G_
+          `(p
+            "You can read the "
+            ,(G_ `(a (@ (href ,(guix-url "help/"))) "project documentation"))
+            " already available in the system and in the website, and
+            help us identify any errors or omissions. Creating new
+            manuals, tutorials, and blog entries will also help users and
+            developers discover what we do. "))
+        ,(G_
+          `(p
+            "Helping improve the documentation of the "
+            ,(G_ `(a (@ (href ,(guix-url "packages/"))) "packaged software"))
+            " is another way to contribute. "))
        (p
         ,(link-more
-          #:label "Start writing"
+           #:label (G_ "Start writing")
           #:url "https://lists.gnu.org/mailman/listinfo/guix-devel";)))
 
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "packages")) "Packages")
-       (p
-        "Hundreds of software, documentation, and assets need to be
-         packaged to make it easier for users to install their
-         favorite tools with the Guix package manager, and be
-         productive using the system. ")
-       (p
-        "Information on how to add packages to the distribution can
-         be found "
-        (a (@ (href ,(manual-url "Packaging-Guidelines.html")))
-           "in the manual")
-        ". ")
-       (p
-        "Check out the "
-        (a (@ (href ,(guix-url "packages/")))
-           "package database")
-        " for a list of available packages, and the "
-        (a (@ (href 
"//issues.guix.gnu.org/search?query=tag%3Apatch+is%3Aopen"))
-           "patch-tracking database")
-        " for a list of pending submissions.")
+        ,(G_ `(h3 (@ (id "packages")) "Packages"))
+        ,(G_
+          `(p
+            "Hundreds of software, documentation, and assets need to be
+            packaged to make it easier for users to install their
+            favorite tools with the Guix package manager, and be
+            productive using the system. "))
+        ;; TRANSLATORS: Packaging Guidelines is a section name in the
+        ;; English (en) manual.
+        ,(G_
+          `(p
+            "Information on how to add packages to the distribution can
+            be found "
+            ,(G_
+              (manual-href
+               "in the manual"
+               (G_ "en")
+               (G_ "Packaging-Guidelines.html")))
+            ". "))
+        ,(G_
+          `(p
+            "Check out the "
+            ,(G_ `(a (@ (href ,(guix-url "packages/")))
+                     "package database"))
+            " for a list of available packages, and the "
+            ,(G_ `(a (@ (href 
"//issues.guix.gnu.org/search?query=tag%3Apatch+is%3Aopen"))
+                     "patch-tracking database"))
+            " for a list of pending submissions."))
        (p
         ,(link-more
-          #:label "Send a new package"
+           #:label (G_ "Send a new package")
           #:url "https://issues.guix.gnu.org";)))
 
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "programming")) "Programming")
-       (p
-        "Source code is in the "
-        (a (@ (href "//git.savannah.gnu.org/cgit/guix.git/"))
-           "main Git repository")
-        ".  "
-        "We use "
-        (a (@ (href ,(gnu-url "software/guile"))) "GNU Guile")
-        " as the main programming and extension language for the
-         components of the system. ")
-       (p
-        "You will find it useful to browse the "
-        (a (@ (href ,(gnu-url "software/guile/manual")))
-           "Guile manual")
-        " or other "
-        (a (@ (href "http://www.schemers.org/Documents/#intro-texts";))
-           "introductory material about Scheme")
-        ". Also, make sure to read the "
-        (a (@ (href ,(manual-url "Contributing.html")))
-           "Contributing")
-        " section of the manual for more details on the development
-         setup, as well as the coding and cooperation conventions used
-         in the project. ")
+        ,(G_ `(h3 (@ (id "programming")) "Programming"))
+        ,(G_
+          `(p
+            "Source code is in the "
+            ,(G_ `(a (@ (href "//git.savannah.gnu.org/cgit/guix.git/"))
+                     "main Git repository"))
+            ".  "
+            "We use "
+            ,(G_ `(a (@ (href ,(gnu-url "software/guile"))) "GNU Guile"))
+            " as the main programming and extension language for the
+            components of the system. "))
+        ;; TRANSLATORS: Contributing is a section name in the English
+        ;; (en) manual.
+        ,(G_
+          `(p
+            "You will find it useful to browse the "
+            ,(G_
+              `(a (@ (href ,(gnu-url "software/guile/manual")))
+                  "Guile manual"))
+            " or other "
+            ,(G_ `(a (@ (href 
"http://www.schemers.org/Documents/#intro-texts";))
+                     "introductory material about Scheme"))
+            ". Also, make sure to read the "
+            ,(G_ (manual-href "Contributing"
+                              (G_ "en")
+                              (G_ "Contributing.html")))
+            " section of the manual for more details on the development
+            setup, as well as the coding and cooperation conventions used
+            in the project. "))
        (p
         ,(link-more
-          #:label "Send a patch"
+           #:label (G_ "Send a patch")
           #:url "https://lists.gnu.org/mailman/listinfo/guix-patches";)))
 
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "sysadmin")) "System Administration")
-       (p
-        "Our system infrastructure makes it possible for all the
-         contributors to communicate and collaborate in the project,
-         and users to be able to download and install packages. Help
-         us keep the system up and running smoothly. ")
-       (p
-        "You can also "
-        (a (@ (href ,(guix-url "donate/")))
-           "donate hardware or hosting")
-        " for our "
-        (a (@ (href "https://ci.guix.gnu.org";)) "build farm") ".  ")
+        ,(G_ `(h3 (@ (id "sysadmin")) "System Administration"))
+        ,(G_
+          `(p
+            "Our system infrastructure makes it possible for all the
+            contributors to communicate and collaborate in the project,
+            and users to be able to download and install packages. Help
+            us keep the system up and running smoothly. "))
+        ,(G_
+          `(p
+            "You can also "
+            ,(G_ `(a (@ (href ,(guix-url "donate/")))
+                     "donate hardware or hosting"))
+            " for our "
+            ,(G_ `(a (@ (href "https://ci.guix.gnu.org";)) "build farm")) ".  
"))
        (p
         ,(link-more
-          #:label "Contribute"
+           #:label (G_ "Contribute")
           #:url "https://lists.gnu.org/mailman/listinfo/guix-devel";)))
 
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "testing")) "Test and Bug Reports")
-       (p
-        "Install the software and send feedback to the community
-         about your experience. Help the project by reporting bugs. "
-         "You can also get started by "
-         (a (@ (href "https://issues.guix.gnu.org/easy";))
-            "picking an “easy” bug")
-         " to work on.")
-       (p
-        "Before reporting a bug, please check whether the bug is
-         already "
-        (a (@ (href "https://issues.guix.gnu.org";))
-           "in the bug database")
-        ". See "
-        (a (@ (href "https://debbugs.gnu.org/Developer.html";))
-           "the developer information page")
-        " for more information on how to manipulate bug reports. ")
+        ,(G_ `(h3 (@ (id "testing")) "Test and Bug Reports"))
+        ,(G_
+          `(p
+            "Install the software and send feedback to the community
+            about your experience. Help the project by reporting bugs. "
+            "You can also get started by "
+            ,(G_ `(a (@ (href "https://issues.guix.gnu.org/easy";))
+                     "picking an “easy” bug"))
+            " to work on."))
+
+        ,(G_
+          `(p
+            "Before reporting a bug, please check whether the bug is
+            already "
+            ,(G_ `(a (@ (href "https://issues.guix.gnu.org";))
+                     "in the bug database"))
+            ". See "
+            ,(G_ `(a (@ (href "https://debbugs.gnu.org/Developer.html";))
+                     "the developer information page"))
+            " for more information on how to manipulate bug reports. "))
        (p
         ,(link-more
-          #:label "Report a bug"
+           #:label (G_ "Report a bug")
           #:url "https://issues.guix.gnu.org";)))
 
 
        (div
        (@ (class "summary-box"))
-       (h3 (@ (id "translation")) "Translation")
-       (p
-        "You can help translate the "
-        (a (@ (href "https://translationproject.org/domain/guix.html";))
-           "software")
-        ", the "
-        (a (@ (href 
"https://translationproject.org/domain/guix-packages.html";))
-           "package descriptions")
-         ", and the "
-            (a (@ (href 
"https://translationproject.org/domain/guix-manual.html";))
-              "manual")
-        " into your language.  See the "
-        (a (@ (href "https://translationproject.org/html/translators.html";))
-           "Translation Project")
-        " for information on how you can help.")
-       (p
-        (a (@ (href ,(guix-url "packages"))) "Software packages")
-        " provided by the system may have their own translation
-         tools.  Visit their websites and help translate. ")
+        ,(G_ `(h3 (@ (id "translation")) "Translation"))
+        ,(G_
+          `(p
+            "You can help translate the "
+            ,(G_
+              `(a (@ (href "https://translationproject.org/domain/guix.html";))
+                  "software"))
+            ", the "
+            ,(G_
+              `(a (@ (href 
"https://translationproject.org/domain/guix-packages.html";))
+                  "package descriptions"))
+            ", and the "
+            ,(G_
+              `(a (@ (href 
"https://translationproject.org/domain/guix-manual.html";))
+                  "manual"))
+            " into your language.  See the "
+            ,(G_
+              `(a (@ (href 
"https://translationproject.org/html/translators.html";))
+                  "Translation Project"))
+            " for information on how you can help."))
+        ,(G_
+          `(p
+            ,(G_ `(a (@ (href ,(guix-url "packages"))) "Software packages"))
+            " provided by the system may have their own translation
+            tools.  Visit their websites and help translate. "))
        (p
         ,(link-more
-          #:label "Start translating"
+           #:label (G_ "Start translating")
           #:url "https://translationproject.org/";))))
 
 
-      (h3 (@ (id "resources")) "Other resources for contributors")
-      (p
-       "Documents, supporting material of previous talks, and
-       auxiliary information useful to hackers and maintainers is
-       available at "
-       (a (@ (href "//git.savannah.gnu.org/cgit/guix/maintenance.git"))
-         "git://git.sv.gnu.org/guix/maintenance.git")
-       ".")))))
+      ,(G_ `(h3 (@ (id "resources")) "Other resources for contributors"))
+      ,(G_
+        `(p
+          "Documents, supporting material of previous talks, and
+          auxiliary information useful to hackers and maintainers is
+          available at "
+          (a (@ (href "//git.savannah.gnu.org/cgit/guix/maintenance.git"))
+             "git://git.sv.gnu.org/guix/maintenance.git")
+          "."))))))
diff --git a/website/apps/base/templates/donate.scm 
b/website/apps/base/templates/donate.scm
index 7483454..6077891 100644
--- a/website/apps/base/templates/donate.scm
+++ b/website/apps/base/templates/donate.scm
@@ -7,173 +7,212 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (donate-t))
 
 
 (define (donate-t)
   "Return the Donate page in SHTML."
   (theme
-   #:title '("Donate")
+   #:title (C_ "webpage title" '("Donate"))
    #:description
-   "We are looking for donations of hardware and optionally hosting
-   for machines (they should be usable with exclusively free
-   software)."
+   (G_ "We are looking for donations of hardware and optionally
+   hosting for machines (they should be usable with exclusively
+   free software).")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Donations")
-   #:active-menu-item "Donate"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Donations") #\|)
+   #:active-menu-item (C_ "website menu" "Donate")
    #:css (list
          (guix-url "static/base/css/page.css"))
-   #:crumbs (list (crumb "Donate" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Donate") "./"))
    #:content
    `(main
      (section
       (@ (class "page centered-block limit-width"))
-      (h2 "Donate")
-
-      (p
-       "The "
-       (a (@ (href "https://ci.guix.gnu.org";))
-         "build farm")
-       " of Guix runs on donated hardware and"
-       " hosting. As the distribution grows (see the "
-       (a (@ (href ,(guix-url "packages/"))) "package list")
-       "), so do the computing and storage needs.")
-
-      (p
-       "Back in 2015 we "
-       (a (@ (href ,(guix-url 
"blog/2015/guix-starts-fundraising-campaign-with-support-from-the-fsf/")))
-         "ran a fundraising campaign")
-       " to strengthen our build farm, with "
-       (a (@ (href 
"https://www.fsf.org/blogs/community/fsf-announces-support-for-gnu-guix";))
-         "support from the Free Software Foundation (FSF)")
-       ".  The Guix project can always use financial support to further its "
-       "mission.  Please consider helping out by making a donation on this
-       FSF-hosted page:")
+      ,(G_ `(h2 "Donate"))
+
+      ,(G_
+        `(p
+          "The "
+          ,(G_
+            `(a (@ (href "https://ci.guix.gnu.org";))
+                "build farm"))
+          " of Guix runs on donated hardware and"
+          " hosting. As the distribution grows (see the "
+          ,(G_ `(a (@ (href ,(guix-url "packages/"))) "package list"))
+          "), so do the computing and storage needs."))
+
+      ,(G_
+        `(p
+          "Back in 2015 we "
+          ,(G_ `(a (@ (href ,(guix-url 
"blog/2015/guix-starts-fundraising-campaign-with-support-from-the-fsf/")))
+                   "ran a fundraising campaign"))
+          " to strengthen our build farm, with "
+          ,(G_
+            `(a (@ (href 
"https://www.fsf.org/blogs/community/fsf-announces-support-for-gnu-guix";))
+                "support from the Free Software Foundation (FSF)"))
+          ".  The Guix project can always use financial support to further its 
"
+          "mission.  Please consider helping out by making a donation on this
+          FSF-hosted page:"))
 
       (p
        (@ (class "centered-text"))
        ,(button-big
-        #:label "♥ DONATE!"
+         #:label (C_ "button" "♥ DONATE!")
         #:url "https://my.fsf.org/civicrm/contribute/transact?reset=1&id=50";))
 
-      (h3
-       (@ (id "hardware-and-hosting"))
-       "Hardware and Hosting")
+      ,(G_
+        `(h3
+          (@ (id "hardware-and-hosting"))
+          "Hardware and Hosting"))
 
-      (p
-       "We are also looking for donations of hardware and optionally
-        hosting for the following kinds of machines (they should be
-        usable with exclusively free software): ")
+      ,(G_
+        `(p
+          "We are also looking for donations of hardware and optionally
+           hosting for the following kinds of machines (they should be
+           usable with exclusively free software): "))
 
       (ul
-       (li "x86_64 machines, with on the order of 1\xa0TiB of storage
-            and 4\xa0GiB of RAM;")
-       (li "armv7 machines (such as the Novena) to more quickly test
-            and provide binaries for the armhf-linux port;")
-       (li "mips64el machines to strengthen this port."))
-
-      (p
-       "Please get in touch with us through the "
-       (a (@ (href ,(guix-url "contact/"))) "usual channels")
-       " or using the " (b "guix-hardware@gnu.org") " private alias to
-        discuss any opportunities. ")
-
-
-      (h3
-       (@ (id "hardware-donors"))
-       "Thanks to the donors!")
-
-      (p
-       "The table below summarizes hardware and hosting donations that
-        make the " (a (@ (href "https://ci.guix.gnu.org";)) "build farm")
-       " for the Guix System Distribution a reality.")
+       ,(G_
+         `(li "x86_64 machines, with on the order of 1\xa0TiB of storage
+               and 4\xa0GiB of RAM;"))
+       ,(G_
+         `(li "armv7 machines (such as the Novena) to more quickly test
+               and provide binaries for the armhf-linux port;"))
+       ,(G_
+         `(li "mips64el machines to strengthen this port.")))
+
+      ,(G_
+        `(p
+          "Please get in touch with us through the "
+          ,(G_ `(a (@ (href ,(guix-url "contact/"))) "usual channels"))
+          " or using the " (b "guix-hardware@gnu.org") " private alias to
+           discuss any opportunities. "))
+
+
+      ,(G_
+        `(h3
+          (@ (id "hardware-donors"))
+          "Thanks to the donors!"))
+
+      ,(G_
+        `(p
+          "The table below summarizes hardware and hosting donations that
+           make the " ,(G_ `(a (@ (href "https://ci.guix.gnu.org";)) "build 
farm"))
+           " for the Guix System Distribution a reality."))
 
       (div
        (@ (class "table-box"))
        (table
        (thead
-        (tr (th "machine")
-            (th "system")
-            (th "donors")))
+         ,(G_ `(tr ,(G_ `(th "machine"))
+                   ,(G_ `(th "system"))
+                   ,(G_ `(th "donors")))))
        (tbody
 
-     (tr
-      (td "berlin.guixsd.org")
-      (td "build farm with 25 build nodes for x86_64-linux and
-i686-linux, and dedicated storage")
-      (td
-       (ul
-        (li
-         (a (@ (href "https://www.mdc-berlin.de/";))
-            "Max Delbrück Center for Molecular Medicine")
-         " (hardware and hosting)"))))
-        (tr
-         (td "overdrive1.guixsd.org")
-         (td "aarch64-linux")
-         (td
-          (ul
-           (li (a (@ (href ,(guix-url 
"blog/2018/aarch64-build-machines-donated/")))
-                  "ARM Holdings") " (hardware)"))))
-        (tr
-         (td "bayfront.guixsd.org")
-         (td "new build farm front-end (WIP)")
-         (td
-          (ul
-           (li
-            (a (@ (href ,(guix-url "blog/2016/growing-our-build-farm/")))
-               "Igalia")))))
-        (tr
-         (td "guix-x15.sjd.se, guix-x15b.sjd.se")
-         (td "armhf-linux")
-         (td
-          (ul
-           (li (a (@ (href "https://blog.josefsson.org/";))
-                  "Simon Josefsson")))))
-        (tr
-         (td "hydra-slave1")
-         (td "armhf-linux")
-         (td
-          (ul
-           (li "Steve Sprang (hardware)")
-           ;; XXX: Eventually move to the FSF?
-           (li "Mark H Weaver (hosting)"))))
-        (tr
-         (td "hydra-slave2")
-         (td "armhf-linux")
-         (td
-          (ul
-           (li (a (@ (href "http://harmoninstruments.com/";))
-                  "Harmon Instruments")
-               " (hardware)")
-           ;; XXX: Eventually move to the FSF?
-           (li "Mark H Weaver (hosting)"))))
-        (tr
-         (td "hydra-slave3")
-         (td "armhf-linux")
-         (td
-          (ul
-           (li (a (@ (href 
"http://www.kosagi.com/w/index.php?title=Novena_Main_Page";))
-                  "Kosagi (Sutajio Ko-Usagi Pte Ltd)")
-               " (hardware)")
-           (li "Mark H Weaver (hosting)"))))
-        (tr
-         (td "redhill")
-         (td "armhf-linux")
-         (td
-          (ul
-           (li (a (@ (href 
"http://www.kosagi.com/w/index.php?title=Novena_Main_Page";))
-                  "Kosagi (Sutajio Ko-Usagi Pte Ltd)")
-               " (hardware)")
-           (li "Andreas Enge (hosting)")))))))
-
-      (p "Other organizations and individuals helped Guix with hardware and
+         ,(G_ `(tr
+                ,(G_ `(td "berlin.guixsd.org"))
+                ,(G_ `(td "build farm with 25 build nodes for x86_64-linux and
+i686-linux, and dedicated storage"))
+                ,(G_ ((lambda content
+                        `(td
+                          (ul
+                           (li
+                            ,@content))))
+                      (G_ `(a (@ (href "https://www.mdc-berlin.de/";))
+                              "Max Delbrück Center for Molecular Medicine"))
+                      " (hardware and hosting)"))))
+         ,(G_ `(tr
+                ,(G_ `(td "overdrive1.guixsd.org"))
+                ,(G_ `(td "aarch64-linux"))
+                ,(G_ ((lambda content
+                        `(td
+                          (ul
+                           (li
+                            ,@content))))
+                      (G_ `(a (@ (href ,(guix-url 
"blog/2018/aarch64-build-machines-donated/")))
+                              "ARM Holdings") " (hardware)")))))
+         ,(G_
+           `(tr
+             ,(G_ `(td "bayfront.guixsd.org"))
+             ,(G_ `(td "new build farm front-end (WIP)"))
+             ,(G_ ((lambda (content)
+                        `(td
+                          (ul
+                           (li
+                            (a
+                             (@ (href ,(guix-url 
"blog/2016/growing-our-build-farm/")))
+                             ,content)))))
+                   "Igalia"))))
+         ,(G_ `(tr
+                ,(G_ `(td "guix-x15.sjd.se, guix-x15b.sjd.se"))
+                ,(G_ `(td "armhf-linux"))
+                ,(G_ ((lambda (content)
+                        `(td
+                          (ul
+                           (li
+                            (a (@ (href "https://blog.josefsson.org/";))
+                               ,content)))))
+                      "Simon Josefsson"))))
+         ,(G_ `(tr
+                ,(G_ `(td "hydra-slave1"))
+                ,(G_ `(td "armhf-linux"))
+                ,(G_ ((lambda content
+                        `(td
+                          (ul
+                           ,@content)))
+                      (G_ `(li "Steve Sprang (hardware)"))
+                      ;; XXX: Eventually move to the FSF?
+                      (G_ `(li "Mark H Weaver (hosting)"))))))
+         ,(G_ `(tr
+                ,(G_ `(td "hydra-slave2"))
+                ,(G_ `(td "armhf-linux"))
+                ,(G_ ((lambda content
+                        `(td
+                          (ul
+                           ,@content)))
+                      (G_ `(li
+                            ,(G_ `(a (@ (href "http://harmoninstruments.com/";))
+                                     "Harmon Instruments"))
+                            " (hardware)"))
+                      ;; XXX: Eventually move to the FSF?
+                      (G_ `(li "Mark H Weaver (hosting)"))))))
+         ,(G_
+           `(tr
+             ,(G_ `(td "hydra-slave3"))
+             ,(G_ `(td "armhf-linux"))
+             ,(G_ ((lambda content
+                     `(td
+                       (ul
+                        ,@content)))
+                   (G_ `(li
+                    ,(G_ `(a (@ (href 
"http://www.kosagi.com/w/index.php?title=Novena_Main_Page";))
+                             "Kosagi (Sutajio Ko-Usagi Pte Ltd)"))
+                    " (hardware)"))
+                   (G_ `(li "Mark H Weaver (hosting)"))))))
+         ,(G_
+           `(tr
+             ,(G_ `(td "redhill"))
+             ,(G_ `(td "armhf-linux"))
+             ,(G_ ((lambda content
+                     `(td
+                       (ul
+                        ,@content)))
+                   (G_ `(li
+                    ,(G_ `(a (@ (href 
"http://www.kosagi.com/w/index.php?title=Novena_Main_Page";))
+                             "Kosagi (Sutajio Ko-Usagi Pte Ltd)"))
+                    " (hardware)"))
+                   (G_ `(li "Andreas Enge (hosting)")))))))))
+
+      ,(G_
+        `(p "Other organizations and individuals helped Guix with hardware and
 hosting in the past and we thank them: "
-         (a (@ (href "https://www.fsf.org";)) "Free Software Foundation") ", "
-         (a (@ (href "https://es.gnu.org";)) "GNU España") ", "
-         (a (@ (href "https://fsffrance.org/index.en.html";)) "FSF France") ", "
-         (a (@ (href "https://gnunet.org/fsnsg";)) "Free Secure Network Systems 
Group")
-         " at the "
-         (a (@ (href "https://www.tum.de/";)) "Technische Universität München")
-         ".")))))
+            ,(G_ `(a (@ (href "https://www.fsf.org";)) "Free Software 
Foundation")) ", "
+            ,(G_ `(a (@ (href "https://es.gnu.org";)) "GNU España")) ", "
+            ,(G_ `(a (@ (href "https://fsffrance.org/index.en.html";)) "FSF 
France")) ", "
+            ,(G_ `(a (@ (href "https://gnunet.org/fsnsg";)) "Free Secure 
Network Systems Group"))
+            " at the "
+            ,(G_ `(a (@ (href "https://www.tum.de/";)) "Technische Universität 
München"))
+            "."))))))
diff --git a/website/apps/base/templates/graphics.scm 
b/website/apps/base/templates/graphics.scm
index 6089988..befbb19 100644
--- a/website/apps/base/templates/graphics.scm
+++ b/website/apps/base/templates/graphics.scm
@@ -6,66 +6,73 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (graphics-t))
 
 
 (define (graphics-t)
   "Return the Graphics page in SHTML."
   (theme
-   #:title '("Graphics")
+   #:title (C_ "webpage title" '("Graphics"))
    #:description
-   "Information about images used for the graphical identity of
-   GNU Guix and Guix System (formerly “GuixSD”)."
+   (G_ "Information about images used for the graphical identity
+   of GNU Guix and Guix System (formerly “GuixSD”).")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Branding" "Logo")
-   #:active-menu-item "About"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Donations|Branding|Logo") #\|)
+   #:active-menu-item (C_ "website menu" "About")
    #:css (list
          (guix-url "static/base/css/page.css"))
-   #:crumbs (list (crumb "Graphics" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Graphics") "./"))
    #:content
    `(main
      (section
       (@ (class "page centered-block limit-width"))
-      (h2 "Graphics")
+      ,(G_ `(h2 "Graphics"))
 
-      (p
-       "For questions regarding the graphics listed in this page,
-       please contact "
-       (a (@ (href "https://lists.gnu.org/mailman/listinfo/help-guix";))
-         ("help-guix@gnu.org"))
-       ".")
+      ,(G_
+        `(p
+          "For questions regarding the graphics listed in this page,
+          please contact "
+          ,(G_ `(a (@ (href 
"https://lists.gnu.org/mailman/listinfo/help-guix";))
+                   "help-guix@gnu.org"))
+          "."))
       (p
        (@ (class "centered-text"))
        (img (@ (src ,(guix-url "static/base/img/Guix.png"))
-              (alt "GNU Guix logotype"))))
-      (p
-       "The standalone Guix, formerly known as the “Guix System
-       Distribution” or GuixSD, had its own logo, which is now
-       deprecated.")
+               ,(G_ `(alt "GNU Guix logotype")))))
+      ,(G_
+        `(p
+          "The standalone Guix, formerly known as the “Guix System
+          Distribution” or GuixSD, had its own logo, which is now
+          deprecated."))
 
-      (p
-       "The GNU Guix and GuixSD
-       logotypes were designed by Luis Felipe López Acevedo
-       (a.k.a. sirgazil).  They are available under the following
-       terms:")
+      ,(G_
+        `(p
+          "The GNU Guix and GuixSD
+          logotypes were designed by Luis Felipe López Acevedo
+          (a.k.a. sirgazil).  They are available under the following
+          terms:"))
       (blockquote
        (p "Copyright © 2015 Luis Felipe López Acevedo")
        (p
-       "Permission is granted to copy, distribute and/or modify this
+        "Permission is granted to copy, distribute and/or modify this
         work under the terms of the "
-       (a (@ (href "https://creativecommons.org/licenses/by-sa/4.0/";))
-          "Creative Commons Attribution-ShareAlike 4.0 International License")
-       "."))
-      (p
-       "The source files (SVG) for these logotypes, their variants, and
-       other artwork used in the different components of the GNU Guix
-       project are available in the "
-       (a (@ (href 
"//git.savannah.gnu.org/cgit/guix/guix-artwork.git/tree/logo"))
-         "guix-artwork")
-       " repository, including the previous GNU Guix logotype designed
-       by Nikita Karetnikov in 2013 and "
-       (a (@ (href "https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25205";))
-         "superseded")
-       " by the golden GNU in 2016.")))))
+        `(a (@ (href "https://creativecommons.org/licenses/by-sa/4.0/";))
+            "Creative Commons Attribution-ShareAlike 4.0 International 
License")
+        "."))
+      ,(G_
+        `(p
+          "The source files (SVG) for these logotypes, their variants, and
+          other artwork used in the different components of the GNU Guix
+          project are available in the "
+          ,(G_
+            `(a (@ (href 
"//git.savannah.gnu.org/cgit/guix/guix-artwork.git/tree/logo"))
+                "guix-artwork"))
+          " repository, including the previous GNU Guix logotype designed
+          by Nikita Karetnikov in 2013 and "
+          ,(G_
+            `(a (@ (href 
"https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25205";))
+                "superseded"))
+          " by the golden GNU in 2016."))))))
diff --git a/website/apps/base/templates/help.scm 
b/website/apps/base/templates/help.scm
index 631be31..6e6e282 100644
--- a/website/apps/base/templates/help.scm
+++ b/website/apps/base/templates/help.scm
@@ -7,31 +7,32 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (help-t))
 
 
 (define (help-t)
   "Return the Help page in SHTML."
   (theme
-   #:title '("Help")
+   #:title (C_ "webpage title" '("Help"))
    #:description
-   "A list of resources about how to use GNU Guix, plus
+   (G_ "A list of resources about how to use GNU Guix, plus
    information about getting help from the community of users and
-   developers."
+   developers.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Help resources")
-   #:active-menu-item "Help"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Help resources") #\|)
+   #:active-menu-item (C_ "website menu" "Help")
    #:css (list
          (guix-url "static/base/css/page.css")
          (guix-url "static/base/css/item-preview.css"))
-   #:crumbs (list (crumb "Help" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Help") "./"))
    #:content
    `(main
      (section
       (@ (class "page"))
-      (h2 "Help")
+      ,(G_ `(h2 "Help"))
 
       (div
        (@ (class "centered-text"))
@@ -42,24 +43,25 @@
        (img
         (@ (src ,(guix-url "static/base/img/manual-icon.png"))
            (alt "")))
-       (h3 "GNU Guix Manual")
-       (p
-        "Documentation for GNU Guix is available
-         online.  You may also find more information about Guix by running "
-        (code "info guix") ".")
-       (p
-         ,(link-more #:label "Read Guix manual"
-                     #:url (guix-url "manual/en")))
+        ,(G_ `(h3 "GNU Guix Manual"))
+        ,(G_
+          `(p
+            "Documentation for GNU Guix is available
+            online.  You may also find more information about Guix by running "
+            ,(G_ `(code "info guix")) "."))
+        (p
+         ,(link-more #:label (G_ "Read Guix manual")
+                     #:url (guix-url "manual/en" #:localize #f)))
         (p
-         (a (@ (href ,(guix-url "manual/de"))) "Deutsch") " | "
-         (a (@ (href ,(guix-url "manual/en"))) "English") " | "
-         (a (@ (href ,(guix-url "manual/es"))) "español") " | "
-         (a (@ (href ,(guix-url "manual/fr"))) "français") " | "
-         (a (@ (href ,(guix-url "manual/ru"))) "русский")  " | "
-         (a (@ (href ,(guix-url "manual/zh-cn"))) "简体中文"))
+         (a (@ (href ,(guix-url "manual/de" #:localize #f))) "Deutsch") " | "
+         (a (@ (href ,(guix-url "manual/en" #:localize #f))) "English") " | "
+         (a (@ (href ,(guix-url "manual/es" #:localize #f))) "español") " | "
+         (a (@ (href ,(guix-url "manual/fr" #:localize #f))) "français") " | "
+         (a (@ (href ,(guix-url "manual/ru" #:localize #f))) "русский")  " | "
+         (a (@ (href ,(guix-url "manual/zh-cn" #:localize #f))) "简体中文"))
 
         ,(link-more
-         #:label "Get Guix reference card"
+          #:label (G_ "Get Guix reference card")
          #:url (guix-url "guix-refcard.pdf")))
 
 
@@ -67,15 +69,16 @@
         (@ (class "summary-box"))
         (img (@ (src ,(guix-url "static/base/img/videos-icon.png"))
                 (alt "")))
-        (h3 "Videos")
-        (p
-         "The collection of videos includes instructional material
-         to help you get started with every day use of GNU Guix as
-         well as other topics that present advanced features of the
-         system.")
+        ,(G_ `(h3 "Videos"))
+        ,(G_
+          `(p
+            "The collection of videos includes instructional material
+            to help you get started with every day use of GNU Guix as
+            well as other topics that present advanced features of the
+            system."))
         (p
          ,(link-more
-           #:label "Browse all videos"
+           #:label (G_ "Browse all videos")
            #:url (guix-url "videos/"))))
 
 
@@ -83,31 +86,33 @@
         (@ (class "summary-box"))
         (img (@ (src ,(guix-url "static/base/img/cookbook-icon.png"))
                 (alt "")))
-        (h3 "Cookbook")
-        (p
-         "Tutorials, how-to guides and examples contributed by the
-         Guix community which show you how to use the system and its
-         collection of packages to achieve common and not-so-common
-         goals users may have.")
+        ,(G_ `(h3 "Cookbook"))
+        ,(G_
+          `(p
+            "Tutorials, how-to guides and examples contributed by the
+            Guix community which show you how to use the system and its
+            collection of packages to achieve common and not-so-common
+            goals users may have."))
         (p
          ,(link-more
-           #:label "Browse the recipes"
-           #:url (guix-url "cookbook/"))))
+           #:label (G_ "Browse the recipes")
+           #:url (guix-url "cookbook/" #:localize #f))))
 
 
        (div
        (@ (class "summary-box"))
        (img (@ (src ,(guix-url "static/base/img/library-icon.png"))
                (alt "")))
-       (h3 "GNU Manuals")
-       (p
-        "Guix is a distribution of the "
-        (a (@ (href ,(gnu-url))) "GNU operating system")
-        ".  Documentation for GNU packages is
-         available online in various formats. ")
+        ,(G_ `(h3 "GNU Manuals"))
+        ,(G_
+          `(p
+            "Guix is a distribution of the "
+            ,(G_ `(a (@ (href ,(gnu-url))) "GNU operating system"))
+            ".  Documentation for GNU packages is
+            available online in various formats. "))
        (p
         ,(link-more
-          #:label "Browse GNU manuals"
+           #:label (G_ "Browse GNU manuals")
           #:url (gnu-url "manual"))))
 
 
@@ -115,18 +120,20 @@
        (@ (class "summary-box"))
        (img (@ (src ,(guix-url "static/base/img/chat-icon.png"))
                (alt "")))
-       (h3 "IRC Chat")
-       (p
-        "For real-time support from the community, you can connect
-         to the " (code "#guix") " channel on irc.freenode.net. There
-         you can get help about anything related to GNU Guix.")
-       (p
-        "The " (code "#guix") " channel is logged. Previous
-         conversations can be browsed online. See the "
-        (a (@ (href ,guix-irc-log-url)) "channel logs") ". ")
+        ,(G_ `(h3 "IRC Chat"))
+        ,(G_
+          `(p
+            "For real-time support from the community, you can connect
+            to the " (code "#guix") " channel on irc.freenode.net. There
+            you can get help about anything related to GNU Guix."))
+        ,(G_
+          `(p
+            "The " (code "#guix") " channel is logged. Previous
+            conversations can be browsed online. See the "
+            ,(G_ `(a (@ (href ,guix-irc-log-url)) "channel logs")) ". "))
        (p
         ,(link-more
-          #:label "Connect"
+           #:label (G_ "Connect")
           #:url (guix-url "contact/irc/"))))
 
 
@@ -134,13 +141,14 @@
        (@ (class "summary-box"))
        (img (@ (src ,(guix-url "static/base/img/email-icon.png"))
                (alt "")))
-       (h3 "Mailing lists")
-       (p
-        "Email support from the community is also available through
-         several mailing list. The messages sent to the lists are
-         public and archived online.")
+        ,(G_ `(h3 "Mailing lists"))
+        ,(G_
+          `(p
+            "Email support from the community is also available through
+            several mailing list. The messages sent to the lists are
+            public and archived online."))
 
        (p
         ,(link-more
-          #:label "See all lists"
+           #:label (G_ "See all lists")
           #:url (guix-url "contact/")))))))))
diff --git a/website/apps/base/templates/home.scm 
b/website/apps/base/templates/home.scm
index 8b81b82..7dc7813 100644
--- a/website/apps/base/templates/home.scm
+++ b/website/apps/base/templates/home.scm
@@ -9,24 +9,27 @@
   #:use-module (apps base utils)
   #:use-module (apps blog templates components)
   #:use-module (apps media templates components)
+  #:use-module (apps i18n)
   #:export (home-t))
 
 
 (define (home-t context)
   "Return the Home page in SHTML using the data in CONTEXT."
   (theme
-   #:title '("GNU's advanced distro and transactional package manager")
+   #:title (C_ "webpage title"
+               '("GNU's advanced distro and transactional package manager"))
    #:description
-   "Guix is an advanced distribution of the GNU operating system.
+   (G_ "Guix is an advanced distribution of the GNU operating system.
    Guix is technology that respects the freedom of computer users.
-   You are free to run the system for any purpose, study how it works,
-   improve it, and share it with the whole world."
+   You are free to run the system for any purpose, study how it
+   works, improve it, and share it with the whole world.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "GNU Guile" "Guile Scheme" "Transactional upgrades"
-     "Functional package management" "Reproducibility")
-   #:active-menu-item "Overview"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+   #:active-menu-item (C_ "website menu" "Overview")
    #:css (list
          (guix-url "static/base/css/item-preview.css")
          (guix-url "static/base/css/index.css")
@@ -36,87 +39,98 @@
      ;; Featured content.
      (section
       (@ (class "featured-content"))
-      (h2 (@ (class "a11y-offset")) "Summary")
+      ,(G_ `(h2 (@ (class "a11y-offset")) "Summary"))
       (ul
-       (li
-       (b "Liberating.")
-       " Guix is an advanced
-        distribution of the "
-       ,(link-yellow
-         #:label "GNU operating system"
-         #:url (gnu-url "gnu/about-gnu.html"))
-       " developed by the "
-       ,(link-yellow
-         #:label "GNU Project"
-         #:url (gnu-url))
-       "—which respects the "
-       ,(link-yellow
-         #:label "freedom of computer users"
-         #:url (gnu-url "distros/free-system-distribution-guidelines.html"))
-       ". ")
-
-       (li
-       (b "Dependable.")
-        " Guix "
-       ,(link-yellow
-         #:label "supports"
-         #:url (manual-url "Package-Management.html"))
-        " transactional upgrades and roll-backs, unprivileged
-        package management, "
-       ,(link-yellow
-         #:label "and more"
-         #:url (manual-url "Features.html"))
-       ".  When used as a standalone distribution, Guix supports "
-        ,(link-yellow
-          #:label "declarative system configuration"
-          #:url (manual-url "Using-the-Configuration-System.html"))
-        " for transparent and reproducible operating systems.")
-
-       (li
-       (b "Hackable.")
-       " It provides "
-       ,(link-yellow
-         #:label "Guile Scheme"
-         #:url (gnu-url "software/guile/"))
-       " APIs, including high-level embedded domain-specific
-        languages (EDSLs) to "
-       ,(link-yellow
-         #:label "define packages"
-         #:url (manual-url "Defining-Packages.html"))
-       " and "
-       ,(link-yellow
-         #:label "whole-system configurations"
-         #:url (manual-url "System-Configuration.html"))
-       "."))
+       ,(G_
+         `(li
+           ,(G_ `(b "Liberating."))
+           " Guix is an advanced distribution of the "
+           ,(G_ (link-yellow
+                 #:label "GNU operating system"
+                 #:url (gnu-url "gnu/about-gnu.html")))
+           " developed by the "
+           ,(G_ (link-yellow
+                 #:label "GNU Project"
+                 #:url (gnu-url)))
+           "—which respects the "
+           ,(G_ (link-yellow
+                 #:label "freedom of computer users"
+                 #:url (gnu-url "distros/free-system-distribution-\
+guidelines.html")))
+           ". "))
+
+       ;; TRANSLATORS: Package Management, Features and Using the
+       ;; Configuration System are section names in the English (en)
+       ;; manual.
+       ,(G_
+         `(li
+           ,(G_ `(b "Dependable."))
+           " Guix "
+           ,(G_ (manual-link-yellow "supports"
+                                    (G_ "en")
+                                    (G_ "Package-Management.html")))
+           " transactional upgrades and roll-backs, unprivileged \
+package management, "
+           ,(G_ (manual-link-yellow "and more"
+                                    (G_ "en")
+                                    (G_ "Features.html")))
+           ".  When used as a standalone distribution, Guix supports "
+           ,(G_ (manual-link-yellow "declarative system configuration"
+                                    (G_ "en")
+                                    (G_ 
"Using-the-Configuration-System.html")))
+           " for transparent and reproducible operating systems."))
+
+       ;; TRANSLATORS: Defining Packages and System Configuration are
+       ;; section names in the English (en) manual.
+       ,(G_
+         `(li
+           ,(G_ `(b "Hackable."))
+           " It provides "
+           ,(G_ (link-yellow
+                 #:label "Guile Scheme"
+                 #:url (gnu-url "software/guile/")))
+           " APIs, including high-level embedded domain-specific \
+languages (EDSLs) to "
+           ,(G_ (manual-link-yellow "define packages"
+                                    (G_ "en")
+                                    (G_ "Defining-Packages.html")))
+           " and "
+           ,(G_ (manual-link-yellow "whole-system configurations"
+                                    (G_ "en")
+                                    (G_ "System-Configuration.html")))
+           ".")))
 
       (div
        (@ (class "action-box centered-text"))
        ,(button-big
-        #:label (string-append "DOWNLOAD v" (latest-guix-version))
+         #:label (apply string-append
+                        (C_ "button" `("DOWNLOAD v" ,(latest-guix-version) 
"")))
         #:url (guix-url "download/")
         #:light #true)
        " " ; A space for readability in non-CSS browsers.
        ,(button-big
-        #:label "CONTRIBUTE"
+         #:label (C_ "button" "CONTRIBUTE")
         #:url (guix-url "contribute/")
         #:light #true)))
 
      ;; Discover Guix.
      (section
       (@ (class "discovery-box"))
-      (h2 "Discover Guix")
+      ,(G_ `(h2 "Discover Guix"))
 
-      (p
-       (@ (class "limit-width centered-block"))
-       "Guix comes with thousands of packages which include
-       applications, system tools, documentation, fonts, and other
-       digital goods readily available for installing with the "
-       ,(link-yellow #:label "GNU Guix" #:url "#guix-in-other-distros")
-       " package manager.")
+      ,(G_
+        `(p
+          (@ (class "limit-width centered-block"))
+          "Guix comes with thousands of packages which include \
+applications, system tools, documentation, fonts, and other digital \
+goods readily available for installing with the "
+          ,(G_ (link-yellow #:label "GNU Guix"
+                            #:url (identity "#guix-in-other-distros")))
+          " package manager."))
 
       ,(screenshots-box (context-datum context "screenshots"))
 
-      (h3 "Instructional videos")
+      ,(G_ '(h3 "Instructional videos"))
 
       (div
        ,@(map video-preview (context-datum context "videos")))
@@ -130,55 +144,57 @@
          #:light #true)
        " "
        ,(button-big
-        #:label "ALL PACKAGES"
+         #:label (C_ "button" "ALL PACKAGES")
         #:url (guix-url "packages/")
         #:light #true))
 
       ,(horizontal-separator #:light #true)
 
       ;; Guix in different fields.
-      (h3 "GNU Guix in your field")
+      ,(G_ `(h3 "GNU Guix in your field"))
 
-      (p
-       (@ (class "limit-width centered-block"))
-       "Read some stories about how people are using GNU Guix in their daily
-       lives.")
+      ,(G_
+        `(p
+          (@ (class "limit-width centered-block"))
+          "Read some stories about how people are using GNU Guix in
+their daily lives."))
 
       (div
        (@ (class "fields-box"))
 
        " " ; A space for readability in non-CSS browsers (same below).
        ,(button-big
-        #:label "SOFTWARE DEVELOPMENT"
-        #:url (guix-url "blog/tags/software-development/")
-        #:light #true)
+         #:label (C_ "button" "SOFTWARE DEVELOPMENT")
+         #:url (guix-url "blog/tags/software-development/")
+         #:light #true)
        " "
        ,(button-big
-        #:label "BIOINFORMATICS"
-        #:url (guix-url "blog/tags/bioinformatics/")
-        #:light #true)
+         #:label (C_ "button" "BIOINFORMATICS")
+         #:url (guix-url "blog/tags/bioinformatics/")
+         #:light #true)
        " "
        ,(button-big
-        #:label "HIGH PERFORMANCE COMPUTING"
-        #:url (guix-url "blog/tags/high-performance-computing/")
-        #:light #true)
+         #:label (C_ "button" "HIGH PERFORMANCE COMPUTING")
+         #:url (guix-url "blog/tags/high-performance-computing/")
+         #:light #true)
        " "
        ,(button-big
-        #:label "RESEARCH"
-        #:url (guix-url "blog/tags/research/")
-        #:light #true)
+         #:label (C_ "button" "RESEARCH")
+         #:url (guix-url "blog/tags/research/")
+         #:light #true)
        " "
        ,(button-big
-        #:label "ALL FIELDS..."
-        #:url (guix-url "blog/")
-        #:light #true))
+         #:label (C_ "button" "ALL FIELDS...")
+         #:url (guix-url "blog/")
+         #:light #true))
 
       ,(horizontal-separator #:light #true)
 
       ;; Using Guix in other distros.
-      (h3
-       (@ (id "guix-in-other-distros"))
-       "GNU Guix in other GNU/Linux distros")
+      ,(G_
+        `(h3
+          (@ (id "guix-in-other-distros"))
+          "GNU Guix in other GNU/Linux distros"))
 
       (div
        (@ (class "info-box"))
@@ -186,54 +202,55 @@
        (@ (src 
"https://audio-video.gnu.org/video/misc/2016-07__GNU_Guix_Demo_2.webm";)
           (poster ,(guix-url "static/media/img/guix-demo.png"))
           (controls "controls"))
-       (p
-        "Video: "
-        ,(link-yellow
-          #:label "Demo of Guix in another GNU/Linux distribution"
-          #:url 
"https://audio-video.gnu.org/video/misc/2016-07__GNU_Guix_Demo_2.webm";)
-        " (1 minute, 30 seconds).")))
+        ,(G_
+          `(p
+            "Video: "
+            ,(G_ (link-yellow
+                  #:label "Demo of Guix in another GNU/Linux distribution"
+                  #:url "https://audio-video.gnu.org/video/misc/\
+2016-07__GNU_Guix_Demo_2.webm"))
+            " (1 minute, 30 seconds)."))))
 
       (div
        (@ (class "info-box justify-left"))
-       (p
-       "If you don't use GNU Guix as a standalone GNU/Linux distribution,
-        you still can use it as a
-       package manager on top of any GNU/Linux distribution. This
-        way, you can benefit from all its conveniences.")
+       ,(G_ `(p
+              "If you don't use GNU Guix as a standalone GNU/Linux \
+distribution, you still can use it as a package manager on top of any \
+GNU/Linux distribution. This way, you can benefit from all its conveniences."))
 
-       (p
-       "Guix won't interfere with the package manager that comes
-        with your distribution. They can live together."))
+       ,(G_ `(p
+              "Guix won't interfere with the package manager that comes \
+with your distribution. They can live together.")))
 
       (div
        (@ (class "action-box centered-text"))
        ,(button-big
-        #:label "TRY IT OUT!"
+         #:label (C_ "button" "TRY IT OUT!")
         #:url (guix-url "download/")
         #:light #true)))
 
      ;; Latest Blog posts.
      (section
       (@ (class "centered-text"))
-      (h2 "Blog")
+      ,(G_ `(h2 "Blog"))
 
       ,@(map post-preview (context-datum context "posts"))
 
       (div
        (@ (class "action-box centered-text"))
        ,(button-big
-        #:label "ALL POSTS"
+         #:label (C_ "button" "ALL POSTS")
         #:url (guix-url "blog/"))))
 
      ;; Contact info.
      (section
       (@ (class "contact-box centered-text"))
-      (h2 "Contact")
+      ,(G_ `(h2 "Contact"))
 
       ,@(map contact-preview (context-datum context "contact-media"))
 
       (div
        (@ (class "action-box centered-text"))
        ,(button-big
-        #:label "ALL CONTACT MEDIA"
+         #:label (C_ "button" "ALL CONTACT MEDIA")
         #:url (guix-url "contact/")))))))
diff --git a/website/apps/base/templates/irc.scm 
b/website/apps/base/templates/irc.scm
index a58b9e0..05cfd2e 100644
--- a/website/apps/base/templates/irc.scm
+++ b/website/apps/base/templates/irc.scm
@@ -6,45 +6,49 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (irc-t))
 
 
 (define (irc-t)
   "Return the Kiwi IRC widget page in SHTML."
   (theme
-   #:title '("IRC" "Contact")
+   #:title
+   (list (C_ "webpage title" "IRC")
+         (C_ "webpage title" "Contact"))
    #:description
-   "Internet relay chat."
+   (G_ "Internet relay chat.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "IRC" "chat")
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|IRC|chat") #\|)
    #:active-menu-item "About"
    #:css (list
          (guix-url "static/base/css/page.css")
          (guix-url "static/base/css/irc.css"))
-   #:crumbs (list (crumb "Contact" (guix-url "contact/"))
-                 (crumb "IRC" "./"))
+   #:crumbs (list (crumb (C_ "webpage title" "Contact") (guix-url "contact/"))
+                 (crumb (C_ "webpage title" "IRC") "./"))
    #:content
    `(main
      (section
       (@ (class "page"))
-      (h2 "IRC")
+      ,(G_ `(h2 "IRC"))
 
-      (p
-       (@ (class "centered-block limit-width"))
-       "Join the " (code "#guix") " channel on the "
-       (a (@ (href "https://en.wikipedia.org/wiki/Freenode";))
-       "Freenode IRC network")
-       " to chat with the GNU Guix community or to get help
-       in real-time. You can use the chat widget below, or just use
-       the "
-       (a (@ (href 
"https://en.wikipedia.org/wiki/Comparison_of_Internet_Relay_Chat_clients";))
-         "IRC client")
-       " of your preference. Note that the conversations that happen
-       on the " (code "#guix") " channel are logged ("
-       (a (@ (href ,guix-irc-log-url)) "browse the log")
-       ").")
+      ,(G_
+        `(p
+          (@ (class "centered-block limit-width"))
+          "Join the " (code "#guix") " channel on the "
+          ,(G_ `(a (@ (href "https://en.wikipedia.org/wiki/Freenode";))
+                   "Freenode IRC network"))
+          " to chat with the GNU Guix community or to get help
+          in real-time. You can use the chat widget below, or just use
+          the "
+          ,(G_ `(a (@ (href 
"https://en.wikipedia.org/wiki/Comparison_of_Internet_Relay_Chat_clients";))
+                   "IRC client"))
+          " of your preference. Note that the conversations that happen
+          on the " (code "#guix") " channel are logged ("
+          ,(G_ `(a (@ (href ,guix-irc-log-url)) "browse the log"))
+          ")."))
 
       (iframe
        (@ (class "chat-widget centered-block")
diff --git a/website/apps/base/templates/menu.scm 
b/website/apps/base/templates/menu.scm
index 7af9f67..5b245f8 100644
--- a/website/apps/base/templates/menu.scm
+++ b/website/apps/base/templates/menu.scm
@@ -6,17 +6,20 @@
   #:use-module (apps base templates components)
   #:use-module (apps base templates theme)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (menu-t))
 
 
 (define (menu-t)
   "Return the Menu page in SHTML."
   (theme
-   #:title '("Menu")
-   #:description "Website menu."
-   #:keywords '("GNU" "Linux" "Unix" "Free software" "Libre software"
-               "Operating system" "GNU Hurd" "GNU Guix package manager"
-               "GNU Guile" "Guile Scheme" "Transactional upgrades"
-               "Functional package management" "Reproducibility")
-   #:active-menu-item "Menu"
+   #:title (C_ "webpage title" '("Menu"))
+   #:description (G_ "Website menu.")
+   #:keywords
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+   #:active-menu-item (C_ "website menu" "Menu")
    #:css (list (guix-url "static/base/css/menu.css"))))
diff --git a/website/apps/base/templates/security.scm 
b/website/apps/base/templates/security.scm
index f092074..b094339 100644
--- a/website/apps/base/templates/security.scm
+++ b/website/apps/base/templates/security.scm
@@ -3,9 +3,11 @@
 ;;; copyright interest on this file.
 
 (define-module (apps base templates security)
+  #:use-module (apps base templates components)
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (security-t))
 
 (define ludovics-key
@@ -14,36 +16,38 @@
 (define (security-t)
   "Return the Security page in SHTML."
   (theme
-   #:title '("Security")
+   #:title (C_ "webpage title" '("Security"))
    #:description
-   "Important information about getting security updates for your
-   GNU Guix installation, and instructions on how to report
-   security issues."
+   (G_ "Important information about getting security updates
+   for your GNU Guix installation, and instructions on how
+   to report security issues.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Security updates")
-   #:active-menu-item "About"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Security updates") #\|)
+   #:active-menu-item (C_ "website menu" "About")
    #:css (list
          (guix-url "static/base/css/page.css"))
-   #:crumbs (list (crumb "Security" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Security") "./"))
    #:content
    `(main
      (section
       (@ (class "page centered-block limit-width"))
-      (h2 "Security")
+      ,(G_ `(h2 "Security"))
 
-      (h3 "How to report security issues")
-      (p
-       "To report sensitive security issues in Guix itself or the
-        packages it provides, you can write to the private mailing list "
-       (a (@ (href "https://lists.gnu.org/mailman/listinfo/guix-security";))
-         ("guix-security@gnu.org")) ".  This list is monitored by a
-        small team of Guix developers.")
-      (p
-       "If you prefer to send your report using OpenPGP encrypted email,
-        please send it to one of the following Guix developers using their
-        respective OpenPGP key:")
+      ,(G_ `(h3 "How to report security issues"))
+      ,(G_
+        `(p
+          "To report sensitive security issues in Guix itself or the
+           packages it provides, you can write to the private mailing list "
+          (a (@ (href "https://lists.gnu.org/mailman/listinfo/guix-security";))
+             ("guix-security@gnu.org")) ".  This list is monitored by a
+           small team of Guix developers."))
+      ,(G_
+        `(p
+          "If you prefer to send your report using OpenPGP encrypted email,
+           please send it to one of the following Guix developers using their
+           respective OpenPGP key:"))
       (ul
         (li "Leo Famulari"
           (ul (@ (class "mono"))
@@ -58,29 +62,37 @@
           (ul (@ (class "mono"))
             (li "BCA6 89B6 3655 3801 C3C6 2150 197A 5888 235F ACAC"))))
 
-      (h3 "Release signatures")
-      (p
-       "Releases of Guix are signed using the OpenPGP "
-       "key with the fingerprint "
-       (span (@ (class "mono")) ,ludovics-key)
-       ".  "
-       "Users should "
-       (a (@ (href ,(manual-url "Binary-Installation.html"))) "verify")
-       " their downloads before extracting or running them.")
+      ,(G_ `(h3 "Release signatures"))
+      ,(G_
+        `(p
+          "Releases of Guix are signed using the OpenPGP "
+          "key with the fingerprint "
+          (span (@ (class "mono")) ,ludovics-key)
+          ".  "
+          "Users should "
+          ,(G_ (manual-href "verify"
+                            (G_ "en")
+                            (G_ "Binary-Installation.html")))
+          " their downloads before extracting or running them."))
 
-      (h3 "Security updates")
-      (p
-       "When security vulnerabilities are found in Guix or the "
-       "packages provided by Guix, we will provide "
-       (a (@ (href ,(manual-url "Security-Updates.html"))) "security updates")
-       " quickly and with minimal disruption for users.  When appropriate, "
-       "a security advisory is published on the blog with the "
-       (a (@ (href ,(guix-url "blog/tags/security-advisory")))
-          "Security Advisory tag")
-       " and on the " (a (@ (href ,(guix-url "contact")))
-                         (code "info-guix") " mailing list")
-       "; " (code "guix pull --news") " may also display the advisory.")
-      (p
-       "Guix uses a “rolling release” model.  All security "
-       "bug-fixes are pushed directly to the master branch.  There"
-       " is no “stable” branch that only receives security fixes.")))))
+      ,(G_ `(h3 "Security updates"))
+      ,(G_
+        `(p
+          "When security vulnerabilities are found in Guix or the "
+          "packages provided by Guix, we will provide "
+          ,(G_ (manual-href "security updates"
+                            (G_ "en")
+                            (G_ "Security-Updates.html")))
+          " quickly and with minimal disruption for users.  When appropriate, "
+          "a security advisory is published on the blog with the "
+          ,(G_ `(a (@ (href ,(guix-url "blog/tags/security-advisory")))
+                   "Security Advisory tag"))
+          " and on the "
+          ,(G_ `(a (@ (href ,(guix-url "contact")))
+                   ,(G_ `(code "info-guix")) " mailing list"))
+          "; " (code "guix pull --news") " may also display the advisory."))
+      ,(G_
+        `(p
+          "Guix uses a “rolling release” model.  All security "
+          "bug-fixes are pushed directly to the master branch.  There"
+          " is no “stable” branch that only receives security fixes."))))))
diff --git a/website/apps/base/templates/theme.scm 
b/website/apps/base/templates/theme.scm
index ae3a65c..f77d341 100644
--- a/website/apps/base/templates/theme.scm
+++ b/website/apps/base/templates/theme.scm
@@ -5,15 +5,16 @@
 (define-module (apps base templates theme)
   #:use-module (apps base templates components)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:export (theme))
 
 
 (define* (theme #:key
-               (lang-tag "en")
+                (lang-tag %current-ietf-tag)
                (title '())
                (description "")
                (keywords '())
-               (active-menu-item "About")
+                (active-menu-item (C_ "website menu" "About"))
                (css '())
                (scripts '())
                (crumbs '())
@@ -23,7 +24,8 @@
    LANG-TAG (string)
      IETF language tag. This is used to specify the language of the
      document. For example: en, en-CA. If not provided, the value
-     defaults to English (en).
+     defaults to the currently built language, i.e. the
+     %current-ietf-tag from (apps i18n).
 
    TITLE (list)
      A list of strings to form the value of the title element of the
@@ -65,12 +67,14 @@
   `((doctype "html")
 
     (html
-     (@ (lang "en"))
+     (@ (lang lang-tag))
 
      (head
       ,(if (null? title)
-          `(title "GNU Guix")
-          `(title ,(string-join (append title '("GNU Guix")) " — ")))
+           `(title ,(C_ "webpage title" "GNU Guix"))
+           `(title ,(string-join (append title
+                                         (C_ "webpage title" '("GNU Guix")))
+                                 " — ")))
       (meta (@ (charset "UTF-8")))
       (meta (@ (name "keywords") (content ,(string-join keywords ", "))))
       (meta (@ (name "description") (content ,description)))
@@ -91,7 +95,7 @@
             css)
       ;; Feeds.
       (link (@ (type "application/atom+xml") (rel "alternate")
-              (title "GNU Guix — Activity Feed")
+               (title ,(C_ "webpage title" "GNU Guix — Activity Feed"))
               (href ,(guix-url "feeds/blog.atom"))))
       (link (@ (rel "icon") (type "image/png")
               (href ,(guix-url "static/base/img/icon.png"))))
@@ -108,17 +112,22 @@
       ,(if (null? crumbs) "" (breadcrumbs crumbs))
 
       ,content
-      (footer
-       "Made with " (span (@ (class "metta")) "♥")
-       " by humans and powered by "
-       (a (@ (class "link-yellow") (href ,(gnu-url "software/guile/")))
-         "GNU Guile") ".  "
-         (a
-          (@ (class "link-yellow")
-             (href 
"//git.savannah.gnu.org/cgit/guix/guix-artwork.git/tree/website"))
-          "Source code")
-         " under the "
-         (a
-          (@ (class "link-yellow")
-             (href ,(gnu-url "licenses/agpl-3.0.html")))
-          "GNU AGPL") ".")))))
+      ,(G_
+        `(footer
+          "Made with " ,(G_ `(span (@ (class "metta")) "♥"))
+          " by humans and powered by "
+          ,(G_ `(a
+                 (@ (class "link-yellow")
+                    (href ,(gnu-url "software/guile/")))
+                 "GNU Guile"))
+          ".  "
+          ,(G_ `(a
+                 (@ (class "link-yellow")
+                    (href 
"//git.savannah.gnu.org/cgit/guix/guix-artwork.git/tree/website"))
+                 "Source code"))
+          " under the "
+          ,(G_ `(a
+                 (@ (class "link-yellow")
+                    (href ,(gnu-url "licenses/agpl-3.0.html")))
+                 "GNU AGPL"))
+          "."))))))
diff --git a/website/apps/base/utils.scm b/website/apps/base/utils.scm
index 946639e..bb202e4 100644
--- a/website/apps/base/utils.scm
+++ b/website/apps/base/utils.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2015 Mathieu Lirzin <mthl@openmailbox.org>
 ;;; Copyright © 2013 Alex Sassmannshausen <alex.sassmannshausen@gmail.com>
 ;;; Copyright © 2017 Eric Bavier <bavier@member.fsf.org>
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
 ;;; Initially written by sirgazil who waives all copyright interest on this
 ;;; file.
 ;;;
@@ -25,6 +26,7 @@
   #:use-module (apps aux lists)
   #:use-module (apps aux system)
   #:use-module (apps base types)
+  #:use-module (apps i18n)
   #:use-module (haunt page)
   #:use-module (ice-9 i18n)
   #:use-module (ice-9 match)
@@ -34,7 +36,9 @@
            guix-irc-log-url
            guix-url
            latest-guix-version
+            locale-display-name
            manual-url
+            manual-url-with-language
            number*
            paginate))
 
@@ -62,6 +66,14 @@
 (define latest-guix-version
   (make-parameter "1.1.0"))
 
+(define (locale-display-name)
+  "Return the display name of the current locale."
+  ;; TRANSLATORS: The locale’s display name; please include a country
+  ;; code like in English (US) *only* if there are multiple
+  ;; Translation Project teams for the same language.
+  (let ((str '(G_ "English (US)")))
+    (gettext (cadr str))))
+
 
 
 ;;;
@@ -94,16 +106,21 @@
   (string-append "https://git.savannah.gnu.org/cgit/guix.git/tree/"; subpath))
 
 
-(define* (guix-url #:optional (subpath ""))
+(define* (guix-url #:optional (subpath "") #:key (localize #t))
   "Append SUBPATH to GNU Guix root URL path (see guix-root-url-path).
 
    SUBPATH (string)
      An optional relative URL path to a resource in the GNU Guix path.
      For example: 'packages/icecat-XYZ/'.
 
+   LOCALIZE (boolean)
+     Whether to prepend the result of 'localized-root-path' to the URL path.
+
    RETURN VALUE (string)
      A URL path. For example: /software/guix/packages/icecat-XYZ/."
-  (string-append (guix-root-url-path) subpath))
+  (string-append (guix-root-url-path)
+                 (if localize (localized-root-path subpath) "")
+                 subpath))
 
 
 (define* (manual-url #:optional (subpath "")
@@ -120,7 +137,28 @@
   (string-append
    (guix-url (string-append (string-append "manual/" language
                                            "/html_node/")
-                            subpath))))
+                            subpath) #:localize #f)))
+
+(define* (manual-url-with-language _ language #:optional (subpath ""))
+  "Shorthand for manual-url without keywords for prettier output
+PO files when marked for translation.  It can be marked for translation
+as:
+
+  (G_ (manual-url-with-language (G_ \"en\") (G_ \"Some-section.html\")))
+
+   LANGUAGE (string)
+     Normalized language for the Guix manual as produced by
+'doc/build.scm' in the Guix source tree, i.e. \"en\" for the English
+manual.
+
+   SUBPATH (string)
+     Like manual-url.
+
+   RETURN VALUE (string)
+     A URL path. For example:
+     /software/guix/manual/en/html_node/System-installation.html."
+  ;; The _ argument is a placeholder for an arg added by G_, cf. 
i18n-howto.txt.
+  (manual-url subpath #:language language))
 
 
 
diff --git a/website/apps/blog/templates/components.scm 
b/website/apps/blog/templates/components.scm
index 14b12ac..8be9b22 100644
--- a/website/apps/blog/templates/components.scm
+++ b/website/apps/blog/templates/components.scm
@@ -8,6 +8,7 @@
   #:use-module (apps aux web)
   #:use-module (apps base utils)
   #:use-module (apps blog utils)
+  #:use-module (apps i18n)
   #:use-module (haunt post)
   #:use-module (srfi srfi-19)
   #:export (post-preview
@@ -29,11 +30,12 @@
     (h3 ,(post-ref post 'title))
     (p
      (@ (class "item-date"))
-     ,(date->string (post-date post) "~B ~e, ~Y"))
+     ,(date->string (post-date post) (C_ "SRFI-19 date->string format"
+                                         "~B ~e, ~Y")))
     (p
      (@ (class "item-summary"))
      ,(string-summarize (sxml->string* (post-sxml post)) 30)
-     "…")))
+     ,(C_ "blog post summary ellipsis" "…"))))
 
 
 (define* (sidebar tags #:optional (current-tag #false))
@@ -44,13 +46,13 @@
      Haunt's 'posts/group-by-tag' procedure in (haunt post) module."
   `(section
     (@ (class "side-bar"))
-    (h3 (@ (class "a11y-offset")) "Blog menu: ")
+    (h3 (@ (class "a11y-offset")) (G_ "Blog menu: "))
 
     (h4
      (@ (class "bar-title bar-title-top"))
      ,(if current-tag
-         "Get topic updates"
-         "Get blog updates"))
+          (G_ "Get topic updates")
+          (G_ "Get blog updates")))
     (ul
      (@ (class "bar-list"))
      (li (@ (class "bar-item"))
@@ -62,9 +64,9 @@
                                             (slugify current-tag)
                                             ".atom"))))
                    `(href ,(guix-url (url-path-join "feeds" "blog.atom")))))
-           " Atom feed")))
+            ,(C_ "button" "Atom feed"))))
 
-    (h4 (@ (class "bar-title")) "Posts by topic")
+    (h4 (@ (class "bar-title")) (G_ "Posts by topic"))
     (ul
      (@ (class "bar-list"))
      ,@(map
diff --git a/website/apps/blog/templates/feed.scm 
b/website/apps/blog/templates/feed.scm
index 842255d..0702318 100644
--- a/website/apps/blog/templates/feed.scm
+++ b/website/apps/blog/templates/feed.scm
@@ -10,6 +10,7 @@
   #:use-module (apps base types)
   #:use-module (apps base utils)
   #:use-module (apps blog utils)
+  #:use-module (apps i18n)
   #:use-module (haunt html)
   #:use-module (haunt post)
   #:use-module (srfi srfi-19)
@@ -28,7 +29,7 @@
       (@ (xmlns "http://www.w3.org/2005/Atom";))
       (id ,id)
       (title ,title)
-      (author (name "GNU Guix") (uri ,domain))
+      (author (name (C_ "feed author name" "GNU Guix")) (uri ,domain))
       (icon ,(guix-url "static/base/img/icon.png"))
       (updated ,(date->string (current-date) "~4"))
       (link (@ (rel "alternate") (href ,alternate)))
diff --git a/website/apps/blog/templates/post-list.scm 
b/website/apps/blog/templates/post-list.scm
index 86b9365..c91f24b 100644
--- a/website/apps/blog/templates/post-list.scm
+++ b/website/apps/blog/templates/post-list.scm
@@ -9,6 +9,7 @@
   #:use-module (apps base types)
   #:use-module (apps base utils)
   #:use-module ((apps blog templates components) #:prefix blog:)
+  #:use-module (apps i18n)
   #:export (post-list-t))
 
 
@@ -19,22 +20,24 @@
        (total-pages
         (number->string (context-datum context "total-pages"))))
     (theme
-     #:title (list (string-append "Page " page-number) "Blog")
+     #:title (list (G_ (string-append "Page " page-number ""))
+                   (C_ "webpage title" "Blog"))
      #:description
-     "Blog posts about GNU Guix."
+     (G_ "Blog posts about GNU Guix.")
      #:keywords
-     '("GNU" "Linux" "Unix" "Free software" "Libre software"
-       "Operating system" "GNU Hurd" "GNU Guix package manager"
-       "GNU Guile" "Guile Scheme" "Transactional upgrades"
-       "Functional package management" "Reproducibility")
-     #:active-menu-item "Blog"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Blog")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/item-preview.css")
           (guix-url "static/base/css/sidebar.css"))
      #:crumbs
-     (list (crumb "Blog" (guix-url "blog/"))
-          (crumb (string-append "Page " page-number)
+     (list (crumb (C_ "website menu" "Blog") (guix-url "blog/"))
+           (crumb (G_ (string-append "Page " page-number ""))
                  (guix-url (url-path-join "blog"
                                           "page"
                                           page-number
@@ -43,7 +46,7 @@
      `(main
        (section
        (@ (class "page centered-text"))
-       (h2 "Blog"
+        (h2 (G_ "Blog")
            ,(page-indicator (string->number page-number)
                             (string->number total-pages)))
 
diff --git a/website/apps/blog/templates/post.scm 
b/website/apps/blog/templates/post.scm
index 35c57a8..de02c6c 100644
--- a/website/apps/blog/templates/post.scm
+++ b/website/apps/blog/templates/post.scm
@@ -9,6 +9,7 @@
   #:use-module (apps base utils)
   #:use-module (apps blog utils)
   #:use-module ((apps blog templates components) #:prefix blog:)
+  #:use-module (apps i18n)
   #:use-module (haunt post)
   #:use-module (srfi srfi-19)
   #:export (post-t))
@@ -21,17 +22,17 @@
     (theme
      #:title (list (post-ref post 'title)
                   (date->string (post-date post) "~Y")
-                  "Blog")
+                   (C_ "webpage title" "Blog"))
      #:description
-     "Blog posts about GNU Guix."
+     (G_ "Blog posts about GNU Guix.")
      #:keywords tags
-     #:active-menu-item "Blog"
+     #:active-menu-item (C_ "website menu" "Blog")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/code.css")
           (guix-url "static/blog/css/post.css"))
      #:crumbs
-     (list (crumb "Blog" (guix-url "blog/"))
+     (list (crumb (C_ "website menu" "Blog") (guix-url "blog/"))
           (crumb (post-ref post 'title)
                  (guix-url (post-url-path post))))
      #:content
@@ -42,14 +43,15 @@
        (p
         (@ (class "post-metadata centered-text"))
         ,(post-ref post 'author) " — "
-        ,(date->string (post-date post) "~B ~e, ~Y"))
+         ,(date->string (post-date post) (C_ "SRFI-19 date->string format"
+                                             "~B ~e, ~Y")))
 
        ,(change-image-to-video
           (syntax-highlight (post-sxml post)))
 
        (div
         (@ (class "tag-list"))
-        (p "Related topics:")
+         ,(G_ `(p "Related topics:"))
 
         ,@(map
            (lambda (tag)
diff --git a/website/apps/blog/templates/tag.scm 
b/website/apps/blog/templates/tag.scm
index a000c8d..9537022 100644
--- a/website/apps/blog/templates/tag.scm
+++ b/website/apps/blog/templates/tag.scm
@@ -10,6 +10,7 @@
   #:use-module (apps base utils)
   #:use-module ((apps blog templates components) #:prefix blog:)
   #:use-module (apps blog utils)
+  #:use-module (apps i18n)
   #:export (tag-t))
 
 
@@ -21,25 +22,27 @@
        (total-pages
         (number->string (context-datum context "total-pages"))))
     (theme
-     #:title (list (string-append "Page " page-number) tag "Blog")
+     #:title (list (G_ (string-append "Page " page-number ""))
+                   tag (C_ "webpage title" "Blog"))
      #:description
-     (string-append "Blog posts about "
-                   tag
-                   " on GNU Guix.")
+     (G_ (string-append "Blog posts about "
+                        tag
+                        " on GNU Guix."))
      #:keywords
-     '("GNU" "Linux" "Unix" "Free software" "Libre software"
-       "Operating system" "GNU Hurd" "GNU Guix package manager"
-       "GNU Guile" "Guile Scheme" "Transactional upgrades"
-       "Functional package management" "Reproducibility")
-     #:active-menu-item "Blog"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Blog")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/item-preview.css")
           (guix-url "static/base/css/sidebar.css"))
      #:crumbs
-     (list (crumb "Blog" (guix-url "blog/"))
+     (list (crumb (C_ "website menu" "Blog") (guix-url "blog/"))
           (crumb tag (guix-url (tag-url-path tag)))
-          (crumb (string-append "Page " page-number)
+           (crumb (G_ (string-append "Page " page-number ""))
                  (guix-url (url-path-join (tag-url-path tag)
                                           "page"
                                           page-number
@@ -48,7 +51,7 @@
      `(main
        (section
        (@ (class "page centered-text"))
-       (h2 "Blog — " ,tag
+        (h2 ,(G_ "Blog — ") ,tag
            ,(page-indicator (string->number page-number)
                             (string->number total-pages)))
 
diff --git a/website/apps/blog/utils.scm b/website/apps/blog/utils.scm
index 7fd1581..501cbf0 100644
--- a/website/apps/blog/utils.scm
+++ b/website/apps/blog/utils.scm
@@ -19,6 +19,7 @@
 (define-module (apps blog utils)
   #:use-module (apps aux lists)
   #:use-module (apps aux web)
+  #:use-module (apps i18n)
   #:use-module (haunt post)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
@@ -141,8 +142,8 @@ tags.  This hack allows one to refer to a video from a 
Markdown document."
            `(video (@ (src ,src)
                       (poster ,(string-append src ".poster.png"))
                       (controls "controls"))
-                   (p (a (@ (href ,src) (class "link-subtle"))
-                         "Download video.")))
+                   (p ,(G_ `(a (@ (href ,src) (class "link-subtle"))
+                               "Download video."))))
            sxml)))
     ((tag ('@ attributes ...) body ...)
      `(,tag (@ ,@attributes) ,@(map change-image-to-video body)))
diff --git a/website/apps/download/data.scm b/website/apps/download/data.scm
index cfaa037..14db656 100644
--- a/website/apps/download/data.scm
+++ b/website/apps/download/data.scm
@@ -5,6 +5,7 @@
 (define-module (apps download data)
   #:use-module (apps base utils)
   #:use-module (apps download types)
+  #:use-module (apps i18n)
   #:export (system-downloads))
 
 
@@ -16,34 +17,44 @@
 (define system-downloads
   (list
    (download
-    #:title (string-append "GNU Guix System " (latest-guix-version))
+    #:title (C_ "download page title"
+                (string-append "GNU Guix System " (latest-guix-version) ""))
     #:description
     `(div
-      (p "USB/DVD ISO installer of the standalone Guix System."))
+      ,(G_ `(p "USB/DVD ISO installer of the standalone Guix System.")))
     #:image (guix-url "static/base/img/GuixSD-package.png")
     #:base-url (string-append 
"https://ftp.gnu.org/gnu/guix/guix-system-install-";
                              (latest-guix-version) ".")
     #:variants (list (variant "x86_64" "x86_64-linux.iso.xz")
                     (variant "i686" "i686-linux.iso.xz"))
-    #:manual (manual-url "System-Installation.html"))
+    ;; TRANSLATORS: System installation is a section name in the
+    ;; English (en) manual.
+    #:manual (G_ (manual-url-with-language (G_ "en")
+                                           "System-Installation.html")))
 
    (download
-    #:title (string-append "GNU Guix " (latest-guix-version) " QEMU Image")
+    #:title (C_ "download page title"
+                (string-append "GNU Guix " (latest-guix-version) " QEMU 
Image"))
     #:description
     `(div
-      (p "QCOW2 virtual machine (VM) image."))
+      ,(G_ `(p "QCOW2 virtual machine (VM) image.")))
     #:image (guix-url "static/base/img/QEMU-package.png")
     #:base-url (string-append 
"https://ftp.gnu.org/gnu/guix/guix-system-vm-image-";
                              (latest-guix-version) ".")
     #:variants (list (variant "x86_64" "x86_64-linux.xz"))
-    #:manual (manual-url "Running-Guix-in-a-VM.html"))
+    ;; TRANSLATORS: Running Guix in a VM is a section name in the
+    ;; English (en) manual.
+    #:manual (G_ (manual-url-with-language (G_ "en")
+                                           "Running-Guix-in-a-VM.html")))
 
    (download
-    #:title (string-append "GNU Guix " (latest-guix-version) " Binary")
+    #:title (C_ "download page title"
+                (string-append "GNU Guix " (latest-guix-version) " Binary"))
     #:description
-    '(p
-      "Self-contained tarball providing binaries for Guix and its
-      dependencies, to be installed on top of your Linux-based system.")
+    (G_
+     '(p
+       "Self-contained tarball providing binaries for Guix and its
+       dependencies, to be installed on top of your Linux-based system."))
     #:image (guix-url "static/base/img/Guix-package.png")
     #:base-url (string-append "https://ftp.gnu.org/gnu/guix/guix-binary-";
                              (latest-guix-version) ".")
@@ -51,13 +62,20 @@
                     (variant "i686" "i686-linux.tar.xz")
                     (variant "armhf" "armhf-linux.tar.xz")
                      (variant "aarch64" "aarch64-linux.tar.xz"))
-    #:manual (manual-url "Binary-Installation.html"))
+    ;; TRANSLATORS: Binary Installation is a section name in the
+    ;; English (en) manual.
+    #:manual (G_ (manual-url-with-language (G_ "en")
+                                           "Binary-Installation.html")))
 
    (download
-    #:title (string-append "GNU Guix " (latest-guix-version) " Source")
-    #:description '(p "Source code distribution.")
+    #:title (C_ "download page title"
+                (string-append "GNU Guix " (latest-guix-version) " Source"))
+    #:description (G_ '(p "Source code distribution."))
     #:image (guix-url "static/base/img/src-package.png")
     #:base-url (string-append "https://ftp.gnu.org/gnu/guix/guix-";
                              (latest-guix-version) ".")
     #:variants (list (variant "tarball" "tar.gz"))
-    #:manual (manual-url "Requirements.html"))))
+    ;; TRANSLATORS: Requirements is a section name in the English (en)
+    ;; manual.
+    #:manual (G_ (manual-url-with-language (G_ "en")
+                                           "Requirements.html")))))
diff --git a/website/apps/download/templates/components.scm 
b/website/apps/download/templates/components.scm
index 1c26da3..0fd5e2d 100644
--- a/website/apps/download/templates/components.scm
+++ b/website/apps/download/templates/components.scm
@@ -4,6 +4,7 @@
 
 (define-module (apps download templates components)
   #:use-module (apps download types)
+  #:use-module (apps i18n)
   #:export (download))
 
 
@@ -21,7 +22,7 @@
     (img (@ (src ,(download-image dnd)) (alt "")))
     (h3 ,(download-title dnd))
     ,(download-description dnd)
-    (p "Download options:")
+    ,(G_ `(p "Download options:"))
     ,@(map (lambda (variant)
             `(a
               (@ (class "download-btn")
@@ -34,7 +35,7 @@
          (download-variants dnd))
 
     (p
-     "Signatures: "
+     ,(G_ "Signatures: ")
      ,@(map (lambda (variant)
             `(a
               (@ (class "signature-btn")
@@ -46,4 +47,9 @@
               " ")) ; Force a space for readability in non-CSS browsers.
            (download-variants dnd)))
 
-    (p (a (@ (href ,(download-manual dnd))) "Installation instructions") ".")))
+    ,(G_
+      `(p
+        ,(G_
+          `(a (@ (href ,(download-manual dnd)))
+              "Installation instructions"))
+        "."))))
diff --git a/website/apps/download/templates/download-latest.scm 
b/website/apps/download/templates/download-latest.scm
index 4265581..609670c 100644
--- a/website/apps/download/templates/download-latest.scm
+++ b/website/apps/download/templates/download-latest.scm
@@ -18,10 +18,12 @@
 ;;; along with the GNU Guix web site.  If not, see 
<http://www.gnu.org/licenses/>.
 
 (define-module (apps download templates download-latest)
+  #:use-module (apps base templates components)
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
   #:use-module (apps download templates components)
+  #:use-module (apps i18n)
   #:use-module (guix ci)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
@@ -45,16 +47,16 @@
 
 (define images
   (list (make-image
-         "GNU Guix System on Linux"
-         "USB/DVD ISO installer of the standalone Guix System on Linux."
+         (C_ "download page title" "GNU Guix System on Linux")
+         (G_ "USB/DVD ISO installer of the standalone Guix System on Linux.")
          (guix-url "static/base/img/GuixSD-package.png")
          "iso9660-image"
          (list default-system)
          (list default-system)
          "ISO-9660")
         (make-image
-         "GNU Guix System on GNU Hurd"
-         "Virtual machine image of the standalone Guix System on GNU Hurd."
+         (C_ "download page title" "GNU Guix System on GNU Hurd")
+         (G_ "Virtual machine image of the standalone Guix System on GNU 
Hurd.")
          (guix-url "static/base/img/hurd.png")
          "hurd-barebones-disk-image"
          (list "qcow2")
@@ -88,7 +90,7 @@
       (img (@ (src ,logo) (alt "")))
       (h3 ,title)
       ,description
-      (p "Download options:")
+      ,(G_ `(p "Download options:"))
       ,@(map (lambda (system label)
                `(a
                  (@ (class "download-btn")
@@ -97,47 +99,52 @@
                  ,label
                  " ")) ; Force a space for readability in non-CSS browsers.
              systems labels)
-      (p "Build details: "
-         ,@(map (lambda (system label)
-                  `(a
-                    (@ (class "detail-btn")
-                       (download "")
-                       (href ,(build-detail-url job system)))
-                    ,label
-                    " ")) ; Force a space for readability in non-CSS browsers.
-                systems labels)))))
+      ,(G_
+        `(p "Build details: "
+            ,@(map (lambda (system label)
+                     `(a
+                       (@ (class "detail-btn")
+                          (download "")
+                          (href ,(build-detail-url job system)))
+                       ,label
+                       " ")) ; Force a space for readability in non-CSS 
browsers.
+                   systems labels))))))
 
 (define (download-latest-t)
   "Return the Download latest page in SHTML."
   (theme
-   #:title '("Download latest")
+   #:title (C_ "webpage title" '("Download latest"))
    #:description
-   "Download latest GNU Guix System images built by the Cuirass continuous
-integration system."
+   (G_ "Download latest GNU Guix System images built by the Cuirass continuous
+integration system.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Installer" "Source code" "Package manager")
-   #:active-menu-item "Download"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Installer|Source code|\
+Package manager") #\|)
+   #:active-menu-item (C_ "website menu" "Download")
    #:css (list
           (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/download.css"))
-   #:crumbs (list (crumb "Download" (guix-url "download/"))
-                  (crumb "Latest" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Download") (guix-url "download/"))
+                  (crumb (C_ "website menu" "Latest") "./"))
    #:content
    `(main
      (section
       (@ (class "page"))
-      (h2 "Download latest images")
-      (p
-       (@ (class "centered-block limit-width"))
-       "Download latest GNU Guix System images built by the "
-       (a (@ (href ,(manual-url "Continuous-Integration.html"))) "Cuirass")
-       " continuous integration system at "
-       (a (@ (href ,ci-url)) "ci.guix.gnu.org")
-       ". These images are " (b "development snapshots")
-       ", you might prefer to use stable images that can be found "
-       (a (@ (href ,(guix-url "download/"))) "here."))
+      ,(G_ `(h2 "Download latest images"))
+      ;; TRANSLATORS: Continuous Integration is a section name
+      ;; in the English (en) manual.
+      ,(G_
+        `(p
+          (@ (class "centered-block limit-width"))
+          "Download latest GNU Guix System images built by the "
+          ,(G_ (manual-href "Cuirass"  (G_ "en") (G_ 
"Continuous-Integration.html")))
+          " continuous integration system at "
+          (a (@ (href ,ci-url)) "ci.guix.gnu.org")
+          ". These images are " ,(G_ `(b "development snapshots"))
+          ", you might prefer to use stable images that can be found "
+          ,(G_ `(a (@ (href ,(guix-url "download/"))) "here."))))
       (div
        (@ (class "centered-block limit-width"))
        ,(map image-download images))))))
diff --git a/website/apps/download/templates/download.scm 
b/website/apps/download/templates/download.scm
index de2e458..bcf3cd2 100644
--- a/website/apps/download/templates/download.scm
+++ b/website/apps/download/templates/download.scm
@@ -19,60 +19,65 @@
 ;;; along with the GNU Guix web site.  If not, see 
<http://www.gnu.org/licenses/>.
 
 (define-module (apps download templates download)
+  #:use-module (apps base templates components)
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
   #:use-module (apps download templates components)
+  #:use-module (apps i18n)
   #:export (download-t))
 
 
 (define (download-t context)
   "Return the Download page in SHTML."
   (theme
-   #:title '("Download")
+   #:title (C_ "webpage title" '("Download"))
    #:description
-   "Installers and source files for GNU Guix.  GNU Guix can be
-   installed on different GNU/Linux distributions."
+   (G_ "Installers and source files for GNU Guix.  GNU Guix can be
+   installed on different GNU/Linux distributions.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Installer" "Source code" "Package manager")
-   #:active-menu-item "Download"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Installer|Source code|\
+Package manager") #\|)
+   #:active-menu-item (C_ "website menu" "Download")
    #:css (list
          (guix-url "static/base/css/page.css")
          (guix-url "static/base/css/download.css"))
-   #:crumbs (list (crumb "Download" "./"))
+   #:crumbs (list (crumb (C_ "website menu" "Download") "./"))
    #:content
    `(main
      (section
       (@ (class "page"))
-      (h2 "Download")
+      ,(G_ `(h2 "Download"))
 
-      (p
-       (@ (class "centered-block limit-width"))
-       "As of version " ,(latest-guix-version)
-       ", the standalone Guix System "
-       (a
-       (@ (href ,(manual-url "System-Installation.html")))
-       "can be installed")
-       " on an i686, x86_64, ARMv7, or AArch64 machine.  It uses the "
-       (a (@ (href ,(gnu-url "software/linux-libre"))) "Linux-Libre")
-       " kernel and the "
-       (a (@ (href ,(gnu-url "software/shepherd"))) "GNU Shepherd")
-       " init system. Alternately, GNU Guix
-       can be installed as an additional package manager on top of an
-       installed Linux-based system.")
+      ;; TRANSLATORS: System Installation is a section name
+      ;; in the English (en) manual.
+      ,(G_
+        `(p
+          (@ (class "centered-block limit-width"))
+          "As of version " ,(latest-guix-version)
+          ", the standalone Guix System "
+          ,(G_ (manual-href "can be installed" (G_ "en") (G_ 
"System-Installation.html")))
+          " on an i686, x86_64, ARMv7, or AArch64 machine.  It uses the "
+          ,(G_ `(a (@ (href ,(gnu-url "software/linux-libre"))) "Linux-Libre"))
+          " kernel and the "
+          ,(G_ `(a (@ (href ,(gnu-url "software/shepherd"))) "GNU Shepherd"))
+          " init system. Alternately, GNU Guix
+          can be installed as an additional package manager on top of an
+          installed Linux-based system."))
 
       (div
        (@ (class "centered-text"))
        ,@(map download (context-datum context "downloads")))
 
-      (p
-       (@ (class "centered-block limit-width"))
-       "Source code and binaries for the Guix System distribution ISO
-       image as well as GNU Guix can be found on the GNU servers at "
-       (a (@ (href "https://ftp.gnu.org/gnu/guix/";))
-         "https://ftp.gnu.org/gnu/guix/";)
-       ".  Older releases can still be found on "
-       (a (@ (href "https://alpha.gnu.org/gnu/guix/";))
-          "alpha.gnu.org") ".")))))
+      ,(G_
+        `(p
+          (@ (class "centered-block limit-width"))
+          "Source code and binaries for the Guix System distribution ISO
+          image as well as GNU Guix can be found on the GNU servers at "
+          (a (@ (href "https://ftp.gnu.org/gnu/guix/";))
+             "https://ftp.gnu.org/gnu/guix/";)
+          ".  Older releases can still be found on "
+          (a (@ (href "https://alpha.gnu.org/gnu/guix/";))
+             "alpha.gnu.org") "."))))))
diff --git a/website/apps/i18n.scm b/website/apps/i18n.scm
new file mode 100644
index 0000000..d88333a
--- /dev/null
+++ b/website/apps/i18n.scm
@@ -0,0 +1,132 @@
+;;; GNU Guix web site
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
+;;;
+;;; This file is part of the GNU Guix web site.
+;;;
+;;; The GNU Guix web site is free software; you can redistribute it and/or 
modify it
+;;; under the terms of the GNU Affero General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; The GNU Guix web site is distributed in the hope that it will be useful, 
but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU Affero General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Affero General Public License
+;;; along with the GNU Guix web site.  If not, see 
<http://www.gnu.org/licenses/>.
+
+(define-module (apps i18n)
+  #:use-module (haunt asset)
+  #:use-module (haunt page)
+  #:use-module (haunt utils)
+  #:use-module (ice-9 match)
+  #:use-module (sexp-xgettext)
+  #:use-module (srfi srfi-1)
+  #:export (G_
+            N_
+            C_
+            NC_
+            %current-ietf-tag
+            %current-lang
+            %current-lingua
+            builder->localized-builder
+            builders->localized-builders
+            ietf-tags-file-contents
+            localized-root-path))
+
+(define %gettext-domain
+  "guix-website")
+
+(bindtextdomain %gettext-domain (getcwd))
+(bind-textdomain-codeset %gettext-domain "UTF-8")
+(textdomain %gettext-domain)
+
+;; NOTE: The sgettext macros have no hygiene because they use
+;; datum->syntax and do not preserve the semantics of anything looking
+;; like an sgettext macro.  This is an exceptional use case; do not
+;; try this at home.
+
+(define-syntax G_
+  sgettext)
+
+(set-simple-keywords! '(G_))
+
+(define-syntax N_ ;like ngettext
+  sngettext)
+
+(define-syntax C_ ;like pgettext
+  spgettext)
+
+(define-syntax NC_ ;like npgettext
+  snpgettext)
+
+(set-complex-keywords! '(N_ C_ NC_))
+
+(define %current-lingua
+  ;; strip the character encoding:
+  (car (string-split (setlocale LC_ALL) #\.)))
+
+(define-syntax ietf-tags-file-contents
+  (identifier-syntax
+   (force (delay (call-with-input-file
+                     "po/ietf-tags.scm"
+                   (lambda (port) (read port)))))))
+
+
+(define %current-ietf-tag
+  (or (assoc-ref ietf-tags-file-contents %current-lingua)
+      "en"))
+
+(define %current-lang
+  (car (string-split %current-ietf-tag #\-)))
+
+(define* (localized-root-path url #:key (lingua %current-ietf-tag))
+  "Given a URL as used in a href attribute, return the URL prefix
+'builder->localized-builder' would use for the URL when called with
+LINGUA."
+  (if (or (string-suffix? ".html" url)
+          (string-suffix? "/" url))
+      (string-append lingua "/")
+      ""))
+
+(define (first-value arg)
+  "For some reason the builder returned by static-directory returns
+multiple values.  This procedure is used to retain only the first
+return value.  TODO: This should not be necessary."
+  arg)
+
+(define <asset>
+  (@@ (haunt asset) <asset>))
+
+(define <page>
+  (@@ (haunt page) <page>))
+
+(define (builder->localized-builder builder)
+  "Return a Haunt builder procedure generated from an existing BUILDER
+with translations for the current system locale coming from
+sexp-xgettext."
+  (compose
+   (lambda (pages-and-assets)
+     (map
+      (lambda (page-or-asset)
+        (match page-or-asset
+          (($ <page> file-name contents writer)
+           (let ((new-name (string-append (localized-root-path file-name)
+                                          file-name)))
+             (make-page new-name contents writer)))
+          (($ <asset> source target)
+           (let ((new-name (string-append (localized-root-path target) 
target)))
+             (make-asset source new-name)))))
+      pages-and-assets))
+   (lambda (site posts)
+     (first-value (builder site posts)))))
+
+(define (builders->localized-builders builders)
+  "Return a list of new Haunt builder procedures generated from
+BUILDERS and localized via sexp-xgettext for the current system
+locale."
+  (flatten
+   (map-in-order
+    builder->localized-builder
+    builders)))
diff --git a/website/apps/media/data.scm b/website/apps/media/data.scm
index 623293f..65f1074 100644
--- a/website/apps/media/data.scm
+++ b/website/apps/media/data.scm
@@ -5,6 +5,7 @@
 
 (define-module (apps media data)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps media types)
   #:use-module (srfi srfi-19)
   #:export (playlists
@@ -21,57 +22,57 @@
   (list
    (list
     (video
-     #:title "Installation from Script"
+     #:title (C_ "video title" "Installation from Script")
      #:description
-     '(p "Explains how to install Guix on distributions not running
-GNU Guix.")
+     (G_ '(p "Explains how to install Guix on distributions not running
+GNU Guix."))
      #:url "https://guix.gnu.org/guix-videos/01-installation-from-script.webm";
      #:poster (guix-url "static/videos/img/installation-from-script.png")
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S")))
    (list
     (video
-     #:title "Everyday use of GNU Guix, Part One"
+     #:title (C_ "video title" "Everyday use of GNU Guix, Part One")
      #:description
-     '(p "How to install packages and how to manage software package
-generations.")
+     (G_ '(p "How to install packages and how to manage software package
+generations."))
      #:url "https://guix.gnu.org/guix-videos/02-everyday-use-part-one.webm";
      #:poster (guix-url "static/videos/img/everyday-use-01.png")
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S"))
     (video
-     #:title "Everyday use of GNU Guix, Part Two"
+     #:title (C_ "video title" "Everyday use of GNU Guix, Part Two")
      #:description
-     '(p "How to upgrade software and how to reclaim storage space.")
+     (G_ '(p "How to upgrade software and how to reclaim storage space."))
      #:url "https://guix.gnu.org/guix-videos/02-everyday-use-part-two.webm";
      #:poster (guix-url "static/videos/img/everyday-use-02.png")
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S")))
    (list
     (video
-     #:title "Asking for help"
+     #:title (C_ "video title" "Asking for help")
      #:description
-     '(p "How to get help from the Guix community.")
+     (G_ '(p "How to get help from the Guix community."))
      #:url "https://guix.gnu.org/guix-videos/03-help.webm";
      #:poster (guix-url "static/videos/img/help.png")
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S")))
    (list
     (video
-     #:title "Packaging, Part One"
+     #:title (C_ "video title" "Packaging, Part One")
      #:description
-     '(p "How to set up a development environment for GNU Guix.")
+     (G_ '(p "How to set up a development environment for GNU Guix."))
      #:url "https://guix.gnu.org/guix-videos/04-packaging-part-one.webm";
      #:poster (guix-url "static/videos/img/packaging-01.png")
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S"))
-                (video
-     #:title "Packaging, Part Two"
+    (video
+     #:title (C_ "video title" "Packaging, Part Two")
      #:description
-     '(p "How to create a package recipe for not yet packaged software.")
+     (G_ '(p "How to create a package recipe for not yet packaged software."))
      #:url "https://guix.gnu.org/guix-videos/04-packaging-part-two.webm";
      #:poster (guix-url "static/videos/img/packaging-02.png")
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S"))
                     (video
-     #:title "Packaging, Part Three"
+     #:title (C_ "video title" "Packaging, Part Three")
      #:description
-     '(p "How to submit a package for inclusion in the GNU Guix
-distribution.")
+     (G_ '(p "How to submit a package for inclusion in the GNU Guix
+distribution."))
      #:url "https://guix.gnu.org/guix-videos/04-packaging-part-three.webm";
      #:poster (guix-url "static/videos/img/packaging-03.png")
      #:last-updated (string->date "2020-03-28T16:00:00" 
"~Y-~m-~dT~H:~M:~S")))))
@@ -80,43 +81,43 @@ distribution.")
 (define screenshots
   (list
    (screenshot
-    #:title "Graphical log-in"
+    #:title (C_ "screenshot title" "Graphical log-in")
     #:slug "slim"
     #:image (guix-url "static/media/img/gdm-sessions.png")
     #:preview (guix-url "static/media/img/gdm-sessions.mini.png")
-    #:caption "Graphical log-in screen")
+    #:caption (G_ "Graphical log-in screen"))
 
    (screenshot
-    #:title "GNOME"
+    #:title (C_ "screenshot title" "GNOME")
     #:slug "gnome"
     #:image (guix-url "static/media/img/gnome.png")
     #:preview (guix-url "static/media/img/gnome.mini.png")
-    #:caption "GNOME desktop environment")
+    #:caption (G_ "GNOME desktop environment"))
 
    (screenshot
-    #:title "Xfce"
+    #:title (C_ "screenshot title" "Xfce")
     #:slug "xfce"
     #:image (guix-url "static/media/img/xfce.png")
     #:preview (guix-url "static/media/img/xfce.mini.png")
-    #:caption "Xfce desktop environment")
+    #:caption (G_ "Xfce desktop environment"))
 
    (screenshot
-    #:title "Virtual machine"
+    #:title (C_ "screenshot title" "Virtual machine")
     #:slug "virtual-machine"
     #:image (guix-url "static/media/img/guix-system-vm.png")
     #:preview (guix-url "static/media/img/guix-system-vm.mini.png")
-    #:caption "Virtual machine started with 'guix system vm'")
+    #:caption (G_ "Virtual machine started with 'guix system vm'"))
 
    (screenshot
-    #:title "Sway"
+    #:title (C_ "screenshot title" "Sway")
     #:slug "sway"
     #:image (guix-url "static/media/img/sway.png")
     #:preview (guix-url "static/media/img/sway.mini.png")
-    #:caption "Sway window manager running wayland")
+    #:caption (G_ "Sway window manager running wayland"))
 
    (screenshot
-    #:title "Enlightenment"
+    #:title (C_ "screenshot title" "Enlightenment")
     #:slug "enlightenment"
     #:image (guix-url "static/media/img/enlightenment-inkscape.png")
     #:preview (guix-url "static/media/img/enlightenment-inkscape.mini.png")
-    #:caption "Enlightenment, Inkscape, and Serbian text")))
+    #:caption (G_ "Enlightenment, Inkscape, and Serbian text"))))
diff --git a/website/apps/media/templates/components.scm 
b/website/apps/media/templates/components.scm
index 945b9db..d928d23 100644
--- a/website/apps/media/templates/components.scm
+++ b/website/apps/media/templates/components.scm
@@ -10,6 +10,7 @@
   #:use-module (apps aux web)
   #:use-module (apps base templates components)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps media types)
   #:use-module (apps media utils)
   #:use-module (srfi srfi-19)
@@ -66,7 +67,7 @@ top."
         (controls "controls"))
       ;; TODO: Insert missing video-tracks.
       (p
-       "Download video: "
+       (G_ "Download video: ")
        ,(link-subtle
          #:label (video-title video)
          #:url (video-url video)))))
@@ -97,5 +98,5 @@ given video object.
     ,(video-description video)
     ,(let ((date (video-last-updated video)))
        (if date
-           `(p "Last updated: " ,(date->string date))
+           (G_ `(p "Last updated: " ,(date->string date) ""))
            ""))))
diff --git a/website/apps/media/templates/screenshot.scm 
b/website/apps/media/templates/screenshot.scm
index b738340..b2d42b8 100644
--- a/website/apps/media/templates/screenshot.scm
+++ b/website/apps/media/templates/screenshot.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base utils)
   #:use-module (apps media templates components)
   #:use-module (apps media types)
+  #:use-module (apps i18n)
   #:export (screenshot-t))
 
 
@@ -16,14 +17,15 @@
   (let ((shot (context-datum context "screenshot"))
        (shots (context-datum context "screenshots")))
     (theme
-     #:title (list (screenshot-title shot) "Screenshots")
+     #:title (list (screenshot-title shot) (C_ "webpage title" "Screenshots"))
      #:description (screenshot-caption shot)
      #:keywords
-     '("GNU" "Linux" "Unix" "Free software" "Libre software"
-       "Operating system" "GNU Hurd" "GNU Guix package manager"
-       "GNU Guile" "Guile Scheme" "Transactional upgrades"
-       "Functional package management" "Reproducibility")
-     #:active-menu-item "Media"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Media")
      #:css (list (guix-url "static/base/css/index.css")
                  (guix-url "static/media/css/screenshots.css"))
      #:content
diff --git a/website/apps/media/templates/screenshots-overview.scm 
b/website/apps/media/templates/screenshots-overview.scm
index 2b19d05..0ab29b2 100644
--- a/website/apps/media/templates/screenshots-overview.scm
+++ b/website/apps/media/templates/screenshots-overview.scm
@@ -6,6 +6,7 @@
 (define-module (apps media templates screenshots-overview)
   #:use-module (apps base templates theme)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps media templates components)
   #:export (screenshots-overview-t))
 
@@ -13,14 +14,15 @@
 (define (screenshots-overview-t screenshots)
   "Return an SHTML page for all SCREENSHOTS."
   (theme
-   #:title '("Screenshots")
-   #:description "Overview of all screenshots."
+   #:title (C_ "webpage title" '("Screenshots"))
+   #:description (G_ "Overview of all screenshots.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "GNU Guile" "Guile Scheme" "Transactional upgrades"
-     "Functional package management" "Reproducibility")
-   #:active-menu-item "Media"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+   #:active-menu-item (C_ "website menu" "Media")
    #:css (list (guix-url "static/base/css/index.css")
                (guix-url "static/base/css/screenshots.css"))
    #:content
diff --git a/website/apps/media/templates/video-list.scm 
b/website/apps/media/templates/video-list.scm
index e05aa62..7ded416 100644
--- a/website/apps/media/templates/video-list.scm
+++ b/website/apps/media/templates/video-list.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps media data)
   #:use-module (apps media templates components)
   #:use-module (apps media types)
@@ -18,18 +19,18 @@
 (define (video-list-t)
   "Return a list of videos in SHTML."
   (theme
-   #:title '("Videos")
+   #:title (C_ "webpage title" '("Videos"))
    #:description
-   "Video about GNU Guix."
+   (G_ "Video about GNU Guix.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Help resources" "Videos")
-   #:active-menu-item "Videos"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Help resources|Videos") #\|)
+   #:active-menu-item (C_ "website menu" "Media")
    #:css (list
           (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/index.css"))
-   #:crumbs (list (crumb "Videos" (guix-url "videos/")))
+   #:crumbs (list (crumb (C_ "website menu" "Videos") (guix-url "videos/")))
    #:content
    `(main
      (@ (class "page centered-block limit-width"))
diff --git a/website/apps/media/templates/video.scm 
b/website/apps/media/templates/video.scm
index dff5a8a..4a04fa3 100644
--- a/website/apps/media/templates/video.scm
+++ b/website/apps/media/templates/video.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps media templates components)
   #:use-module (apps media types)
   #:use-module (apps media utils)
@@ -18,18 +19,18 @@
   "Return a page in SHTML for the given VIDEO.  If true, links to the
 PREVIOUS and NEXT videos are added."
   (theme
-   #:title (list "Video" (video-title video))
+   #:title (list (C_ "webpage title" "Video") (video-title video))
    #:description
-   "Video about GNU Guix."
+   (G_ "Video about GNU Guix.")
    #:keywords
-   '("GNU" "Linux" "Unix" "Free software" "Libre software"
-     "Operating system" "GNU Hurd" "GNU Guix package manager"
-     "Help resources" "Videos")
-   #:active-menu-item "Videos"
+   (string-split ;TRANSLATORS: |-separated list of webpage keywords
+    (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|Help resources|Videos") #\|)
+   #:active-menu-item (C_ "website menu" "Media")
    #:css (list
           (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/index.css"))
-   #:crumbs (list (crumb "Videos" (guix-url "videos/"))
+   #:crumbs (list (crumb (C_ "website menu" "Videos") (guix-url "videos/"))
                   (crumb (video-title video) "./"))
    #:content
    `(main
@@ -41,12 +42,12 @@ PREVIOUS and NEXT videos are added."
             (@ (class "fields-box"))
             ,(if previous
                  (button-big
-                  #:label "← Previous"
+                  #:label (C_ "button" "← Previous")
                   #:url (guix-url (video->url previous)))
                  "")
             ,(if next
                  (button-big
-                  #:label "Next →"
+                  #:label (C_ "button" "Next →")
                   #:url (guix-url (video->url next)))
                  ""))
           ""))))
diff --git a/website/apps/packages/templates/components.scm 
b/website/apps/packages/templates/components.scm
index a80115f..59b4857 100644
--- a/website/apps/packages/templates/components.scm
+++ b/website/apps/packages/templates/components.scm
@@ -8,6 +8,7 @@
   #:use-module (apps aux web)
   #:use-module (apps base templates components)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps packages data)
   #:use-module (apps packages types)
   #:use-module (apps packages utils)
@@ -55,37 +56,38 @@
      ;; 'gnu-package?' might fetch stuff from the network.  Assume #f if that
      ;; doesn't work.
      ,(if (false-if-exception (gnu-package? package))
-          '(p (i "This is a GNU package.  "))
+          `(p (i ,(G_ "This is a GNU package.  ")))
           "")
 
      ,(package-description-shtml package))
 
     (ul
      (@ (class "package-info"))
-     (li (b "License:") " "
-        ,(license->shtml (package-license package))
-        ".")
-
-     (li (b "Website:") " "
-        ,(link-subtle #:label (package-home-page package)
-                      #:url (package-home-page package)) ".")
-
-     (li (b "Package source:") " "
-        ,(location->shtml (package-location package))
-        ".")
-
-     (li (b "Patches:") " "
-        ,(patches->shtml (package-patches package))
-        ".")
-
-     (li (b "Lint issues:") " "
-        ,(if (null? (package-lint-issues package))
-             "No"
-             (link-subtle #:label "Yes"
-                          #:url (guix-url "packages/issues/")))
-        ".")
-
-     (li (b "Builds:") " " ,(supported-systems->shtml package) ".")
+     ,(G_ `(li ,(G_ `(b "License:")) " "
+               ,(license->shtml (package-license package))
+               "."))
+
+     ,(G_ `(li ,(G_ `(b "Website:")) " "
+               ,(link-subtle #:label (package-home-page package)
+                             #:url (package-home-page package)) "."))
+
+     ,(G_ `(li ,(G_ `(b "Package source:")) " "
+               ,(location->shtml (package-location package))
+               "."))
+
+     ,(G_ `(li ,(G_ `(b "Patches:")) " "
+               ,(patches->shtml (package-patches package))
+               "."))
+
+     ,(G_ `(li ,(G_ `(b "Lint issues:")) " "
+               ,(if (null? (package-lint-issues package))
+                    (G_ "No")
+                    (link-subtle #:label (G_ "Yes")
+                                 #:url (guix-url "packages/issues/")))
+               "."))
+
+     ,(G_ `(li ,(G_ `(b "Builds:")) " "
+               ,(supported-systems->shtml package) "."))
      "\n")))
 
 
@@ -99,7 +101,7 @@
      A span element if the count is 0. A mark element otherwise."
   `(,(if (> count 0) 'mark 'span)
     ,(number->string count)
-    ,(if (= count 1) " issue" " issues")))
+    ,(N_ " issue" " issues" count)))
 
 
 (define* (letter-selector #:optional (active-letter ""))
@@ -110,10 +112,10 @@
      The letter that should be displayed as active."
   `(section
     (@ (class "letter-selector"))
-    (h3 (@ (class "a11y-offset")) "Packages menu: ")
+    ,(G_ `(h3 (@ (class "a11y-offset")) "Packages menu: "))
 
-    (h4 (@ (class "selector-title selector-title-top"))
-       "Browse alphabetically")
+    ,(G_ `(h4 (@ (class "selector-title selector-title-top"))
+              "Browse alphabetically"))
     (div
      (@ (class "selector-box-padded"))
      ,@(map
@@ -199,7 +201,7 @@
      If the list of patches is empty, return the string 'None'.
      Otherwise, return a list of links to patches."
   (if (null? patches)
-      "None"
+      (G_ "None")
       (separate
        (map (lambda (patch)
              (link-subtle #:label (ilink-name patch)
@@ -216,9 +218,9 @@
      The letter in which the current packages are listed."
   `(section
     (@ (class "side-bar"))
-    (h3 (@ (class "a11y-offset")) "Packages menu: ")
+    ,(G_ `(h3 (@ (class "a11y-offset")) "Packages menu: "))
 
-    (h4 (@ (class "bar-title bar-title-top")) "Browse alphabetically")
+    ,(G_ `(h4 (@ (class "bar-title bar-title-top")) "Browse alphabetically"))
     (div
      (@ (class "bar-box-padded"))
      ,@(map
@@ -233,16 +235,16 @@
 
     ;; FIXME: This is currently too costly to produce so we just disable it.
 
-    ;; (h4 (@ (class "bar-title")) "Packages Issues")
+    ;; ,(G_ `(h4 (@ (class "bar-title")) "Packages Issues"))
     ;; (ul
     ;;  (@ (class "bar-list"))
     ;;  (li (@ (class "bar-item"))
-    ;;      (a (@ (class "bar-link")
-    ;;            (href ,(guix-url "packages/issues/lint/"))) "Lint"))
+    ;;      ,(G_ `(a (@ (class "bar-link")
+    ;;                  (href ,(guix-url "packages/issues/lint/"))) "Lint")))
     ;;  (li (@ (class "bar-item"))
-    ;;      (a (@ (class "bar-link")
-    ;;            (href ,(guix-url "packages/issues/reproducibility/")))
-    ;;         "Reproducibility")))
+    ;;      ,(G_ `(a (@ (class "bar-link")
+    ;;                  (href ,(guix-url "packages/issues/reproducibility/")))
+    ;;               "Reproducibility"))))
     ))
 
 
@@ -265,7 +267,7 @@
                   %hydra-supported-systems
                   (package-transitive-supported-systems package))))
     (if (null? systems)
-       "None"
+        (G_ "None")
         ;; TODO: There's currently no way to refer to a job like
         ;; 'coreutils-8.32' in the Cuirass web UI.  Add such a link once it's
         ;; become available.
diff --git a/website/apps/packages/templates/detailed-index.scm 
b/website/apps/packages/templates/detailed-index.scm
index 89c6f23..4d31ac5 100644
--- a/website/apps/packages/templates/detailed-index.scm
+++ b/website/apps/packages/templates/detailed-index.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps packages templates components)
   #:use-module (srfi srfi-19)
   #:export (detailed-index-t))
@@ -18,39 +19,45 @@
 packages to advertise."
   (let ((packages (context-datum context "packages")))
     (theme
-     #:title (list "Packages")
+     #:title (C_ "webpage title" (list "Packages"))
      #:description
-     "List of packages available through GNU Guix."
+     (G_ "List of packages available through GNU Guix.")
      #:keywords
-     (list "GNU" "Linux" "Unix" "Free software" "Libre software"
-          "Operating system" "GNU Hurd" "GNU Guix package manager"
-          "GNU Guile" "Guile Scheme" "Transactional upgrades"
-          "Functional package management" "Reproducibility")
-     #:active-menu-item "Packages"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Packages")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/item-preview.css")
           (guix-url "static/packages/css/letter-selector.css")
           (guix-url "static/packages/css/package-list.css"))
      #:crumbs
-     (list (crumb "Packages" (guix-url "packages/")))
+     (list (crumb (C_ "website menu" "Packages") (guix-url "packages/")))
      #:content
      `(main
        (section
        (@ (class "page centered-text"))
-       (h2 "Packages")
+        ,(G_ `(h2 "Packages"))
 
-       (p
-        (@ (class "limit-width centered-block"))
-        "GNU Guix provides " ,(number* (or total (length packages)))
-        " packages transparently "
-        (a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
-           "available as pre-built binaries")
-        ". These pages provide a complete list of the packages.  Our "
-        (a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
-           "continuous integration system")
-        " shows their current build status "
-        "(updated " ,(date->string (current-date) "~B ~e, ~Y") ").")
+        ,(G_
+          `(p
+            (@ (class "limit-width centered-block"))
+            "GNU Guix provides " ,(number* (or total (length packages)))
+            " packages transparently "
+            ,(G_
+              `(a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
+                  "available as pre-built binaries"))
+            ". These pages provide a complete list of the packages.  Our "
+            ,(G_
+              `(a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
+                  "continuous integration system"))
+            " shows their current build status "
+            "(updated " ,(date->string (current-date)
+                                       (C_ "SRFI-19 date->string format"
+                                           "~B ~e, ~Y")) ")."))
 
        (div
         (@ (class "sheet sheet-padded justify-left"))
diff --git a/website/apps/packages/templates/detailed-package-list.scm 
b/website/apps/packages/templates/detailed-package-list.scm
index 73ebecf..1332c98 100644
--- a/website/apps/packages/templates/detailed-package-list.scm
+++ b/website/apps/packages/templates/detailed-package-list.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps packages templates components)
   #:export (detailed-package-list-t))
 
@@ -20,15 +21,17 @@
        (total-pages
         (number->string (context-datum context "total-pages"))))
     (theme
-     #:title (list (string-append "Page " page-number) letter "Packages")
+     #:title (list (G_ (string-append "Page " page-number ""))
+                   letter (C_ "webpage title" "Packages"))
      #:description
-     "List of packages available through GNU Guix."
+     (G_ "List of packages available through GNU Guix.")
      #:keywords
-     '("GNU" "Linux" "Unix" "Free software" "Libre software"
-       "Operating system" "GNU Hurd" "GNU Guix package manager"
-       "GNU Guile" "Guile Scheme" "Transactional upgrades"
-       "Functional package management" "Reproducibility")
-     #:active-menu-item "Packages"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Packages")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/item-preview.css")
@@ -37,11 +40,11 @@
      #:scripts
      (list (guix-url "static/packages/js/build-status.js"))
      #:crumbs
-     (list (crumb "Packages" (guix-url "packages/"))
+     (list (crumb (C_ "website menu" "Packages") (guix-url "packages/"))
           (crumb letter (guix-url (url-path-join "packages"
                                                  letter
                                                  "")))
-          (crumb (string-append "Page " page-number)
+           (crumb (G_ (string-append "Page " page-number ""))
                  (guix-url (url-path-join "packages"
                                           "page"
                                           page-number
@@ -50,7 +53,7 @@
      `(main
        (section
        (@ (class "page centered-text"))
-       (h2 "Packages — " ,letter
+        (h2 (G_ "Packages — ") ,letter
            ,(page-indicator (string->number page-number)
                             (string->number total-pages)))
 
diff --git a/website/apps/packages/templates/index.scm 
b/website/apps/packages/templates/index.scm
index d1044f7..7b73e0c 100644
--- a/website/apps/packages/templates/index.scm
+++ b/website/apps/packages/templates/index.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps packages templates components)
   #:use-module (srfi srfi-19)
   #:export (index-t))
@@ -18,37 +19,43 @@
   (let ((packages (context-datum context "packages"))
        (total    (context-datum context "total")))
     (theme
-     #:title (list "Packages")
+     #:title (C_ "webpage title" (list "Packages"))
      #:description
-     "List of packages available through GNU Guix."
+     (G_ "List of packages available through GNU Guix.")
      #:keywords
-     (list "GNU" "Linux" "Unix" "Free software" "Libre software"
-          "Operating system" "GNU Hurd" "GNU Guix package manager"
-          "GNU Guile" "Guile Scheme" "Transactional upgrades"
-          "Functional package management" "Reproducibility")
-     #:active-menu-item "Packages"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Packages")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/item-preview.css")
           (guix-url "static/packages/css/letter-selector.css"))
      #:crumbs
-     (list (crumb "Packages" (guix-url "packages/")))
+     (list (crumb (C_ "website menu" "Packages") (guix-url "packages/")))
      #:content
      `(main
        (section
        (@ (class "page centered-text"))
-       (h2 "Packages")
+        ,(G_ `(h2 "Packages"))
 
-       (p
-        (@ (class "limit-width centered-block"))
-        "GNU Guix provides " ,(number* total) " packages transparently "
-        (a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
-           "available as pre-built binaries")
-        ". These pages provide a complete list of the packages.  Our "
-        (a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
-           "continuous integration system")
-        " shows their current build status "
-        "(updated " ,(date->string (current-date) "~B ~e, ~Y") ").")
+        ,(G_
+          `(p
+            (@ (class "limit-width centered-block"))
+            "GNU Guix provides " ,(number* total) " packages transparently "
+            ,(G_
+              `(a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
+                  "available as pre-built binaries"))
+            ". These pages provide a complete list of the packages.  Our "
+            ,(G_
+              `(a (@ (href "https://ci.guix.gnu.org/jobset/guix-master";))
+                  "continuous integration system"))
+            " shows their current build status "
+            "(updated " ,(date->string (current-date)
+                                       (C_ "SRFI-19 date->string format"
+                                           "~B ~e, ~Y")) ")."))
 
        (div
         (@ (class "sheet"))
diff --git a/website/apps/packages/templates/package-list.scm 
b/website/apps/packages/templates/package-list.scm
index 694ecdd..b5873cc 100644
--- a/website/apps/packages/templates/package-list.scm
+++ b/website/apps/packages/templates/package-list.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps packages templates components)
   #:export (package-list-t))
 
@@ -20,25 +21,27 @@
        (total-pages
         (number->string (context-datum context "total-pages"))))
     (theme
-     #:title (list (string-append "Page " page-number) letter "Packages")
+     #:title (list (G_ (string-append "Page " page-number ""))
+                   letter (C_ "webpage title" "Packages"))
      #:description
      "List of packages available through GNU Guix."
      #:keywords
-     '("GNU" "Linux" "Unix" "Free software" "Libre software"
-       "Operating system" "GNU Hurd" "GNU Guix package manager"
-       "GNU Guile" "Guile Scheme" "Transactional upgrades"
-       "Functional package management" "Reproducibility")
-     #:active-menu-item "Packages"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Packages")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/base/css/item-preview.css")
           (guix-url "static/packages/css/letter-selector.css"))
      #:crumbs
-     (list (crumb "Packages" (guix-url "packages/"))
+     (list (crumb (C_ "website menu" "Packages") (guix-url "packages/"))
           (crumb letter (guix-url (url-path-join "packages"
                                                  letter
                                                  "")))
-          (crumb (string-append "Page " page-number)
+           (crumb (G_ (string-append "Page " page-number ""))
                  (guix-url (url-path-join "packages"
                                           "page"
                                           page-number
@@ -47,7 +50,7 @@
      `(main
        (section
        (@ (class "page centered-text"))
-       (h2 "Packages — " ,letter
+       (h2 (G_ "Packages — ") ,letter
            ,(page-indicator (string->number page-number)
                             (string->number total-pages)))
 
diff --git a/website/apps/packages/templates/package.scm 
b/website/apps/packages/templates/package.scm
index b618d79..aa3dcf0 100644
--- a/website/apps/packages/templates/package.scm
+++ b/website/apps/packages/templates/package.scm
@@ -8,6 +8,7 @@
   #:use-module (apps base templates theme)
   #:use-module (apps base types)
   #:use-module (apps base utils)
+  #:use-module (apps i18n)
   #:use-module (apps packages templates components)
   #:use-module (apps packages types)
   #:use-module (apps packages utils)
@@ -24,19 +25,20 @@
                                    (package-version package)))
         (lint-issues (package-lint-issues package)))
     (theme
-     #:title (list package-id "Packages")
+     #:title (C_ "webpage title" (list package-id "Packages"))
      #:description (package-synopsis-shtml package)
      #:keywords
-     '("GNU" "Linux" "Unix" "Free software" "Libre software"
-       "Operating system" "GNU Hurd" "GNU Guix package manager"
-       "GNU Guile" "Guile Scheme" "Transactional upgrades"
-       "Functional package management" "Reproducibility")
-     #:active-menu-item "Packages"
+     (string-split ;TRANSLATORS: |-separated list of webpage keywords
+      (G_ "GNU|Linux|Unix|Free software|Libre software|Operating \
+system|GNU Hurd|GNU Guix package manager|GNU Guile|Guile \
+Scheme|Transactional upgrades|Functional package \
+management|Reproducibility") #\|)
+     #:active-menu-item (C_ "website menu" "Packages")
      #:css
      (list (guix-url "static/base/css/page.css")
           (guix-url "static/packages/css/package.css"))
      #:crumbs
-     (list (crumb "Packages" (guix-url "packages/"))
+     (list (crumb (C_ "website menu" "Packages") (guix-url "packages/"))
           (crumb package-id
                  (guix-url (package-url-path package))))
      #:content
@@ -51,31 +53,35 @@
         ;; 'gnu-package?' might fetch stuff from the network.  Assume #f if
         ;; that doesn't work.
        (p ,(if (false-if-exception (gnu-package? package))
-                '(it "This is a GNU package.  ")
+                (G_ '(it "This is a GNU package.  "))
                 "")
            ,(package-description-shtml package))
 
        (ul
         (@ (class "package-info"))
-        (li (b "Website: ")
-            (a (@ (href ,(package-home-page package)))
-               ,(package-home-page package)))
-        (li (b "License: ")
-            ,(license->shtml (package-license package)))
-        (li (b "Package source: ")
-            ,(location->shtml (package-location package)))
-        (li (b "Patches: ")
-            ,(patches->shtml (package-patches package)))
-        (li (b "Builds: ")
-            ,(supported-systems->shtml package)))
+         ,(G_ `(li ,(G_ `(b "Website: "))
+                   (a (@ (href ,(package-home-page package)))
+                      ,(package-home-page package))))
+         ,(G_ `(li ,(G_ `(b "License: "))
+                   ,(license->shtml (package-license package))))
+         ,(G_ `(li ,(G_ `(b "Package source: "))
+                   ,(location->shtml (package-location package))))
+         ,(G_ `(li ,(G_ `(b "Patches: "))
+                   ,(patches->shtml (package-patches package))))
+         ,(G_ `(li ,(G_ `(b "Builds: "))
+                   ,(supported-systems->shtml package))))
 
        ;; Lint issues.
        ,(if (null? lint-issues)
             ""
-            `((h3 "Lint issues")
-              (p
-               ,(issue-count->shtml (length lint-issues)) ". "
-               "See " (a (@ (href "#")) "package definition")
-               " in Guix source code.")
+             (G_ `(,(G_ `(h3 "Lint issues"))
+                   ,(G_
+                     `(p
+                       ""
+                       ,(issue-count->shtml
+                         (length lint-issues))
+                       ". "
+                       "See " ,(G_ `(a (@ (href "#")) "package definition"))
+                       " in Guix source code."))
 
-              ,@(map lint-issue->shtml lint-issues))))))))
+                   ,@(map lint-issue->shtml lint-issues)))))))))
diff --git a/website/haunt.scm b/website/haunt.scm
index 0cb7177..01e2af7 100644
--- a/website/haunt.scm
+++ b/website/haunt.scm
@@ -5,22 +5,25 @@
 (use-modules ((apps base builder) #:prefix base:)
             ((apps blog builder) #:prefix blog:)
             ((apps download builder) #:prefix download:)
+             (apps i18n)
              ((apps media builder) #:prefix media:)
             ((apps packages builder) #:prefix packages:)
             (haunt asset)
              (haunt builder assets)
              (haunt reader)
             (haunt reader commonmark)
-             (haunt site))
-
+             (haunt site)
+             (ice-9 rdelim)
+             (srfi srfi-1))
 
 (site #:title "GNU Guix"
       #:domain "https://guix.gnu.org";
       #:build-directory "/tmp/gnu.org/software/guix"
       #:readers (list sxml-reader html-reader commonmark-reader)
-      #:builders (list base:builder
-                      blog:builder
-                      download:builder
-                       media:builder
-                      packages:builder
-                      (static-directory "static")))
+      #:builders (builders->localized-builders
+                  (list base:builder
+                        blog:builder
+                        download:builder
+                        media:builder
+                        packages:builder
+                        (static-directory "static"))))
diff --git a/website/i18n-howto.txt b/website/i18n-howto.txt
new file mode 100644
index 0000000..29b8852
--- /dev/null
+++ b/website/i18n-howto.txt
@@ -0,0 +1,86 @@
+With sexp-xgettext, arbitrary s-expressions can be marked for
+translation (not only strings like with normal xgettext).
+
+S-expressions can be marked with G_ (simple marking for translation),
+N_ (“complex” marking with different forms depending on number like
+ngettext), C_ (“complex” marking distinguished from other markings by
+a msgctxt like pgettext) or NC_ (mix of both).
+
+Marking a string for translation behaves like normal gettext.  Marking
+a parenthesized expression (i.e. a list or procedure call) extracts
+each string from the parenthesized expression.  If a symbol, keyword
+or other parenthesized expression occurs between the strings, it is
+extracted as an XML element.  Expressions before or after all strings
+are not extracted.  If strings from a parenthesized sub-expression
+shall be extracted too, the sub-expression must again be marked with
+G_ unless it is the only sub-expression or it follows a quote,
+unquote, quasiquote or unquote-splicing.  The order of XML elements
+can be changed in the translation to produce a different ordering
+inside a parenthesized expression.  If a string shall not be extracted
+from a marked expression, it must be wrapped, for example by a call to
+the identity procedure.  Be careful when marking non-SHTML content
+such as procedure calls for translation: Additional strings will be
+inserted between non-string elements.
+
+Known issues:
+
+* Line numbers are sometimes off.
+
+* Some less important other TODOs in the comments.
+
+=====
+
+The following commands are an example of the translation for locale
+de_DE.  Adapt as necessary.  We assume the software requirements
+mentioned in the README are installed.
+
+To create a pot file:
+
+guile scripts/sexp-xgettext.scm -f po/POTFILES \
+                                -o po/guix-website.pot \
+                                --from-code=UTF-8 \
+                                --copyright-holder="Ludovic Courtès" \
+                                --package-name="guix-website" \
+                                --msgid-bugs-address="ludo@gnu.org" \
+                                --keyword=G_ \
+                                --keyword=N_:1,2 \
+                                --keyword=C_:1c,2 \
+                                --keyword=NC_:1c,2,3
+
+To create a po file from a pot file, do the usual:
+
+cd po
+msginit -l de --no-translator
+
+To merge an existing po file with a new pot file:
+
+cd po
+msgmerge --previous -U de.po guix-website.pot
+
+To update mo files:
+
+mkdir -p de/LC_MESSAGES
+cd po
+msgfmt de.po
+cd ..
+mv po/messages.mo de/LC_MESSAGES/guix-website.mo
+
+To build all languages:
+
+guix build -f .guix.scm
+
+To test the de_DE translation, update its mo file as above, then:
+
+guix environment --ad-hoc haunt
+LC_ALL=de_DE.utf8 \
+ GUILE_LOAD_PATH=$(guix build 
guile-syntax-highlight)/share/guile/site/3.0:$GUILE_LOAD_PATH \
+ GUIX_WEB_SITE_LOCAL=yes \
+ haunt build
+GUILE_LOAD_PATH=$(guix build 
guile-syntax-highlight)/share/guile/site/3.0:$GUILE_LOAD_PATH \
+ haunt serve
+
+For checking for errors / debugging newly marked files you can try:
+
+GUILE_LOAD_PATH=.:$(guix build haunt)/share/guile/site/3.0:\
+$(guix build guile-syntax-highlight)/share/guile/site/3.0:$GUILE_LOAD_PATH \
+ guile apps/base/templates/about.scm   # an example for debugging about.scm
diff --git a/website/po/LINGUAS b/website/po/LINGUAS
new file mode 100644
index 0000000..d4dd759
--- /dev/null
+++ b/website/po/LINGUAS
@@ -0,0 +1,3 @@
+# Translation with sexp-xgettext requires the full LL_CC locale name
+# to be specified.
+en_US
diff --git a/website/po/POTFILES b/website/po/POTFILES
new file mode 100644
index 0000000..6f9f349
--- /dev/null
+++ b/website/po/POTFILES
@@ -0,0 +1,37 @@
+# high-priority files that should come first in the PO file
+apps/base/utils.scm
+apps/base/templates/home.scm
+apps/base/templates/theme.scm
+apps/base/templates/components.scm
+apps/base/templates/about.scm
+apps/base/data.scm
+apps/base/templates/help.scm
+# other files
+apps/base/templates/contact.scm
+apps/base/templates/contribute.scm
+apps/base/templates/donate.scm
+apps/base/templates/graphics.scm
+apps/base/templates/irc.scm
+apps/base/templates/menu.scm
+apps/base/templates/security.scm
+apps/blog/templates/components.scm
+apps/blog/templates/feed.scm
+apps/blog/templates/post-list.scm
+apps/blog/templates/post.scm
+apps/blog/templates/tag.scm
+apps/download/data.scm
+apps/download/templates/components.scm
+apps/download/templates/download.scm
+apps/download/templates/download-latest.scm
+apps/media/data.scm
+apps/media/templates/components.scm
+apps/media/templates/screenshot.scm
+apps/media/templates/screenshots-overview.scm
+apps/media/templates/video.scm
+apps/media/templates/video-list.scm
+apps/packages/templates/components.scm
+apps/packages/templates/detailed-index.scm
+apps/packages/templates/detailed-package-list.scm
+apps/packages/templates/index.scm
+apps/packages/templates/package-list.scm
+apps/packages/templates/package.scm
diff --git a/website/po/guix-website.pot b/website/po/guix-website.pot
new file mode 100644
index 0000000..cb57308
--- /dev/null
+++ b/website/po/guix-website.pot
@@ -0,0 +1,1403 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Ludovic Courtès
+# This file is distributed under the same license as the guix-website package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: guix-website\n"
+"Report-Msgid-Bugs-To: ludo@gnu.org\n"
+"POT-Creation-Date: 2020-07-10 20:18+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. TRANSLATORS: The locale’s display name; please include a country
+#. code like in English (US) *only* if there are multiple
+#. Translation Project teams for the same language.
+#: apps/base/utils.scm:74
+msgid "English (US)"
+msgstr ""
+
+#: apps/base/templates/home.scm:19
+msgctxt "webpage title"
+msgid "GNU's advanced distro and transactional package manager"
+msgstr ""
+
+#: apps/base/templates/home.scm:21
+msgid "Guix is an advanced distribution of the GNU operating system.\n   Guix 
is technology that respects the freedom of computer users.\n   You are free to 
run the system for any purpose, study how it\n   works, improve it, and share 
it with the whole world."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/home.scm:27 apps/base/templates/menu.scm:20 
apps/blog/templates/post-list.scm:29 apps/blog/templates/tag.scm:33 
apps/media/templates/screenshot.scm:24 
apps/media/templates/screenshots-overview.scm:21 
apps/packages/templates/detailed-index.scm:27 
apps/packages/templates/detailed-package-list.scm:30 
apps/packages/templates/index.scm:27 
apps/packages/templates/package-list.scm:30 
apps/packages/templates/package.scm:32
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|GNU Guile|Guile Scheme|Transactional 
upgrades|Functional package management|Reproducibility"
+msgstr ""
+
+#: apps/base/templates/home.scm:31 apps/base/templates/components.scm:343
+msgctxt "website menu"
+msgid "Overview"
+msgstr ""
+
+#: apps/base/templates/home.scm:41
+msgid "Summary"
+msgstr ""
+
+#: apps/base/templates/home.scm:43
+msgid "<1>Liberating.</1> Guix is an advanced distribution of the <2>GNU 
operating system</2> developed by the <3>GNU Project</3>—which respects the 
<4>freedom of computer users</4>. "
+msgstr ""
+
+#. TRANSLATORS: Package Management, Features and Using the
+#. Configuration System are section names in the English (en)
+#. manual.
+#: apps/base/templates/home.scm:64
+msgid "<1>Dependable.</1> Guix 
<2>supports<2.1>en</2.1><2.2>Package-Management.html</2.2></2> transactional 
upgrades and roll-backs, unprivileged package management, <3>and 
more<3.1>en</3.1><3.2>Features.html</3.2></3>.  When used as a standalone 
distribution, Guix supports <4>declarative system 
configuration<4.1>en</4.1><4.2>Using-the-Configuration-System.html</4.2></4> 
for transparent and reproducible operating systems."
+msgstr ""
+
+#. TRANSLATORS: Defining Packages and System Configuration are
+#. section names in the English (en) manual.
+#: apps/base/templates/home.scm:84
+msgid "<1>Hackable.</1> It provides <2>Guile Scheme</2> APIs, including 
high-level embedded domain-specific languages (EDSLs) to <3>define 
packages<3.1>en</3.1><3.2>Defining-Packages.html</3.2></3> and <4>whole-system 
configurations<4.1>en</4.1><4.2>System-Configuration.html</4.2></4>."
+msgstr ""
+
+#: apps/base/templates/home.scm:106
+msgctxt "button"
+msgid "DOWNLOAD v<1/>"
+msgstr ""
+
+#: apps/base/templates/home.scm:111
+msgctxt "button"
+msgid "CONTRIBUTE"
+msgstr ""
+
+#: apps/base/templates/home.scm:118
+msgid "Discover Guix"
+msgstr ""
+
+#: apps/base/templates/home.scm:120
+msgid "Guix comes with thousands of packages which include applications, 
system tools, documentation, fonts, and other digital goods readily available 
for installing with the <1>GNU Guix</1> package manager."
+msgstr ""
+
+#: apps/base/templates/home.scm:132
+msgid "Instructional videos"
+msgstr ""
+
+#: apps/base/templates/home.scm:146
+msgctxt "button"
+msgid "ALL PACKAGES"
+msgstr ""
+
+#: apps/base/templates/home.scm:153
+msgid "GNU Guix in your field"
+msgstr ""
+
+#: apps/base/templates/home.scm:155
+msgid "Read some stories about how people are using GNU Guix in\ntheir daily 
lives."
+msgstr ""
+
+#: apps/base/templates/home.scm:166
+msgctxt "button"
+msgid "SOFTWARE DEVELOPMENT"
+msgstr ""
+
+#: apps/base/templates/home.scm:171
+msgctxt "button"
+msgid "BIOINFORMATICS"
+msgstr ""
+
+#: apps/base/templates/home.scm:176
+msgctxt "button"
+msgid "HIGH PERFORMANCE COMPUTING"
+msgstr ""
+
+#: apps/base/templates/home.scm:181
+msgctxt "button"
+msgid "RESEARCH"
+msgstr ""
+
+#: apps/base/templates/home.scm:186
+msgctxt "button"
+msgid "ALL FIELDS..."
+msgstr ""
+
+#: apps/base/templates/home.scm:193
+msgid "GNU Guix in other GNU/Linux distros"
+msgstr ""
+
+#: apps/base/templates/home.scm:204
+msgid "Video: <1>Demo of Guix in another GNU/Linux 
distribution<1.1/>https://audio-video.gnu.org/video/misc/2016-07__GNU_Guix_Demo_2.webm</1>
 (1 minute, 30 seconds)."
+msgstr ""
+
+#: apps/base/templates/home.scm:215
+msgid "If you don't use GNU Guix as a standalone GNU/Linux distribution, you 
still can use it as a package manager on top of any GNU/Linux distribution. 
This way, you can benefit from all its conveniences."
+msgstr ""
+
+#: apps/base/templates/home.scm:220
+msgid "Guix won't interfere with the package manager that comes with your 
distribution. They can live together."
+msgstr ""
+
+#: apps/base/templates/home.scm:227
+msgctxt "button"
+msgid "TRY IT OUT!"
+msgstr ""
+
+#: apps/base/templates/home.scm:234 apps/blog/templates/post-list.scm:49
+msgid "Blog"
+msgstr ""
+
+#: apps/base/templates/home.scm:241
+msgctxt "button"
+msgid "ALL POSTS"
+msgstr ""
+
+#: apps/base/templates/home.scm:247 apps/base/templates/contact.scm:36
+msgid "Contact"
+msgstr ""
+
+#: apps/base/templates/home.scm:254
+msgctxt "button"
+msgid "ALL CONTACT MEDIA"
+msgstr ""
+
+#: apps/base/templates/theme.scm:17 apps/base/templates/components.scm:367 
apps/base/templates/about.scm:27 apps/base/templates/about.scm:30 
apps/base/templates/contact.scm:26 apps/base/templates/contribute.scm:26 
apps/base/templates/graphics.scm:24 apps/base/templates/security.scm:28
+msgctxt "website menu"
+msgid "About"
+msgstr ""
+
+#: apps/base/templates/theme.scm:74 apps/base/templates/theme.scm:76
+msgctxt "webpage title"
+msgid "GNU Guix"
+msgstr ""
+
+#: apps/base/templates/theme.scm:98
+msgctxt "webpage title"
+msgid "GNU Guix — Activity Feed"
+msgstr ""
+
+#: apps/base/templates/theme.scm:115
+msgid "Made with <1>♥</1> by humans and powered by <2>GNU Guile</2>.  
<3>Source code</3> under the <4>GNU AGPL</4>."
+msgstr ""
+
+#: apps/base/templates/components.scm:47
+msgid "Your location:"
+msgstr ""
+
+#: apps/base/templates/components.scm:49
+msgid "Home"
+msgstr ""
+
+#: apps/base/templates/components.scm:153
+msgid "archive"
+msgstr ""
+
+#: apps/base/templates/components.scm:337
+msgctxt "website menu"
+msgid "Guix"
+msgstr ""
+
+#: apps/base/templates/components.scm:341
+msgid "website menu:"
+msgstr ""
+
+#: apps/base/templates/components.scm:345 
apps/download/templates/download.scm:43 apps/download/templates/download.scm:47 
apps/download/templates/download-latest.scm:125 
apps/download/templates/download-latest.scm:129
+msgctxt "website menu"
+msgid "Download"
+msgstr ""
+
+#: apps/base/templates/components.scm:351
+msgctxt "website menu"
+msgid "Stable"
+msgstr ""
+
+#: apps/base/templates/components.scm:354 
apps/download/templates/download-latest.scm:130
+msgctxt "website menu"
+msgid "Latest"
+msgstr ""
+
+#: apps/base/templates/components.scm:355 
apps/packages/templates/detailed-index.scm:31 
apps/packages/templates/detailed-index.scm:38 
apps/packages/templates/detailed-package-list.scm:34 
apps/packages/templates/detailed-package-list.scm:43 
apps/packages/templates/index.scm:31 apps/packages/templates/index.scm:37 
apps/packages/templates/package-list.scm:34 
apps/packages/templates/package-list.scm:40 
apps/packages/templates/package.scm:36 apps/packages/templates/package.scm:41
+msgctxt "website menu"
+msgid "Packages"
+msgstr ""
+
+#: apps/base/templates/components.scm:356 apps/blog/templates/post-list.scm:33 
apps/blog/templates/post-list.scm:39 apps/blog/templates/post.scm:29 
apps/blog/templates/post.scm:35 apps/blog/templates/tag.scm:37 
apps/blog/templates/tag.scm:43
+msgctxt "website menu"
+msgid "Blog"
+msgstr ""
+
+#: apps/base/templates/components.scm:358 
apps/media/templates/screenshot.scm:28 
apps/media/templates/screenshots-overview.scm:25 
apps/media/templates/video.scm:29 apps/media/templates/video-list.scm:29
+msgctxt "website menu"
+msgid "Media"
+msgstr ""
+
+#: apps/base/templates/components.scm:361 apps/media/templates/video.scm:33 
apps/media/templates/video-list.scm:33
+msgctxt "website menu"
+msgid "Videos"
+msgstr ""
+
+#: apps/base/templates/components.scm:362
+msgctxt "website menu"
+msgid "Screenshots"
+msgstr ""
+
+#: apps/base/templates/components.scm:364 apps/base/templates/help.scm:26 
apps/base/templates/help.scm:30
+msgctxt "website menu"
+msgid "Help"
+msgstr ""
+
+#: apps/base/templates/components.scm:365 apps/base/templates/donate.scm:26 
apps/base/templates/donate.scm:29
+msgctxt "website menu"
+msgid "Donate"
+msgstr ""
+
+#: apps/base/templates/components.scm:370 apps/base/templates/contact.scm:31
+msgctxt "website menu"
+msgid "Contact"
+msgstr ""
+
+#: apps/base/templates/components.scm:371 apps/base/templates/contribute.scm:30
+msgctxt "website menu"
+msgid "Contribute"
+msgstr ""
+
+#: apps/base/templates/components.scm:372 apps/base/templates/security.scm:31
+msgctxt "website menu"
+msgid "Security"
+msgstr ""
+
+#: apps/base/templates/components.scm:373 apps/base/templates/graphics.scm:27
+msgctxt "website menu"
+msgid "Graphics"
+msgstr ""
+
+#: apps/base/templates/components.scm:391
+msgid " (Page <1/> of <2/>)"
+msgstr ""
+
+#: apps/base/templates/components.scm:415
+msgid "Page <1/> of <2/>. Go to another page: "
+msgstr ""
+
+#: apps/base/templates/about.scm:17
+msgctxt "webpage title"
+msgid "About"
+msgstr ""
+
+#: apps/base/templates/about.scm:19
+msgid "Guix is an advanced distribution of the GNU operating system.\n    Guix 
is technology that respects the freedom of computer users.\n    You are free to 
run the system for any purpose, study how it\n    works, improve it, and share 
it with the whole world."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/about.scm:25
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager"
+msgstr ""
+
+#: apps/base/templates/about.scm:35
+msgid "About the Project"
+msgstr ""
+
+#: apps/base/templates/about.scm:37
+msgid "The <1>GNU Guix</1> package and system manager is a <2>free 
software</2> project developed by volunteers around the world under the\n       
     umbrella of the <3>GNU Project</3>. "
+msgstr ""
+
+#: apps/base/templates/about.scm:46
+msgid "Guix System is an advanced distribution of the <1>GNU operating 
system</1>.  It uses the <2>Linux-libre</2> kernel, and support for <3>the 
Hurd</3> is being worked on.  As a GNU distribution, it is committed\n          
  to respecting and enhancing <4>the freedom of its users</4>.  As such, it 
adheres to the <5>GNU Free System Distribution Guidelines</5>."
+msgstr ""
+
+#. TRANSLATORS: Features and Defining Packages are section names
+#. in the English (en) manual.
+#: apps/base/templates/about.scm:64
+msgid "GNU Guix provides <1>state-of-the-art package management 
features<1.1>en</1.1><1.2>Features.html</1.2></1> such as transactional 
upgrades and roll-backs, reproducible\n            build environments, 
unprivileged package management, and\n            per-user profiles.  It uses 
low-level mechanisms from the <2>Nix</2> package manager, but packages are 
<3>defined<3.1>en</3.1><3.2>Defining-Packages.html</3.2></3> as native 
<4>Guile</4> modules, using extensions to the <5>Scheme</5> l [...]
+msgstr ""
+
+#. TRANSLATORS: Using the Configuration System, Initial RAM Disk
+#. and Defining Services are section names in the English (en)
+#. manual.
+#: apps/base/templates/about.scm:85
+msgid "Guix takes that a step further by additionally supporting stateless,\n  
         reproducible <1>operating system 
configurations<1.1>en</1.1><1.2>Using-the-Configuration-System.html</1.2></1>. 
This time the whole system is hackable in Scheme, from the <2>initial RAM 
disk<2.1>en</2.1><2.2>Initial-RAM-Disk.html</2.2></2> to the <3>initialization 
system</3>, and to the <4>system 
services<4.1>en</4.1><4.2>Defining-Services.html</4.2></4>."
+msgstr ""
+
+#: apps/base/templates/about.scm:106
+msgid "Maintainers"
+msgstr ""
+
+#: apps/base/templates/about.scm:108
+msgid "Guix is currently maintained by Ludovic Courtès, Marius Bakke, 
Maxim\nCournoyer, Tobias Geerinckx-Rice and Mathieu Othacehe.  Please use the 
<1>mailing lists</1> for contact.  For sensitive issues, you can reach them 
using the <2/> private alias."
+msgstr ""
+
+#: apps/base/templates/about.scm:117
+msgid "Licensing"
+msgstr ""
+
+#: apps/base/templates/about.scm:119
+msgid "Guix is free software; you can redistribute it and/or modify\n          
it under the terms of the <1>GNU General Public License</1> as published by the 
Free Software Foundation; either\n          version 3 of the License, or (at 
your option) any later\n          version. "
+msgstr ""
+
+#: apps/base/data.scm:24
+msgid "IRC Channel"
+msgstr ""
+
+#: apps/base/data.scm:26
+msgid "Join the <1/> channel on the Freenode IRC network to chat\n       with 
the community about GNU Guix or to get help in\n       real-time."
+msgstr ""
+
+#: apps/base/data.scm:35
+msgid "Info Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:37
+msgid "Subscribe to the <1/> low-traffic mailing\nlist to receive important 
announcements sent by the project maintainers (in\nEnglish)."
+msgstr ""
+
+#: apps/base/data.scm:46
+msgid "Help Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:51
+msgid "Subscribe to the Help mailing list to get support\nfrom the GNU Guix 
community via email.  You can post messages in English\nthough we also accept 
other languages."
+msgstr ""
+
+#: apps/base/data.scm:55
+msgctxt "unique lingua code like en or zh-cn"
+msgid "en"
+msgstr ""
+
+#: apps/base/data.scm:126
+msgid "Bug Reporting"
+msgstr ""
+
+#: apps/base/data.scm:128
+msgid "If you found a bug in Guix, check whether the bug is\n       already in 
the <1>bug database</1>. If it is not, please <2>report it.</2>"
+msgstr ""
+
+#: apps/base/data.scm:140
+msgid "Development Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:142
+msgid "Discussion about the development of GNU Guix. <1> Until July 2013</1>, 
the bug-Guix mailing list filled that role. "
+msgstr ""
+
+#: apps/base/data.scm:152
+msgid "Patches Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:154
+msgid "Submission of patches.  Every message sent to this mailing list\n       
leads to a new entry in our <1>patch tracking tool</1>.  See <2>this page</2> 
for more information on how to use it; see <3>the 
manual<3.1>en</3.1><3.2>Submitting-Patches.html</3.2></3> for more information 
on how to submit a patch.  <4>Until February 2017</4>, the guix-devel mailing 
list filled that role."
+msgstr ""
+
+#: apps/base/data.scm:173
+msgid "Commits Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:175
+msgid "Notifications of commits made to the <1>Git repositories</1>."
+msgstr ""
+
+#: apps/base/data.scm:184
+msgid "Security Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:186
+msgid "This is a private mailing list that anyone can post to to <1>report 
security issues</1> in Guix itself or in the <2>packages</2> it provides.  
Posting here allows Guix developers to address\n       the problem before it is 
widely publicized."
+msgstr ""
+
+#: apps/base/data.scm:198
+msgid "Sysadmin Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:200
+msgid "Private mailing list for the <1>build farm</1> system administration."
+msgstr ""
+
+#: apps/base/data.scm:212
+msgid "GNU System Discuss Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:214
+msgid "Discussion about the development of the broader GNU system."
+msgstr ""
+
+#: apps/base/data.scm:219
+msgid "GNU/Linux-libre Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:221
+msgid "Workgroup for fully free GNU/Linux distributions."
+msgstr ""
+
+#: apps/base/data.scm:226
+msgid "GNU Info Mailing List"
+msgstr ""
+
+#: apps/base/data.scm:228
+msgid "GNU software announcements."
+msgstr ""
+
+#: apps/base/templates/help.scm:17
+msgctxt "webpage title"
+msgid "Help"
+msgstr ""
+
+#: apps/base/templates/help.scm:19
+msgid "A list of resources about how to use GNU Guix, plus\n   information 
about getting help from the community of users and\n   developers."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/help.scm:24
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Help resources"
+msgstr ""
+
+#: apps/base/templates/help.scm:35
+msgid "Help"
+msgstr ""
+
+#: apps/base/templates/help.scm:46
+msgid "GNU Guix Manual"
+msgstr ""
+
+#: apps/base/templates/help.scm:47
+msgid "Documentation for GNU Guix is available\n            online.  You may 
also find more information about Guix by running <1>info guix</1>."
+msgstr ""
+
+#: apps/base/templates/help.scm:53
+msgid "Read Guix manual"
+msgstr ""
+
+#: apps/base/templates/help.scm:64
+msgid "Get Guix reference card"
+msgstr ""
+
+#: apps/base/templates/help.scm:72
+msgid "Videos"
+msgstr ""
+
+#: apps/base/templates/help.scm:73
+msgid "The collection of videos includes instructional material\n            
to help you get started with every day use of GNU Guix as\n            well as 
other topics that present advanced features of the\n            system."
+msgstr ""
+
+#: apps/base/templates/help.scm:81
+msgid "Browse all videos"
+msgstr ""
+
+#: apps/base/templates/help.scm:89
+msgid "Cookbook"
+msgstr ""
+
+#: apps/base/templates/help.scm:90
+msgid "Tutorials, how-to guides and examples contributed by the\n            
Guix community which show you how to use the system and its\n            
collection of packages to achieve common and not-so-common\n            goals 
users may have."
+msgstr ""
+
+#: apps/base/templates/help.scm:98
+msgid "Browse the recipes"
+msgstr ""
+
+#: apps/base/templates/help.scm:106
+msgid "GNU Manuals"
+msgstr ""
+
+#: apps/base/templates/help.scm:107
+msgid "Guix is a distribution of the <1>GNU operating system</1>.  
Documentation for GNU packages is\n            available online in various 
formats. "
+msgstr ""
+
+#: apps/base/templates/help.scm:115
+msgid "Browse GNU manuals"
+msgstr ""
+
+#: apps/base/templates/help.scm:123
+msgid "IRC Chat"
+msgstr ""
+
+#: apps/base/templates/help.scm:124
+msgid "For real-time support from the community, you can connect\n            
to the <1/> channel on irc.freenode.net. There\n            you can get help 
about anything related to GNU Guix."
+msgstr ""
+
+#: apps/base/templates/help.scm:129
+msgid "The <1/> channel is logged. Previous\n            conversations can be 
browsed online. See the <2>channel logs</2>. "
+msgstr ""
+
+#: apps/base/templates/help.scm:136
+msgid "Connect"
+msgstr ""
+
+#: apps/base/templates/help.scm:144
+msgid "Mailing lists"
+msgstr ""
+
+#: apps/base/templates/help.scm:145
+msgid "Email support from the community is also available through\n            
several mailing list. The messages sent to the lists are\n            public 
and archived online."
+msgstr ""
+
+#: apps/base/templates/help.scm:153
+msgid "See all lists"
+msgstr ""
+
+#: apps/base/templates/contact.scm:17 apps/base/templates/irc.scm:18 
apps/base/templates/irc.scm:29
+msgctxt "webpage title"
+msgid "Contact"
+msgstr ""
+
+#: apps/base/templates/contact.scm:19
+msgid "A list of channels to communicate with GNU Guix users\n   and 
developers about anything you want."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/contact.scm:23
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Community|Mailing lists|IRC channels|Bug 
reports|Help"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:17
+msgctxt "webpage title"
+msgid "Contribute"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:19
+msgid "Check all the ways you can contribute to make GNU Guix\n   better, and 
join the world-wide community of volunteers."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/contribute.scm:23
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package 
manager|Volunteer|Development|Translation|I18N|L10N|Artwork"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:35 
apps/base/templates/contribute.scm:110 apps/base/templates/contribute.scm:227
+msgid "Contribute"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:37
+msgid "GNU Guix is a large project developed\n           mostly by volunteers 
from all around the world. You are welcome\n           to join us in the 
<1>development mailing list</1> or in the <2>#guix channel</2> in IRC Freenode. 
Tell us how would you like to help, and we\n          will do our best to guide 
you. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:51
+msgid "We want to provide a warm, friendly, and harassment-free environment,\n 
          so that anyone can contribute to the best of their abilities.  To\n   
        this end our project uses a “Contributor Covenant”, which was adapted\n 
          from <1>https://contributor-covenant.org/</1>.  You can find the full 
pledge in the <2>CODE-OF-CONDUCT</2> file."
+msgstr ""
+
+#: apps/base/templates/contribute.scm:72
+msgid "Project Management"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:73
+msgid "We use <1>Savannah</1> as the central point for development, 
maintenance and\n            distribution of the Guix System Distribution and 
GNU Guix."
+msgstr ""
+
+#: apps/base/templates/contribute.scm:79
+msgid "The source files for all the components of the project,\n            
including software, web site, documentation, and artwork, are\n            
available in <1>Git repositories</1> at Savannah. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:89
+msgid "Access Savannah"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:94
+msgid "Art"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:95
+msgid "We are always looking for artists to help us design and\n            
improve user interfaces, and create multimedia material for\n            
documentation, presentations, and promotional items. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:100
+msgid "The artwork used in the different components of the project\n           
 is available in the <1>guix-artwork</1> repository. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:116
+msgid "Documentation"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:117
+msgid "You can read the <1>project documentation</1> already available in the 
system and in the website, and\n            help us identify any errors or 
omissions. Creating new\n            manuals, tutorials, and blog entries will 
also help users and\n            developers discover what we do. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:125
+msgid "Helping improve the documentation of the <1>packaged software</1> is 
another way to contribute. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:132
+msgid "Start writing"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:138 
apps/packages/templates/detailed-index.scm:43 
apps/packages/templates/index.scm:42
+msgid "Packages"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:139
+msgid "Hundreds of software, documentation, and assets need to be\n            
packaged to make it easier for users to install their\n            favorite 
tools with the Guix package manager, and be\n            productive using the 
system. "
+msgstr ""
+
+#. TRANSLATORS: Packaging Guidelines is a section name in the
+#. English (en) manual.
+#: apps/base/templates/contribute.scm:147
+msgid "Information on how to add packages to the distribution can\n            
be found <1>in the manual<1.1>en</1.1><1.2>Packaging-Guidelines.html</1.2></1>. 
"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:157
+msgid "Check out the <1>package database</1> for a list of available packages, 
and the <2>patch-tracking database</2> for a list of pending submissions."
+msgstr ""
+
+#: apps/base/templates/contribute.scm:168
+msgid "Send a new package"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:174
+msgid "Programming"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:175
+msgid "Source code is in the <1>main Git repository</1>.  We use <2>GNU 
Guile</2> as the main programming and extension language for the\n            
components of the system. "
+msgstr ""
+
+#. TRANSLATORS: Contributing is a section name in the English
+#. (en) manual.
+#: apps/base/templates/contribute.scm:187
+msgid "You will find it useful to browse the <1>Guile manual</1> or other 
<2>introductory material about Scheme</2>. Also, make sure to read the 
<3>Contributing<3.1>en</3.1><3.2>Contributing.html</3.2></3> section of the 
manual for more details on the development\n            setup, as well as the 
coding and cooperation conventions used\n            in the project. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:205
+msgid "Send a patch"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:211
+msgid "System Administration"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:212
+msgid "Our system infrastructure makes it possible for all the\n            
contributors to communicate and collaborate in the project,\n            and 
users to be able to download and install packages. Help\n            us keep 
the system up and running smoothly. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:218
+msgid "You can also <1>donate hardware or hosting</1> for our <2>build 
farm</2>.  "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:233
+msgid "Test and Bug Reports"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:234
+msgid "Install the software and send feedback to the community\n            
about your experience. Help the project by reporting bugs. You can also get 
started by <1>picking an “easy” bug</1> to work on."
+msgstr ""
+
+#: apps/base/templates/contribute.scm:243
+msgid "Before reporting a bug, please check whether the bug is\n            
already <1>in the bug database</1>. See <2>the developer information page</2> 
for more information on how to manipulate bug reports. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:255
+msgid "Report a bug"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:261
+msgid "Translation"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:262
+msgid "You can help translate the <1>software</1>, the <2>package 
descriptions</2>, and the <3>manual</3> into your language.  See the 
<4>Translation Project</4> for information on how you can help."
+msgstr ""
+
+#: apps/base/templates/contribute.scm:281
+msgid "<1>Software packages</1> provided by the system may have their own 
translation\n            tools.  Visit their websites and help translate. "
+msgstr ""
+
+#: apps/base/templates/contribute.scm:288
+msgid "Start translating"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:292
+msgid "Other resources for contributors"
+msgstr ""
+
+#: apps/base/templates/contribute.scm:293
+msgid "Documents, supporting material of previous talks, and\n          
auxiliary information useful to hackers and maintainers is\n          available 
at <1/>."
+msgstr ""
+
+#: apps/base/templates/donate.scm:17
+msgctxt "webpage title"
+msgid "Donate"
+msgstr ""
+
+#: apps/base/templates/donate.scm:19
+msgid "We are looking for donations of hardware and optionally\n   hosting for 
machines (they should be usable with exclusively\n   free software)."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/donate.scm:24
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Donations"
+msgstr ""
+
+#: apps/base/templates/donate.scm:34
+msgid "Donate"
+msgstr ""
+
+#: apps/base/templates/donate.scm:36
+msgid "The <1>build farm</1> of Guix runs on donated hardware and hosting. As 
the distribution grows (see the <2>package list</2>), so do the computing and 
storage needs."
+msgstr ""
+
+#: apps/base/templates/donate.scm:47
+msgid "Back in 2015 we <1>ran a fundraising campaign</1> to strengthen our 
build farm, with <2>support from the Free Software Foundation (FSF)</2>.  The 
Guix project can always use financial support to further its mission.  Please 
consider helping out by making a donation on this\n          FSF-hosted page:"
+msgstr ""
+
+#: apps/base/templates/donate.scm:63
+msgctxt "button"
+msgid "♥ DONATE!"
+msgstr ""
+
+#: apps/base/templates/donate.scm:66
+msgid "Hardware and Hosting"
+msgstr ""
+
+#: apps/base/templates/donate.scm:71
+msgid "We are also looking for donations of hardware and optionally\n          
 hosting for the following kinds of machines (they should be\n           usable 
with exclusively free software): "
+msgstr ""
+
+#: apps/base/templates/donate.scm:78
+msgid "x86_64 machines, with on the order of 1 TiB of storage\n               
and 4 GiB of RAM;"
+msgstr ""
+
+#: apps/base/templates/donate.scm:81
+msgid "armv7 machines (such as the Novena) to more quickly test\n              
 and provide binaries for the armhf-linux port;"
+msgstr ""
+
+#: apps/base/templates/donate.scm:84
+msgid "mips64el machines to strengthen this port."
+msgstr ""
+
+#: apps/base/templates/donate.scm:87
+msgid "Please get in touch with us through the <1>usual channels</1> or using 
the <2/> private alias to\n           discuss any opportunities. "
+msgstr ""
+
+#: apps/base/templates/donate.scm:95
+msgid "Thanks to the donors!"
+msgstr ""
+
+#: apps/base/templates/donate.scm:100
+msgid "The table below summarizes hardware and hosting donations that\n        
   make the <1>build farm</1> for the Guix System Distribution a reality."
+msgstr ""
+
+#: apps/base/templates/donate.scm:110
+msgid "<1>machine</1><2>system</2><3>donors</3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:115
+msgid "<1>berlin.guixsd.org</1><2>build farm with 25 build nodes for 
x86_64-linux and\ni686-linux, and dedicated storage</2><3><3.1>Max Delbrück 
Center for Molecular Medicine</3.1> (hardware and hosting)</3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:127
+msgid "<1>overdrive1.guixsd.org</1><2>aarch64-linux</2><3><3.1>ARM 
Holdings</3.1></3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:137
+msgid "<1>bayfront.guixsd.org</1><2>new build farm front-end 
(WIP)</2><3>Igalia</3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:149
+msgid "<1>guix-x15.sjd.se, guix-x15b.sjd.se</1><2>armhf-linux</2><3>Simon 
Josefsson</3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:159
+msgid "<1>hydra-slave1</1><2>armhf-linux</2><3><3.1>Steve Sprang 
(hardware)</3.1><3.2>Mark H Weaver (hosting)</3.2></3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:169
+msgid "<1>hydra-slave2</1><2>armhf-linux</2><3><3.1><3.1.1>Harmon 
Instruments</3.1.1> (hardware)</3.1><3.2>Mark H Weaver (hosting)</3.2></3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:182
+msgid "<1>hydra-slave3</1><2>armhf-linux</2><3><3.1><3.1.1>Kosagi (Sutajio 
Ko-Usagi Pte Ltd)</3.1.1> (hardware)</3.1><3.2>Mark H Weaver 
(hosting)</3.2></3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:195
+msgid "<1>redhill</1><2>armhf-linux</2><3><3.1><3.1.1>Kosagi (Sutajio Ko-Usagi 
Pte Ltd)</3.1.1> (hardware)</3.1><3.2>Andreas Enge (hosting)</3.2></3>"
+msgstr ""
+
+#: apps/base/templates/donate.scm:209
+msgid "Other organizations and individuals helped Guix with hardware 
and\nhosting in the past and we thank them: <1>Free Software Foundation</1>, 
<2>GNU España</2>, <3>FSF France</3>, <4>Free Secure Network Systems Group</4> 
at the <5>Technische Universität München</5>."
+msgstr ""
+
+#: apps/base/templates/graphics.scm:16
+msgctxt "webpage title"
+msgid "Graphics"
+msgstr ""
+
+#: apps/base/templates/graphics.scm:18
+msgid "Information about images used for the graphical identity\n   of GNU 
Guix and Guix System (formerly “GuixSD”)."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/graphics.scm:22
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Donations|Branding|Logo"
+msgstr ""
+
+#: apps/base/templates/graphics.scm:32
+msgid "Graphics"
+msgstr ""
+
+#: apps/base/templates/graphics.scm:34
+msgid "For questions regarding the graphics listed in this page,\n          
please contact <1>help-guix@gnu.org</1>."
+msgstr ""
+
+#: apps/base/templates/graphics.scm:44
+msgid "GNU Guix logotype"
+msgstr ""
+
+#: apps/base/templates/graphics.scm:45
+msgid "The standalone Guix, formerly known as the “Guix System\n          
Distribution” or GuixSD, had its own logo, which is now\n          deprecated."
+msgstr ""
+
+#: apps/base/templates/graphics.scm:51
+msgid "The GNU Guix and GuixSD\n          logotypes were designed by Luis 
Felipe López Acevedo\n          (a.k.a. sirgazil).  They are available under 
the following\n          terms:"
+msgstr ""
+
+#: apps/base/templates/graphics.scm:65
+msgid "The source files (SVG) for these logotypes, their variants, and\n       
   other artwork used in the different components of the GNU Guix\n          
project are available in the <1>guix-artwork</1> repository, including the 
previous GNU Guix logotype designed\n          by Nikita Karetnikov in 2013 and 
<2>superseded</2> by the golden GNU in 2016."
+msgstr ""
+
+#: apps/base/templates/irc.scm:17 apps/base/templates/irc.scm:30
+msgctxt "webpage title"
+msgid "IRC"
+msgstr ""
+
+#: apps/base/templates/irc.scm:20
+msgid "Internet relay chat."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/irc.scm:23
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|IRC|chat"
+msgstr ""
+
+#: apps/base/templates/irc.scm:35
+msgid "IRC"
+msgstr ""
+
+#: apps/base/templates/irc.scm:37
+msgid "Join the <1/> channel on the <2>Freenode IRC network</2> to chat with 
the GNU Guix community or to get help\n          in real-time. You can use the 
chat widget below, or just use\n          the <3>IRC client</3> of your 
preference. Note that the conversations that happen\n          on the <4/> 
channel are logged (<5>browse the log</5>)."
+msgstr ""
+
+#: apps/base/templates/menu.scm:16
+msgctxt "webpage title"
+msgid "Menu"
+msgstr ""
+
+#: apps/base/templates/menu.scm:17
+msgid "Website menu."
+msgstr ""
+
+#: apps/base/templates/menu.scm:24
+msgctxt "website menu"
+msgid "Menu"
+msgstr ""
+
+#: apps/base/templates/security.scm:19
+msgctxt "webpage title"
+msgid "Security"
+msgstr ""
+
+#: apps/base/templates/security.scm:21
+msgid "Important information about getting security updates\n   for your GNU 
Guix installation, and instructions on how\n   to report security issues."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/base/templates/security.scm:26
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Security updates"
+msgstr ""
+
+#: apps/base/templates/security.scm:36
+msgid "Security"
+msgstr ""
+
+#: apps/base/templates/security.scm:38
+msgid "How to report security issues"
+msgstr ""
+
+#: apps/base/templates/security.scm:39
+msgid "To report sensitive security issues in Guix itself or the\n           
packages it provides, you can write to the private mailing list <1/>.  This 
list is monitored by a\n           small team of Guix developers."
+msgstr ""
+
+#: apps/base/templates/security.scm:46
+msgid "If you prefer to send your report using OpenPGP encrypted email,\n      
     please send it to one of the following Guix developers using their\n       
    respective OpenPGP key:"
+msgstr ""
+
+#: apps/base/templates/security.scm:65
+msgid "Release signatures"
+msgstr ""
+
+#: apps/base/templates/security.scm:66
+msgid "Releases of Guix are signed using the OpenPGP key with the fingerprint 
<1/>.  Users should 
<2>verify<2.1>en</2.1><2.2>Binary-Installation.html</2.2></2> their downloads 
before extracting or running them."
+msgstr ""
+
+#: apps/base/templates/security.scm:78
+msgid "Security updates"
+msgstr ""
+
+#: apps/base/templates/security.scm:79
+msgid "When security vulnerabilities are found in Guix or the packages 
provided by Guix, we will provide <1>security 
updates<1.1>en</1.1><1.2>Security-Updates.html</1.2></1> quickly and with 
minimal disruption for users.  When appropriate, a security advisory is 
published on the blog with the <2>Security Advisory tag</2> and on the 
<3><3.1>info-guix</3.1> mailing list</3>; <4/> may also display the advisory."
+msgstr ""
+
+#: apps/base/templates/security.scm:94
+msgid "Guix uses a “rolling release” model.  All security bug-fixes are pushed 
directly to the master branch.  There is no “stable” branch that only receives 
security fixes."
+msgstr ""
+
+#: apps/blog/templates/components.scm:33 apps/blog/templates/post.scm:46
+msgctxt "SRFI-19 date->string format"
+msgid "~B ~e, ~Y"
+msgstr ""
+
+#: apps/blog/templates/components.scm:37
+msgctxt "blog post summary ellipsis"
+msgid "…"
+msgstr ""
+
+#: apps/blog/templates/components.scm:48
+msgid "Blog menu: "
+msgstr ""
+
+#: apps/blog/templates/components.scm:53
+msgid "Get topic updates"
+msgstr ""
+
+#: apps/blog/templates/components.scm:54
+msgid "Get blog updates"
+msgstr ""
+
+#: apps/blog/templates/components.scm:66
+msgctxt "button"
+msgid "Atom feed"
+msgstr ""
+
+#: apps/blog/templates/components.scm:68
+msgid "Posts by topic"
+msgstr ""
+
+#: apps/blog/templates/feed.scm:32
+msgctxt "feed author name"
+msgid "GNU Guix"
+msgstr ""
+
+#: apps/blog/templates/post-list.scm:23 apps/blog/templates/post-list.scm:40 
apps/blog/templates/tag.scm:25 apps/blog/templates/tag.scm:45 
apps/packages/templates/detailed-package-list.scm:24 
apps/packages/templates/detailed-package-list.scm:47 
apps/packages/templates/package-list.scm:24 
apps/packages/templates/package-list.scm:44
+msgid "Page <1/>"
+msgstr ""
+
+#: apps/blog/templates/post-list.scm:24 apps/blog/templates/post.scm:25 
apps/blog/templates/tag.scm:26
+msgctxt "webpage title"
+msgid "Blog"
+msgstr ""
+
+#: apps/blog/templates/post-list.scm:26 apps/blog/templates/post.scm:27
+msgid "Blog posts about GNU Guix."
+msgstr ""
+
+#: apps/blog/templates/post.scm:53
+msgid "Related topics:"
+msgstr ""
+
+#: apps/blog/templates/tag.scm:28
+msgid "Blog posts about <1/> on GNU Guix."
+msgstr ""
+
+#: apps/blog/templates/tag.scm:54
+msgid "Blog — "
+msgstr ""
+
+#: apps/download/data.scm:20
+msgctxt "download page title"
+msgid "GNU Guix System <1/>"
+msgstr ""
+
+#: apps/download/data.scm:23
+msgid "USB/DVD ISO installer of the standalone Guix System."
+msgstr ""
+
+#. TRANSLATORS: System installation is a section name in the
+#. English (en) manual.
+#: apps/download/data.scm:31
+msgid "<1>en</1>System-Installation.html"
+msgstr ""
+
+#: apps/download/data.scm:35
+msgctxt "download page title"
+msgid "GNU Guix <1/> QEMU Image"
+msgstr ""
+
+#: apps/download/data.scm:38
+msgid "QCOW2 virtual machine (VM) image."
+msgstr ""
+
+#. TRANSLATORS: Running Guix in a VM is a section name in the
+#. English (en) manual.
+#: apps/download/data.scm:45
+msgid "<1>en</1>Running-Guix-in-a-VM.html"
+msgstr ""
+
+#: apps/download/data.scm:49
+msgctxt "download page title"
+msgid "GNU Guix <1/> Binary"
+msgstr ""
+
+#: apps/download/data.scm:51
+msgid "Self-contained tarball providing binaries for Guix and its\n       
dependencies, to be installed on top of your Linux-based system."
+msgstr ""
+
+#. TRANSLATORS: Binary Installation is a section name in the
+#. English (en) manual.
+#: apps/download/data.scm:64
+msgid "<1>en</1>Binary-Installation.html"
+msgstr ""
+
+#: apps/download/data.scm:68
+msgctxt "download page title"
+msgid "GNU Guix <1/> Source"
+msgstr ""
+
+#: apps/download/data.scm:69
+msgid "Source code distribution."
+msgstr ""
+
+#. TRANSLATORS: Requirements is a section name in the English (en)
+#. manual.
+#: apps/download/data.scm:76
+msgid "<1>en</1>Requirements.html"
+msgstr ""
+
+#: apps/download/templates/components.scm:25 
apps/download/templates/download-latest.scm:93
+msgid "Download options:"
+msgstr ""
+
+#: apps/download/templates/components.scm:38
+msgid "Signatures: "
+msgstr ""
+
+#: apps/download/templates/components.scm:50
+msgid "<1>Installation instructions</1>."
+msgstr ""
+
+#: apps/download/templates/download.scm:34
+msgctxt "webpage title"
+msgid "Download"
+msgstr ""
+
+#: apps/download/templates/download.scm:36
+msgid "Installers and source files for GNU Guix.  GNU Guix can be\n   
installed on different GNU/Linux distributions."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/download/templates/download.scm:40 
apps/download/templates/download-latest.scm:122
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Installer|Source code|Package manager"
+msgstr ""
+
+#: apps/download/templates/download.scm:52
+msgid "Download"
+msgstr ""
+
+#. TRANSLATORS: System Installation is a section name
+#. in the English (en) manual.
+#: apps/download/templates/download.scm:56
+msgid "As of version <1/>, the standalone Guix System <2>can be 
installed<2.1>en</2.1><2.2>System-Installation.html</2.2></2> on an i686, 
x86_64, ARMv7, or AArch64 machine.  It uses the <3>Linux-Libre</3> kernel and 
the <4>GNU Shepherd</4> init system. Alternately, GNU Guix\n          can be 
installed as an additional package manager on top of an\n          installed 
Linux-based system."
+msgstr ""
+
+#: apps/download/templates/download.scm:74
+msgid "Source code and binaries for the Guix System distribution ISO\n         
 image as well as GNU Guix can be found on the GNU servers at <1/>.  Older 
releases can still be found on <2/>."
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:50
+msgctxt "download page title"
+msgid "GNU Guix System on Linux"
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:51
+msgid "USB/DVD ISO installer of the standalone Guix System on Linux."
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:58
+msgctxt "download page title"
+msgid "GNU Guix System on GNU Hurd"
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:59
+msgid "Virtual machine image of the standalone Guix System on GNU Hurd."
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:102
+msgid "Build details: "
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:116
+msgctxt "webpage title"
+msgid "Download latest"
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:118
+msgid "Download latest GNU Guix System images built by the Cuirass 
continuous\nintegration system."
+msgstr ""
+
+#: apps/download/templates/download-latest.scm:135
+msgid "Download latest images"
+msgstr ""
+
+#. TRANSLATORS: Continuous Integration is a section name
+#. in the English (en) manual.
+#: apps/download/templates/download-latest.scm:138
+msgid "Download latest GNU Guix System images built by the 
<1>Cuirass<1.1>en</1.1><1.2>Continuous-Integration.html</1.2></1> continuous 
integration system at <2/>. These images are <3>development snapshots</3>, you 
might prefer to use stable images that can be found <4>here.</4>"
+msgstr ""
+
+#: apps/media/data.scm:25
+msgctxt "video title"
+msgid "Installation from Script"
+msgstr ""
+
+#: apps/media/data.scm:27
+msgid "Explains how to install Guix on distributions not running\nGNU Guix."
+msgstr ""
+
+#: apps/media/data.scm:34
+msgctxt "video title"
+msgid "Everyday use of GNU Guix, Part One"
+msgstr ""
+
+#: apps/media/data.scm:36
+msgid "How to install packages and how to manage software 
package\ngenerations."
+msgstr ""
+
+#: apps/media/data.scm:42
+msgctxt "video title"
+msgid "Everyday use of GNU Guix, Part Two"
+msgstr ""
+
+#: apps/media/data.scm:44
+msgid "How to upgrade software and how to reclaim storage space."
+msgstr ""
+
+#: apps/media/data.scm:50
+msgctxt "video title"
+msgid "Asking for help"
+msgstr ""
+
+#: apps/media/data.scm:52
+msgid "How to get help from the Guix community."
+msgstr ""
+
+#: apps/media/data.scm:58
+msgctxt "video title"
+msgid "Packaging, Part One"
+msgstr ""
+
+#: apps/media/data.scm:60
+msgid "How to set up a development environment for GNU Guix."
+msgstr ""
+
+#: apps/media/data.scm:65
+msgctxt "video title"
+msgid "Packaging, Part Two"
+msgstr ""
+
+#: apps/media/data.scm:67
+msgid "How to create a package recipe for not yet packaged software."
+msgstr ""
+
+#: apps/media/data.scm:72
+msgctxt "video title"
+msgid "Packaging, Part Three"
+msgstr ""
+
+#: apps/media/data.scm:74
+msgid "How to submit a package for inclusion in the GNU Guix\ndistribution."
+msgstr ""
+
+#: apps/media/data.scm:84
+msgctxt "screenshot title"
+msgid "Graphical log-in"
+msgstr ""
+
+#: apps/media/data.scm:88
+msgid "Graphical log-in screen"
+msgstr ""
+
+#: apps/media/data.scm:91
+msgctxt "screenshot title"
+msgid "GNOME"
+msgstr ""
+
+#: apps/media/data.scm:95
+msgid "GNOME desktop environment"
+msgstr ""
+
+#: apps/media/data.scm:98
+msgctxt "screenshot title"
+msgid "Xfce"
+msgstr ""
+
+#: apps/media/data.scm:102
+msgid "Xfce desktop environment"
+msgstr ""
+
+#: apps/media/data.scm:105
+msgctxt "screenshot title"
+msgid "Virtual machine"
+msgstr ""
+
+#: apps/media/data.scm:109
+msgid "Virtual machine started with 'guix system vm'"
+msgstr ""
+
+#: apps/media/data.scm:112
+msgctxt "screenshot title"
+msgid "Sway"
+msgstr ""
+
+#: apps/media/data.scm:116
+msgid "Sway window manager running wayland"
+msgstr ""
+
+#: apps/media/data.scm:119
+msgctxt "screenshot title"
+msgid "Enlightenment"
+msgstr ""
+
+#: apps/media/data.scm:123
+msgid "Enlightenment, Inkscape, and Serbian text"
+msgstr ""
+
+#: apps/media/templates/components.scm:70
+msgid "Download video: "
+msgstr ""
+
+#: apps/media/templates/components.scm:101
+msgid "Last updated: <1/>"
+msgstr ""
+
+#: apps/media/templates/screenshot.scm:20 
apps/media/templates/screenshots-overview.scm:17
+msgctxt "webpage title"
+msgid "Screenshots"
+msgstr ""
+
+#: apps/media/templates/screenshots-overview.scm:18
+msgid "Overview of all screenshots."
+msgstr ""
+
+#: apps/media/templates/video.scm:22
+msgctxt "webpage title"
+msgid "Video"
+msgstr ""
+
+#: apps/media/templates/video.scm:24 apps/media/templates/video-list.scm:24
+msgid "Video about GNU Guix."
+msgstr ""
+
+#. TRANSLATORS: |-separated list of webpage keywords
+#. TRANSLATORS: |-separated list of webpage keywords
+#: apps/media/templates/video.scm:27 apps/media/templates/video-list.scm:27
+msgid "GNU|Linux|Unix|Free software|Libre software|Operating system|GNU 
Hurd|GNU Guix package manager|Help resources|Videos"
+msgstr ""
+
+#: apps/media/templates/video.scm:45
+msgctxt "button"
+msgid "← Previous"
+msgstr ""
+
+#: apps/media/templates/video.scm:50
+msgctxt "button"
+msgid "Next →"
+msgstr ""
+
+#: apps/media/templates/video-list.scm:22
+msgctxt "webpage title"
+msgid "Videos"
+msgstr ""
+
+#: apps/packages/templates/components.scm:59 
apps/packages/templates/package.scm:56
+msgid "This is a GNU package.  "
+msgstr ""
+
+#: apps/packages/templates/components.scm:66
+msgid "<1>License:</1> <2/>."
+msgstr ""
+
+#: apps/packages/templates/components.scm:70
+msgid "<1>Website:</1> <2/>."
+msgstr ""
+
+#: apps/packages/templates/components.scm:74
+msgid "<1>Package source:</1> <2/>."
+msgstr ""
+
+#: apps/packages/templates/components.scm:78
+msgid "<1>Patches:</1> <2/>."
+msgstr ""
+
+#: apps/packages/templates/components.scm:82
+msgid "<1>Lint issues:</1> <2/>."
+msgstr ""
+
+#: apps/packages/templates/components.scm:89
+msgid "<1>Builds:</1> <2/>."
+msgstr ""
+
+#: apps/packages/templates/components.scm:104
+msgid " issue"
+msgid_plural " issues"
+msgstr[0] ""
+msgstr[1] ""
+
+#: apps/packages/templates/components.scm:115 
apps/packages/templates/components.scm:221
+msgid "Packages menu: "
+msgstr ""
+
+#: apps/packages/templates/components.scm:117 
apps/packages/templates/components.scm:223
+msgid "Browse alphabetically"
+msgstr ""
+
+#: apps/packages/templates/components.scm:204 
apps/packages/templates/components.scm:270
+msgid "None"
+msgstr ""
+
+#: apps/packages/templates/detailed-index.scm:22 
apps/packages/templates/detailed-package-list.scm:25 
apps/packages/templates/index.scm:22 
apps/packages/templates/package-list.scm:25 
apps/packages/templates/package.scm:28
+msgctxt "webpage title"
+msgid "Packages"
+msgstr ""
+
+#: apps/packages/templates/detailed-index.scm:24 
apps/packages/templates/detailed-package-list.scm:27 
apps/packages/templates/index.scm:24
+msgid "List of packages available through GNU Guix."
+msgstr ""
+
+#: apps/packages/templates/detailed-index.scm:45 
apps/packages/templates/index.scm:44
+msgid "GNU Guix provides <1/> packages transparently <2>available as pre-built 
binaries</2>. These pages provide a complete list of the packages.  Our 
<3>continuous integration system</3> shows their current build status (updated 
<4/>)."
+msgstr ""
+
+#: apps/packages/templates/detailed-package-list.scm:56 
apps/packages/templates/package-list.scm:53
+msgid "Packages — "
+msgstr ""
+
+#: apps/packages/templates/package.scm:62
+msgid "<1>Website: </1>"
+msgstr ""
+
+#: apps/packages/templates/package.scm:65
+msgid "<1>License: </1>"
+msgstr ""
+
+#: apps/packages/templates/package.scm:67
+msgid "<1>Package source: </1>"
+msgstr ""
+
+#: apps/packages/templates/package.scm:69
+msgid "<1>Patches: </1>"
+msgstr ""
+
+#: apps/packages/templates/package.scm:71
+msgid "<1>Builds: </1>"
+msgstr ""
+
+#: apps/packages/templates/package.scm:77
+msgid "<1>Lint issues</1><2><2.1/>. See <2.2>package definition</2.2> in Guix 
source code.</2>"
+msgstr ""
diff --git a/website/po/ietf-tags.scm b/website/po/ietf-tags.scm
new file mode 100644
index 0000000..8102a49
--- /dev/null
+++ b/website/po/ietf-tags.scm
@@ -0,0 +1,9 @@
+;;; This file contains an association list for each translation from
+;;; the locale to an IETF language tag to be used in the URL path of
+;;; translated pages.  The language tag results from the translation
+;;; team’s language code from
+;;; <https://translationproject.org/team/index.html>.  The underscore
+;;; in the team’s code is replaced by a hyphen.  For example, az would
+;;; be used for the Azerbaijani language (not az-Latn) and zh-CN would
+;;; be used for mainland Chinese (not zh-Hans-CN).
+(("en_US" . "en"))
diff --git a/website/scripts/sexp-xgettext.scm 
b/website/scripts/sexp-xgettext.scm
new file mode 100644
index 0000000..aba527f
--- /dev/null
+++ b/website/scripts/sexp-xgettext.scm
@@ -0,0 +1,830 @@
+;;; GNU Guix web site
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
+;;;
+;;; This file is part of the GNU Guix web site.
+;;;
+;;; The GNU Guix web site is free software; you can redistribute it and/or 
modify it
+;;; under the terms of the GNU Affero General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; The GNU Guix web site is distributed in the hope that it will be useful, 
but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU Affero General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Affero General Public License
+;;; along with the GNU Guix web site.  If not, see 
<http://www.gnu.org/licenses/>.
+
+(use-modules (ice-9 getopt-long)
+             (ice-9 match)
+             (ice-9 peg)
+             (ice-9 receive)
+             (ice-9 regex)
+             (ice-9 textual-ports)
+             (srfi srfi-1) ;lists
+             (srfi srfi-9) ;records
+             (srfi srfi-19) ;date
+             (srfi srfi-26)) ;cut
+
+;;; This script imitates xgettext, but combines nested s-expressions
+;;; in the input Scheme files to a single msgstr in the PO file.  It
+;;; works by first reading the keywords specified on the command-line,
+;;; then dealing with the remaining options using (ice-9 getopt-long).
+;;; Then, it parses each Scheme file in the POTFILES file specified
+;;; with --files-from and constructs po entries from it.  For parsing,
+;;; a PEG is used instead of Scheme’s read, because we can extract
+;;; comments with it.  The po entries are written to the PO file
+;;; specified with the --output option.  Scheme code can then use the
+;;; (sexp-xgettext) module to deconstruct the msgids looked up in the
+;;; PO file via gettext.
+
+(define-record-type <keyword-spec>
+  (make-keyword-spec id sg pl c total xcomment)
+  keyword-spec?
+  (id keyword-spec-id) ;identifier
+  (sg keyword-spec-sg) ;arg with singular
+  (pl keyword-spec-pl) ;arg with plural
+  (c keyword-spec-c) ;arg with msgctxt or 'mixed if sg is mixed 
msgctxt|singular
+  (total keyword-spec-total) ;total number of args
+  (xcomment keyword-spec-xcomment))
+
+(define (complex-keyword-spec? keyword-spec)
+  "Return for a keyword passed on the command-line whether it is
+complex, i.e. whether occurrences inside another marked expression may
+be part of that other expression.  See i18n-howto.txt."
+  (match keyword-spec
+    (($ <keyword-spec> _ _ #f #f _ #f) #f)
+    (else #t)))
+
+(define %keyword-specs
+  ;; List of valid xgettext keyword options.
+  ;; Read keywords from command-line options.
+  (let loop ((opts (cdr (command-line)));command-line options from
+                                        ;which to extract --keyword
+                                        ;options
+             (remaining-opts '()) ;unhandled opts
+             (specs '()))
+    (define (string->integer str)
+      (if (string-match "[0-9]+" str)
+          (string->number str)
+          (error "Not a decimal integer.")))
+    (define* (argnums->spec id #:optional (argnums '()))
+      (let loop ((sg #f)
+                 (pl #f)
+                 (c #f)
+                 (total #f)
+                 (xcomment #f)
+                 (argnums argnums))
+        (match argnums
+          (() (make-keyword-spec id
+                                 (if sg sg 1)
+                                 pl
+                                 c
+                                 total
+                                 xcomment))
+          ((arg . argnums)
+           (cond
+            ((string-suffix? "c" arg)
+             (cond (c (error "c suffix clashes"))
+                   (else
+                    (let* ((number-str (string-drop-right arg 1))
+                           (number (string->integer number-str)))
+                      (loop sg pl number total xcomment argnums)))))
+            ((string-suffix? "g" arg)
+             (cond
+              (sg (error "Only first argnum can have g suffix."))
+              (c (error "g suffix clashes."))
+              (else
+               (let* ((number-str (string-drop-right arg 1))
+                      (number (string->integer number-str)))
+                 (loop number #f 'mixed total xcomment argnums)))))
+            ((string-suffix? "t" arg)
+             (cond (total (error "t suffix clashes"))
+                   (else
+                    (let* ((number-str (string-drop-right arg 1))
+                           (number (string->integer number-str)))
+                      (loop sg pl c number xcomment argnums)))))
+            ((string-suffix? "\"" arg)
+             (cond (xcomment (error "xcomment clashes"))
+                   (else
+                    (let* ((comment (substring arg
+                                               1
+                                               (- (string-length arg) 1))))
+                      (loop sg pl c total comment argnums)))))
+            (else
+             (let* ((number (string->integer arg)))
+               (if sg
+                   (if pl
+                       (error "Too many argnums.")
+                       (loop sg number c total xcomment argnums))
+                   (loop number #f c total xcomment argnums)))))))))
+
+    (define (string->spec str) ;see `info xgettext`
+      (match (string-split str #\:)
+        ((id) (argnums->spec id))
+        ((id argnums)
+         (argnums->spec id (string-split argnums #\,)))))
+    (match opts
+      (() (begin
+            ;; remove recognized --keyword command-line options:
+            (set-program-arguments (cons (car (command-line))
+                                         (reverse remaining-opts)))
+            specs))
+      ((current-opt . rest)
+       (cond
+        ((string=? "--" current-opt) specs)
+        ((string-prefix? "--keyword=" current-opt)
+         (let ((keyword (string-drop current-opt (string-length 
"--keyword="))))
+           (loop rest remaining-opts (cons (string->spec keyword) specs))))
+        ((or (string=? "--keyword" current-opt)
+             (string=? "-k" current-opt))
+         (let ((next-opt (car rest)))
+           (loop (cdr rest)
+                 remaining-opts
+                 (cons (string->spec next-opt) specs))))
+        (else (loop rest (cons current-opt remaining-opts) specs)))))))
+
+;;; Other options are not repeated, so we can use getopt-long:
+
+(define %options ;; Corresponds to what is documented at `info xgettext`.
+  (let ((option-spec
+         `((files (single-char #\f) (value #t))
+           (directory (single-char #\D) (value #t))
+           (default-domain (single-char #\d) (value #t))
+           (output (single-char #\o) (value #t))
+           (output-dir (single-char #\p) (value #t))
+           (from-code (value #t))
+           (join-existing (single-char #\j) (value #f))
+           (exclude-file (single-char #\x) (value #t))
+           (add-comments (single-char #\c) (value #t))
+
+           ;; Because getopt-long does not support repeated options,
+           ;; we took care of --keyword options further up.
+           ;; (keyword (single-char #\k) (value #t))
+
+           (flag (value #t))
+           (force-po (value #f))
+           (indent (single-char #\i) (value #f))
+           (no-location (value #f))
+           (add-location (single-char #\n) (value #t))
+           (width (single-char #\w) (value #t))
+           (no-wrap (value #f))
+           (sort-output (single-char #\s) (value #f))
+           (sort-by-file (single-char #\F) (value #f))
+           (omit-header (value #f))
+           (copyright-holder (value #t))
+           (foreign-user (value #f))
+           (package-name (value #t))
+           (package-version (value #t))
+           (msgid-bugs-address (value #t))
+           (msgstr-prefix (single-char #\m) (value #t))
+           (msgstr-suffix (single-char #\m) (value #t))
+           (help (value #f))
+           (pack (value #f)))))
+    (getopt-long (command-line) option-spec)))
+
+
+(define parse-scheme-file
+  ;; This procedure parses FILE and returns a parse tree.
+  (let ()
+    ;;TODO: Optionally ignore case.
+    (define-peg-pattern NL all "\n")
+    (define-peg-pattern comment all (and ";"
+                                         (* (and peg-any
+                                                 (not-followed-by NL)))
+                                         (and peg-any (followed-by NL))))
+    (define-peg-pattern empty none (or " " "\t"))
+    (define-peg-pattern whitespace body (or empty NL))
+    (define-peg-pattern quotation body (or "'" "`" "," ",@"))
+                                        ;TODO: Allow user to specify
+                                        ;other quote reader macros to
+                                        ;be ignored and also ignore
+                                        ;quote spelled out without
+                                        ;reader macro.
+    (define-peg-pattern open body (and (? quotation)
+                                       (or "(" "[" "{")))
+    (define-peg-pattern close body (or ")" "]" "}"))
+    (define-peg-pattern string body (and (followed-by "\"")
+                                         (* (or "\\\""
+                                                (and (or NL peg-any)
+                                                     (not-followed-by "\""))))
+                                         (and (or NL peg-any)
+                                              (followed-by "\""))
+                                         "\""))
+    (define-peg-pattern token all (or string
+                                      (and
+                                       (not-followed-by open)
+                                       (not-followed-by close)
+                                       (not-followed-by comment)
+                                       (* (and peg-any
+                                               (not-followed-by open)
+                                               (not-followed-by close)
+                                               (not-followed-by comment)
+                                               (not-followed-by string)
+                                               (not-followed-by whitespace)))
+                                       (or
+                                        (and peg-any (followed-by open))
+                                        (and peg-any (followed-by close))
+                                        (and peg-any (followed-by comment))
+                                        (and peg-any (followed-by string))
+                                        (and peg-any (followed-by whitespace))
+                                        (not-followed-by peg-any)))))
+    (define-peg-pattern list all (or (and (? quotation) "(" program ")")
+                                     (and (? quotation) "[" program "]")
+                                     (and (? quotation) "{" program "}")))
+    (define-peg-pattern t-or-s body (or token list))
+    (define-peg-pattern program all (* (or whitespace
+                                           comment
+                                           t-or-s)))
+    (lambda (file)
+      (call-with-input-file file
+        (lambda (port)
+          ;; It would be nice to match port directly without
+          ;; converting to a string first, but apparently guile cannot
+          ;; do that yet.
+          (let ((string (get-string-all port)))
+            (peg:tree (match-pattern program string))))))))
+
+
+(define-record-type <po-entry>
+  (make-po-entry ecomments ref flags ctxt id idpl)
+  po-entry?
+;;; irrelevant: (tcomments po-entry-tcomments) ;translator-comments
+  (ecomments po-entry-ecomments) ;extracted-comments
+  (ref po-entry-ref) ;reference
+  (flags po-entry-flags)
+;;; irrelevant: (prevctxt po-entry-prevctxt) ;previous-ctxt
+;;; irrelevant: (prev po-entry-prev) ;previous-translation
+  (ctxt po-entry-ctxt) ;msgctxt
+  (id po-entry-id) ;msgid
+  (idpl po-entry-idpl) ;msgid-plural
+;;; irrelevant: (str po-entry-str) ;msgstr string or association list
+;;;                                ;integer to string
+  )
+
+(define (po-equal? po1 po2)
+  "Return whether PO1 and PO2 have equal ctxt, id and idpl."
+  (and (equal? (po-entry-ctxt po1) (po-entry-ctxt po2))
+       (equal? (po-entry-id po1) (po-entry-id po2))
+       (equal? (po-entry-idpl po1) (po-entry-idpl po2))))
+
+(define (combine-duplicate-po-entries list)
+  "Return LIST with duplicate po entries replaced by a single PO entry
+with both refs."
+  (let loop ((remaining list))
+    (match remaining
+      (() '())
+      ((head . tail)
+       (receive (before from)
+           (break (cut po-equal? head <>) tail)
+         (cond
+          ((null? from) (cons head (loop tail)))
+          (else
+           (loop
+            (cons
+             (match head
+               (($ <po-entry> ecomments1 ref1 flags ctxt id idpl)
+                (match (car from)
+                  (($ <po-entry> ecomments2 ref2 _ _ _ _)
+                   (let ((ecomments (if (or ecomments1 ecomments2)
+                                        (append (or ecomments1 '())
+                                                (or ecomments2 '()))
+                                        #f))
+                         (ref (if (or ref1 ref2)
+                                  (string-join
+                                   (cons
+                                    (or ref1 "")
+                                    (cons
+                                     (or ref2 "")
+                                     '())))
+                                  #f)))
+                     (make-po-entry ecomments ref flags ctxt id idpl))))))
+             (append before (cdr from)))))))))))
+
+(define (write-po-entry po-entry)
+  (define (prepare-text text)
+    "If TEXT is false, return #f.  Otherwise correct the formatting of
+TEXT by escaping backslashes and newlines and enclosing TEXT in
+quotes. Note that Scheme’s write is insufficient because it would
+escape far more.  TODO: Strings should be wrappable to a maximum line
+width."
+    (and text
+         (string-append "\""
+                        (with-output-to-string
+                          (lambda ()
+                            (call-with-input-string text
+                              (lambda (port)
+                                (let loop ((c (get-char port)))
+                                  (unless (eof-object? c)
+                                    (case c
+                                      ((#\\) (display "\\"))
+                                      ((#\newline) (display "\\n"))
+                                      (else (write-char c)))
+                                    (loop (get-char port))))))))
+                        "\"")))
+  (define (write-component c prefix)
+    (when c
+      (begin (display prefix)
+             (display " ")
+             (display c)
+             (newline))))
+  (match po-entry
+    (($ <po-entry> ecomments ref flags ctxt id idpl)
+     (let ((prepared-ctxt (prepare-text ctxt))
+           (prepared-id (prepare-text id))
+           (prepared-idpl (prepare-text idpl)))
+       (when ecomments
+         (for-each
+          (lambda (line)
+            (write-component line "#."))
+          (reverse ecomments)))
+       (write-component ref "#:")
+       (write-component (and flags (string-join flags ", ")) "#,")
+       (write-component prepared-ctxt "msgctxt")
+       (write-component prepared-id "msgid")
+       (write-component prepared-idpl "msgid_plural")
+       (if idpl
+           (begin
+             (display "msgstr[0] \"\"")
+             (newline)
+             (display "msgstr[1] \"\""))
+           (display "msgstr \"\""))
+       (newline)))))
+
+;; Extraction of TRANSLATORS comments:
+
+(define %comments-line
+  (make-parameter #f))
+
+(define %ecomments-string
+  (make-parameter #f))
+
+(define (update-ecomments-string! str)
+  "Sets the value of the parameter object %ecomments-string if str is
+an ecomments string.  An ecomments string is extracted from a comment
+because it starts with TRANSLATORS or a key specified with
+--add-comments." ;TODO: Support for other keys is missing.
+  (cond
+   ((not str) (%ecomments-string #f))
+   ((= (1+ (or (%comments-line)
+               -42)) ;arbitrary unequal initial value
+       (or (%line-number) 0))
+    (let ((m (string-match ";+[ \t]*(.*)" str)))
+      (when m
+        (%comments-line (%line-number))
+        (%ecomments-string
+         (if (%ecomments-string)
+             (cons (match:substring m 1) (%ecomments-string))
+             (list (match:substring m 1)))))))
+   (else
+    (let ((m (string-match ";+[ \t]*(TRANSLATORS:.*)" str)))
+      (if m
+          (begin
+            (%comments-line (%line-number))
+            (%ecomments-string
+             (if (%ecomments-string)
+                 (cons (match:substring m 1) (%ecomments-string))
+                 (list (match:substring m 1)))))
+          (%ecomments-string '#f))))))
+
+(define %file-name
+  (make-parameter #f))
+
+(define (update-file-name! name)
+  "Sets the value of the parameter object %file-name to NAME."
+  (%file-name name))
+
+(define %old-line-number
+  (make-parameter #f))
+
+(define (update-old-line-number! number)
+  "Sets the value of the parameter object %old-line-number to NUMBER."
+  (%old-line-number number))
+
+(define %line-number
+  (make-parameter #f))
+
+(define (update-line-number! number)
+  "Sets the value of the parameter object %line-number to NUMBER."
+  (%line-number number))
+
+(define (incr-line-number!)
+  "Increments the value of the parameter object %line-number by 1."
+  (%line-number (1+ (%line-number))))
+
+(define (incr-line-number-for-each-nl! list)
+  "Increments %line-number once for each NL recursively in LIST.  Does
+nothing if LIST is no list but e.g. an empty 'program."
+  (when (list? list)
+    (for-each
+     (lambda (part)
+       (match part
+         ('NL (incr-line-number!))
+         ((? list?) (incr-line-number-for-each-nl! part))
+         (else #f)))
+     list)))
+
+(define (current-ref)
+  "Return the location field for a PO entry."
+  (let ((add (option-ref %options 'add-location 'full)))
+    (cond
+     ((option-ref %options 'no-location #f) #f)
+     ((eq? add 'full)
+      (string-append (%file-name) ":" (number->string (%line-number))))
+     ((eq? add 'file)
+      (%file-name))
+     ((eq? add 'never)
+      #f))))
+
+(define (make-simple-po-entry msgid)
+  (let ((po (make-po-entry
+             (%ecomments-string)
+             (current-ref)
+             #f ;TODO: Use scheme-format for format strings?
+             #f ;no ctxt
+             msgid
+             #f)))
+    (update-ecomments-string! #f)
+    po))
+
+
+(define (matching-keyword id)
+  "Return the keyword-spec whose identifier is the same as ID, or #f
+if ID is no string or no such keyword-spec exists."
+  (and (symbol? id)
+       (let ((found (member (symbol->string id)
+                            %keyword-specs
+                            (lambda (id spec)
+                              (string=? id (keyword-spec-id spec))))))
+         (and found (car found)))))
+
+(define (nth-exp program n)
+  "Return the Nth 'token or 'list inside the PROGRAM parse tree or #f
+if no tokens or lists exist."
+  (let loop ((i 0)
+             (rest program))
+    (define (on-hit exp)
+      (if (= i n) exp
+          ;; else:
+          (loop (1+ i) (cdr rest))))
+    (match rest
+      (() #f)
+      ((('token . _) . _) (on-hit (car rest)))
+      ((('list open-paren exp close-paren) . _) (on-hit (car rest)))
+      ((_ . _) (loop i (cdr rest)))
+      (else #f))))
+
+(define (more-than-one-exp? program)
+  "Return true if PROGRAM consiste of more than one expression."
+  (if (matching-keyword (token->string-symbol-or-keyw (nth-exp program 0)))
+      (nth-exp program 2) ;if there is third element, keyword does not count
+      (nth-exp program 1)))
+
+(define (token->string-symbol-or-keyw tok)
+  "For a parse tree TOK, if it is a 'token parse tree, return its
+value as a string, symbol or #:-keyword, otherwise return #f."
+  (match tok
+    (('token (parts ...) . remaining)
+     ;; This is a string with line breaks in it.
+     (with-input-from-string
+         (string-append
+          (apply string-append
+                 (map-in-order
+                  (lambda (part)
+                    (match part
+                      (('NL _)
+                       (begin (incr-line-number!)
+                              "\n"))
+                      (else part)))
+                  parts))
+          (car remaining))
+       (lambda ()
+         (read))))
+    (('token exp)
+     (with-input-from-string exp
+       (lambda ()
+         (read))))
+    (else #f)))
+
+(define (complex-marked-list->po-entries parse-tree)
+  "Check if PARSE-TREE is marked by a keyword.  If yes, for a complex
+keyword spec, return a list of po-entries for it.  For a simple
+keyword spec, return the argument number of its singular form.
+Otherwise return #f."
+  (let* ((first (nth-exp parse-tree 0))
+         (spec (matching-keyword (token->string-symbol-or-keyw first))))
+    (if spec
+        (if ;if the identifier of a complex keyword occurs first
+         (complex-keyword-spec? spec)
+         ;; then make po entries for it
+         (match spec
+           (($ <keyword-spec> id sg pl c total xcomment)
+            (if (eq? c 'mixed) ; if msgctxt and singular msgid are in one 
string
+                (let* ((exp (nth-exp parse-tree sg))
+                       (val (token->string-symbol-or-keyw exp))
+                       (idx (if (string? val) (string-rindex val #\|))))
+                  (list
+                   (let ((po (make-po-entry
+                              (%ecomments-string)
+                              (current-ref)
+                              #f ;TODO: Use scheme-format for format strings?
+                              (string-take val idx)
+                              (string-drop val (1+ idx))
+                              #f))) ;plural forms are unsupported here
+                     (update-ecomments-string! #f)
+                     po)))
+                ;; else construct msgids
+                (receive (pl-id pl-entries)
+                    (match pl
+                      (#f (values #f '()))
+                      (else (construct-msgid-and-po-entries
+                             (nth-exp parse-tree pl))))
+                  (receive (sg-id sg-entries)
+                      (construct-msgid-and-po-entries
+                       (nth-exp parse-tree sg))
+                    (cons
+                     (let ((po (make-po-entry
+                                (%ecomments-string)
+                                (current-ref)
+                                #f ;TODO: Use scheme-format for format strings?
+                                (and c (token->string-symbol-or-keyw
+                                        (nth-exp parse-tree c)))
+                                sg-id
+                                pl-id)))
+                       (update-ecomments-string! #f)
+                       po)
+                     (append sg-entries pl-entries)))))))
+         ;; else if it is a simple keyword, return the argnum:
+         (keyword-spec-sg spec))
+        ;; if no keyword occurs, then false
+        #f)))
+
+(define (construct-po-entries parse-tree)
+  "Converts a PARSE-TREE resulting from a call to parse-scheme-file to
+a list of po-entry records.  Unlike construct-msgid-and-po-entries,
+strings are not collected to a msgid.  The list of po-entry records is
+the return value."
+  (let ((entries (complex-marked-list->po-entries parse-tree)))
+    (cond
+     ((list? entries) entries)
+     ((number? entries) ;parse-tree yields a single, simple po entry
+      (update-old-line-number! (%line-number))
+      (receive (id entries)
+          (construct-msgid-and-po-entries
+           (nth-exp parse-tree entries))
+        (update-line-number! (%old-line-number))
+        (let ((po (make-simple-po-entry id)))
+          (incr-line-number-for-each-nl! parse-tree)
+          (cons po entries))))
+     (else ;search for marked translations in parse-tree
+      (match parse-tree
+        (() '())
+        (('comment str) (begin
+                          (update-ecomments-string! str)
+                          '()))
+        (('NL _) (begin (incr-line-number!) '()))
+        (('token . _) (begin (incr-line-number-for-each-nl! parse-tree) '()))
+        (('list open-paren program close-paren)
+         (construct-po-entries program))
+        (('program . components)
+         (append-map construct-po-entries components))
+        ;; Note: PEG compresses empty programs to non-lists:
+        ('program
+         '()))))))
+
+(define* (tag counter prefix #:key (flavor 'start))
+  "Formats the number COUNTER as a tag according to FLAVOR, which is
+either 'start, 'end or 'empty for a start, end or empty tag,
+respectively."
+  (string-append "<"
+                 (if (eq? flavor 'end) "/" "")
+                 prefix
+                 (number->string counter)
+                 (if (eq? flavor 'empty) "/" "")
+                 ">"))
+
+(define-record-type <construct-fold-state>
+  (make-construct-fold-state msgid-string maybe-part counter po-entries)
+  construct-fold-state?
+  ;; msgid constructed so far; #f if none, "" if only empty string:
+  (msgid-string construct-fold-state-msgid-string)
+  ;; only append this if string follows:
+  (maybe-part construct-fold-state-maybe-part)
+  ;; counter for next tag:
+  (counter construct-fold-state-counter)
+  ;; complete po entries from marked sub-expressions:
+  (po-entries construct-fold-state-po-entries))
+
+(define* (construct-msgid-and-po-entries parse-tree
+                                         #:optional
+                                         (prefix ""))
+  "Like construct-po-entries, but with two return values.  The first
+is an accumulated msgid constructed from all components in PARSE-TREE
+for use in make-po-entry.  Non-strings are replaced by tags containing
+PREFIX.  The second return value is a list of po entries for
+sub-expressions marked with a complex keyword spec."
+  (match parse-tree
+    (() (values "" '()))
+    ;; Note: PEG compresses empty programs to non-lists:
+    ('program (values "" '()))
+    (('comment str) (begin
+                      (update-ecomments-string! str)
+                      (values "" '())))
+    (('NL _) (begin (incr-line-number!)
+                    (error "Program consists only of line break."
+                           `(,(%file-name) ,(%line-number)))))
+    (('token . _)
+     (let ((maybe-string (token->string-symbol-or-keyw parse-tree)))
+       (if (string? maybe-string)
+           (values maybe-string '())
+           (error "Single symbol marked for translation."
+                  `(,maybe-string ,(%file-name) ,(%line-number))))))
+    (('list open-paren program close-paren)
+     ;; parse program instead
+     (construct-msgid-and-po-entries program prefix))
+    (('program (? matching-keyword))
+     (error "Double-marked for translation."
+            `(,parse-tree ,(%file-name) ,(%line-number))))
+    (('program . components)
+     ;; Concatenate strings in parse-tree to a new msgid and add an
+     ;; <x> tag for each list in between.
+     (match
+         (fold
+          (lambda (component prev-state)
+            (match prev-state
+              (($ <construct-fold-state> msgid-string maybe-part
+                  counter po-entries)
+               (match component
+                 (('comment str) (begin (update-ecomments-string! str)
+                                        prev-state))
+                 (('NL _) (begin (incr-line-number!)
+                                 prev-state))
+                 (('token . _)
+                  (let ((maybe-string (token->string-symbol-or-keyw 
component)))
+                    (cond
+                     ((string? maybe-string)
+                      ;; if string, append maybe-string to previous msgid
+                      (make-construct-fold-state
+                       (string-append (or msgid-string "")
+                                      maybe-part maybe-string)
+                       ""
+                       counter
+                       po-entries))
+                     ((and (more-than-one-exp? components) ;not the only symbol
+                           (or (not msgid-string) ;no string so far
+                               (string-suffix? ">" msgid-string))) ;tag before
+                      prev-state) ;then ignore
+                     (else ;append tag representing the token
+                      (make-construct-fold-state
+                       msgid-string
+                       (string-append
+                        maybe-part
+                        (tag counter prefix #:flavor 'empty))
+                       (1+ counter)
+                       po-entries)))))
+                 (('list open-paren program close-paren)
+                  (let ((first (nth-exp program 0)))
+                    (incr-line-number-for-each-nl! list)
+                    (match (complex-marked-list->po-entries program)
+                      ((? list? result)
+                       (make-construct-fold-state
+                        msgid-string
+                        (string-append
+                         maybe-part
+                         (tag counter prefix #:flavor 'empty))
+                        (1+ counter)
+                        (append result po-entries)))
+                      (result
+                       (cond
+                        ((number? result)
+                         (receive (id entries)
+                             (construct-msgid-and-po-entries
+                              (nth-exp program result)
+                              (string-append prefix
+                                             (number->string counter)
+                                             "."))
+                           (make-construct-fold-state
+                            (string-append (or msgid-string "")
+                                           maybe-part
+                                           (tag counter prefix
+                                                #:flavor 'start)
+                                           id
+                                           (tag counter prefix
+                                                #:flavor 'end))
+                            ""
+                            (1+ counter)
+                            (append entries po-entries))))
+                        ((not (more-than-one-exp? components))
+                         ;; Singletons do not need to be marked.
+                         (receive (id entries)
+                             (construct-msgid-and-po-entries
+                              program
+                              prefix)
+                           (make-construct-fold-state
+                            id
+                            ""
+                            counter
+                            (append entries po-entries))))
+                        (else ;unmarked list
+                         (if (not msgid-string)
+                             ;; then ignore
+                             prev-state
+                             ;; else:
+                             (make-construct-fold-state
+                              msgid-string
+                              (string-append
+                               maybe-part
+                               (tag counter prefix #:flavor 'empty))
+                              (1+ counter)
+                              po-entries))))))))))))
+          (make-construct-fold-state #f "" 1 '())
+          components)
+       (($ <construct-fold-state> msgid-string maybe-part counter po-entries)
+        (values (or msgid-string
+                    (error "Marking for translation yields empty msgid."
+                           %file-name %line-number))
+                po-entries))))))
+
+(define scheme-file->po-entries
+  (compose construct-po-entries
+           parse-scheme-file))
+
+(define %files-from-port
+  (let ((files-from (option-ref %options 'files #f)))
+    (if files-from
+        (open-input-file files-from)
+        (current-input-port))))
+
+(define %source-files
+  (let loop ((line (get-line %files-from-port))
+             (source-files '()))
+    (if (eof-object? line)
+        (begin
+          (close-port %files-from-port)
+          source-files)
+        ;; else read file names before comment
+        (let ((before-comment (car (string-split line #\#))))
+          (loop (get-line %files-from-port)
+                (append
+                 (map match:substring (list-matches "[^ \t]+" before-comment))
+                 source-files))))))
+
+(define %output-po-entries
+  (fold (lambda (scheme-file po-entries)
+          (begin
+            (update-file-name! scheme-file)
+            (update-line-number! 1)
+            (update-old-line-number! #f)
+            (%comments-line #f)
+            (append (scheme-file->po-entries scheme-file)
+                    po-entries)))
+        '()
+        %source-files))
+
+(define %output-port
+  (let ((output (option-ref %options 'output #f))
+        (domain (option-ref %options 'default-domain #f)))
+    (cond
+     (output (open-output-file output))
+     (domain (open-output-file (string-append domain ".po")))
+     (else (open-output-file "messages.po")))))
+
+(with-output-to-port %output-port
+  (lambda ()
+    (let ((copyright (option-ref %options 'copyright-holder
+                                 "THE PACKAGE'S COPYRIGHT HOLDER"))
+          (package (option-ref %options 'package-name "PACKAGE"))
+          (version (option-ref %options 'package-version #f))
+          (bugs-email (option-ref %options 'msgid-bugs-address "")))
+      (display "# SOME DESCRIPTIVE TITLE.\n")
+      (display (string-append "# Copyright (C) YEAR " copyright "\n"))
+      (display (string-append "# This file is distributed under the same \
+license as the " package " package.\n"))
+      (display "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n")
+      (display "#\n")
+      (write-po-entry (make-po-entry #f #f '("fuzzy") #f "" #f))
+      (display (string-append "\"Project-Id-Version: "
+                              package
+                              (if version
+                                  (string-append " " version)
+                                  "")
+                              "\\n\"\n"))
+      (display (string-append "\"Report-Msgid-Bugs-To: "
+                              bugs-email
+                              "\\n\"\n"))
+      (display (string-append "\"POT-Creation-Date: "
+                              (date->string (current-date) "~1 ~H:~M~z")
+                              "\\n\"\n"))
+      (display "\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n")
+      (display "\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n")
+      (display "\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n")
+      (display "\"Language: \\n\"\n")
+      (display "\"MIME-Version: 1.0\\n\"\n")
+      (display "\"Content-Type: text/plain; charset=UTF-8\\n\"\n")
+      (display "\"Content-Transfer-Encoding: 8bit\\n\"\n")
+      (for-each (lambda (po-entry)
+                  (begin
+                    (newline)
+                    (write-po-entry po-entry)))
+                (combine-duplicate-po-entries %output-po-entries)))))
diff --git a/website/sexp-xgettext.scm b/website/sexp-xgettext.scm
new file mode 100644
index 0000000..71ef4a9
--- /dev/null
+++ b/website/sexp-xgettext.scm
@@ -0,0 +1,530 @@
+;;; GNU Guix web site
+;;; Copyright © 2019 Florian Pelz <pelzflorian@pelzflorian.de>
+;;;
+;;; This file is part of the GNU Guix web site.
+;;;
+;;; The GNU Guix web site is free software; you can redistribute it and/or 
modify it
+;;; under the terms of the GNU Affero General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; The GNU Guix web site is distributed in the hope that it will be useful, 
but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU Affero General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Affero General Public License
+;;; along with the GNU Guix web site.  If not, see 
<http://www.gnu.org/licenses/>.
+
+(define-module (sexp-xgettext)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 peg)
+  #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 receive)
+  #:use-module (ice-9 regex)
+  #:use-module (srfi srfi-1) ;lists
+  #:use-module (srfi srfi-9) ;records
+  #:export (set-complex-keywords!
+            set-simple-keywords!
+            sgettext
+            sngettext
+            spgettext
+            snpgettext
+            %linguas))
+
+(define %complex-keywords
+  ;; Use set-complex-keywords! to change this to a list of keywords
+  ;; for sexp-xgettext functions other than sgettext.
+  (make-parameter '()))
+
+(define (set-complex-keywords! kw)
+  (%complex-keywords kw))
+
+(define %simple-keywords
+  ;; Use set-simple-keywords! to change this to a list of keywords
+  ;; for sgettext.
+  (make-parameter '()))
+
+(define (set-simple-keywords! kw)
+  (%simple-keywords kw))
+
+(define (gettext-keyword? id)
+  (or (member id (%complex-keywords))
+      (member id (%simple-keywords))))
+
+;;COPIED FROM scripts/sexp-xgettext.scm:
+(define* (tag counter prefix #:key (flavor 'start))
+  "Formats the number COUNTER as a tag according to FLAVOR, which is
+either 'start, 'end or 'empty for a start, end or empty tag,
+respectively."
+  (string-append "<"
+                 (if (eq? flavor 'end) "/" "")
+                 prefix
+                 (number->string counter)
+                 (if (eq? flavor 'empty) "/" "")
+                 ">"))
+;;END COPIED FROM scripts/sexp-xgettext.scm
+
+;;ADAPTED FROM scripts/sexp-xgettext.scm
+(define-record-type <construct-fold-state>
+  (make-construct-fold-state msgid-string maybe-part counter)
+  construct-fold-state?
+  ;; msgid constructed so far; #f if none, "" if only empty string
+  (msgid-string construct-fold-state-msgid-string)
+  ;; only append this if string follows:
+  (maybe-part construct-fold-state-maybe-part)
+  ;; counter for next tag:
+  (counter construct-fold-state-counter))
+;;END ADAPTED FROM scripts/sexp-xgettext.scm
+
+(define (sexp->msgid exp)
+  "Return the msgid as constructed by construct-msgid-and-po-entries
+in scripts/sexp-xgettext.scm from the expression EXP."
+  (let loop ((exp exp)
+             (prefix ""))
+    (match exp
+      (() "")
+      ((or ('quote inner-exp)
+           ('quasiquote inner-exp)
+           ('unquote inner-exp)
+           ('unquote-splicing inner-exp))
+       (loop inner-exp prefix))
+      ((first-component . components)
+       (cond
+        ((gettext-keyword? first-component)
+         (error "Double-marked for translation." exp))
+        (else
+         (or
+          (construct-fold-state-msgid-string
+           (fold
+            (lambda (component prev-state)
+              (match prev-state
+                (($ <construct-fold-state> msgid-string maybe-part counter)
+                 (let inner-loop ((exp component))
+                   (match exp
+                     ((or (? symbol?) (? keyword?))
+                      (if (not msgid-string)
+                          ;; ignore symbols at the beginning
+                          prev-state
+                          ;; else make a tag for the symbol
+                          (make-construct-fold-state
+                           msgid-string
+                           (string-append maybe-part
+                                          (tag counter prefix #:flavor 'empty))
+                           (1+ counter))))
+                     ((? string?)
+                      (make-construct-fold-state
+                       (string-append (or msgid-string "")
+                                      maybe-part exp)
+                       "" counter))
+                     ((? list?)
+                      (match exp
+                        (() ;ignore empty list
+                         prev-state)
+                        ((or (singleton)
+                             ('quote singleton)
+                             ('quasiquote singleton)
+                             ('unquote singleton)
+                             ('unquote-splicing singleton))
+                         (inner-loop singleton))
+                        ((components ...)
+                         (cond
+                          ((and (not (null? components))
+                                (member (car components) (%simple-keywords)))
+                           ;; if marked for translation, insert inside tag
+                           (make-construct-fold-state
+                            (string-append (or msgid-string "")
+                                           maybe-part
+                                           (tag counter prefix #:flavor 'start)
+                                           (loop (cadr components)
+                                                 (string-append
+                                                  prefix
+                                                  (number->string counter)
+                                                  "."))
+                                           (tag counter prefix #:flavor 'end))
+                            ""
+                            (1+ counter)))
+                          ;; else ignore if first
+                          ((not msgid-string)
+                           prev-state)
+                          ;; else make empty tag
+                          (else (make-construct-fold-state
+                                 msgid-string
+                                 (string-append
+                                  maybe-part
+                                  (tag counter prefix #:flavor 'empty))
+                                 (1+ counter))))))))))))
+            (make-construct-fold-state #f "" 1)
+            exp))
+          (error "Marking for translation yields empty msgid." exp)))))
+      ((? string?) exp)
+      (else (error "Single symbol marked for translation." exp)))))
+
+(define-record-type <deconstruct-fold-state>
+  (make-deconstruct-fold-state tagged maybe-tagged counter)
+  deconstruct-fold-state?
+  ;; XML-tagged expressions as an association list name->expression:
+  (tagged deconstruct-fold-state-tagged)
+  ;; associate this not-yet-tagged expression with pre if string
+  ;; follows, with post if not:
+  (maybe-tagged deconstruct-fold-state-maybe-tagged)
+  ;; counter for next tag:
+  (counter deconstruct-fold-state-counter))
+
+(define (deconstruct exp msgstr)
+  "Return an s-expression like EXP, but filled with the content from
+MSGSTR."
+  (define (find-empty-element msgstr name)
+    "Return the regex match structure for the empty tag for XML
+element of type NAME inside MSGSTR.  If the element does not exist or
+is more than the empty tag, #f is returned."
+    (string-match (string-append "<" (regexp-quote name) "/>") msgstr))
+  (define (find-element-with-content msgstr name)
+    "Return the regex match structure for the non-empty XML element of
+type NAME inside MSGSTR.  Submatch 1 is its content.  If the element
+does not exist or is just the empty tag, #f is returned."
+    (string-match (string-append "<" (regexp-quote name) ">"
+                                 "(.*)"
+                                 "</" (regexp-quote name) ">")
+                  msgstr))
+  (define (get-first-element-name prefix msgstr)
+    "Return the name of the first XML element in MSGSTR whose name
+begins with PREFIX, or #f if there is none."
+    (let ((m (string-match
+              (string-append "<(" (regexp-quote prefix) "[^>/.]+)/?>") 
msgstr)))
+      (and m (match:substring m 1))))
+  (define (prefix+counter prefix counter)
+    "Return PREFIX with the number COUNTER appended."
+    (string-append prefix (number->string counter)))
+  (let loop ((exp exp)
+             (msgstr msgstr)
+             (prefix ""))
+    (define (unwrap-marked-expression exp)
+      "Return two values for an expression EXP containing a (possibly
+quoted/unquoted) marking for translation with a simple keyword at its
+root.  The first return value is a list with the inner expression, the
+second is a procedure to wrap the processed inner expression in the
+same quotes or unquotes again."
+      (match exp
+        (('quote inner-exp)
+         (receive (unwrapped quotation)
+             (unwrap-marked-expression inner-exp)
+           (values unwrapped
+                   (lambda (res)
+                     (list 'quote (quotation res))))))
+        (('quasiquote inner-exp)
+         (receive (unwrapped quotation)
+             (unwrap-marked-expression inner-exp)
+           (values unwrapped
+                   (lambda (res)
+                     (list 'quasiquote (quotation res))))))
+        (('unquote inner-exp)
+         (receive (unwrapped quotation)
+             (unwrap-marked-expression inner-exp)
+           (values unwrapped
+                   (lambda (res)
+                     (list 'unquote (quotation res))))))
+        (('unquote-splicing inner-exp)
+         (receive (unwrapped quotation)
+             (unwrap-marked-expression inner-exp)
+           (values unwrapped
+                   (lambda (res)
+                     (list 'unquote-splicing (quotation res))))))
+        ((marking . rest) ;list with marking as car
+         ;; assume arg to translate is first argument to marking:
+         (values (list-ref rest 0) identity))))
+    (define (assemble-parenthesized-expression prefix tagged)
+      "Return a parenthesized expression deconstructed from MSGSTR
+with the meaning of XML elements taken from the name->expression
+association list TAGGED.  The special tags [prefix]pre and
+[prefix]post are associated with a list of expressions before or after
+all others in the parenthesized expression with the prefix,
+respectively, in reverse order."
+      (append ;prepend pre elements to what is in msgstr
+       (reverse (or (assoc-ref tagged (string-append prefix "pre")) '()))
+       (let assemble ((rest msgstr))
+         (let ((name (get-first-element-name prefix rest)))
+           (cond
+            ((and name (find-empty-element rest name)) =>
+             ;; first XML element in rest is empty element
+             (lambda (m)
+               (cons*
+                (match:prefix m) ;prepend string before name
+                (assoc-ref tagged name) ;and expression for name
+                (assemble (match:suffix m)))))
+            ((and name (find-element-with-content rest name)) =>
+             ;; first XML element in rest has content
+             (lambda (m)
+               (receive (unwrapped quotation)
+                   (unwrap-marked-expression (assoc-ref tagged name))
+                 (cons*
+                  (match:prefix m) ;prepend string before name
+                  ;; and the deconstructed element with the content as msgstr:
+                  (quotation
+                   (loop
+                    unwrapped
+                    (match:substring m 1)
+                    (string-append name ".")))
+                  (assemble (match:suffix m))))))
+            (else
+             ;; there is no first element
+             (cons
+              rest ;return remaining string
+              (reverse ;and post expressions
+               (or (assoc-ref tagged (string-append prefix "post")) 
'())))))))))
+    (match exp
+      (() '())
+      (('quote singleton)
+       (cons 'quote (list (loop singleton msgstr prefix))))
+      (('quasiquote singleton)
+       (cons 'quasiquote (list (loop singleton msgstr prefix))))
+      (('unquote singleton)
+       (cons 'unquote (list (loop singleton msgstr prefix))))
+      (('unquote-splicing singleton)
+       (cons 'unquote-splicing (list (loop singleton msgstr prefix))))
+      ((singleton)
+       (list (loop singleton msgstr prefix)))
+      ((first-component . components)
+       (cond
+        ((gettext-keyword? first-component)
+         ;; another marking for translation
+         ;; -> should be an error anyway; just retain exp
+         exp)
+        (else
+         ;; This handles a single level of a parenthesized expression.
+         ;; assemble-parenthesized-expression will call loop to
+         ;; recurse to deeper levels.
+         (let ((tagged-state
+                (fold
+                 (lambda (component prev-state)
+                   (match prev-state
+                     (($ <deconstruct-fold-state> tagged maybe-tagged counter)
+                      (let inner-loop ((exp component) ;sexp to handle
+                                       (quoting identity)) ;for wrapping state
+                        (define (tagged-with-maybes)
+                          "Return the value of tagged after adding all
+maybe-tagged expressions.  This should be used as the base value for
+tagged when a string or marked expression is seen."
+                          (match counter
+                            (#f
+                             (alist-cons (string-append prefix "pre")
+                                         maybe-tagged
+                                         tagged))
+                            ((? number?)
+                             (let accumulate ((prev-counter counter)
+                                              (maybes (reverse maybe-tagged)))
+                               (match maybes
+                                 (() tagged)
+                                 ((head . tail)
+                                  (alist-cons
+                                   (prefix+counter prefix prev-counter)
+                                   head
+                                   (accumulate (1+ prev-counter) tail))))))))
+                        (define (add-maybe exp)
+                          "Return a deconstruct-fold-state with EXP
+added to maybe-tagged.  This should be used for expressions that are
+neither strings nor marked for translation with a simple keyword."
+                          (make-deconstruct-fold-state
+                           tagged
+                           (cons (quoting exp) maybe-tagged)
+                           counter))
+                        (define (counter-with-maybes)
+                          "Return the old counter value incremented by
+one for each expression in maybe-tagged.  This should be used together
+with tagged-with-maybes."
+                          (match counter
+                            ((? number?)
+                             (+ counter (length maybe-tagged)))
+                            (#f
+                             1)))
+                        (define (add-tagged exp)
+                          "Return a deconstruct-fold-state with an
+added association in tagged from the current counter to EXP.  If
+MAYBE-TAGGED is not empty, associations for its expressions are added
+to pre or their respective counter.  This should be used for
+expressions marked for translation with a simple keyword."
+                          (let ((c (counter-with-maybes)))
+                            (make-deconstruct-fold-state
+                             (alist-cons
+                              (prefix+counter prefix c)
+                              (quoting exp)
+                              (tagged-with-maybes))
+                             '()
+                             (1+ c))))
+                        (match exp
+                          (('quote inner-exp)
+                           (inner-loop inner-exp
+                                       (lambda (res)
+                                         (list 'quote res))))
+                          (('quasiquote inner-exp)
+                           (inner-loop inner-exp
+                                       (lambda (res)
+                                         (list 'quasiquote res))))
+                          (('unquote inner-exp)
+                           (inner-loop inner-exp
+                                       (lambda (res)
+                                         (list 'unquote res))))
+                          (('unquote-splicing inner-exp)
+                           (inner-loop inner-exp
+                                       (lambda (res)
+                                         (list 'unquote-splicing res))))
+                          (((? gettext-keyword?) . rest)
+                           (add-tagged exp))
+                          ((or (? symbol?) (? keyword?) (? list?))
+                           (add-maybe exp))
+                          ((? string?)
+                           ;; elements in maybe-tagged appear between strings
+                           (let ((c (counter-with-maybes)))
+                             (make-deconstruct-fold-state
+                              (tagged-with-maybes)
+                              '()
+                              c))))))))
+                 (make-deconstruct-fold-state '() '() #f)
+                 exp)))
+           (match tagged-state
+             (($ <deconstruct-fold-state> tagged maybe-tagged counter)
+              (assemble-parenthesized-expression
+               prefix
+               (match maybe-tagged
+                 (() tagged)
+                 (else ;associate maybe-tagged with pre or post
+                  (alist-cons
+                   (cond ;if there already is a pre, use post
+                    ((assoc-ref tagged (string-append prefix "pre"))
+                     (string-append prefix "post"))
+                    (else (string-append prefix "pre")))
+                   maybe-tagged
+                   tagged))))))))))
+      ((? string?) msgstr)
+      (else (error "Single symbol marked for translation." exp)))))
+
+;; NOTE: The sgettext macros have no hygiene because they use
+;; datum->syntax and do not preserve the semantics of anything looking
+;; like an sgettext macro.  This is an exceptional use case; do not
+;; try this at home.
+
+(define (sgettext x)
+  "After choosing an identifier for marking s-expressions for
+translation, make it usable by defining a macro with it calling
+sgettext.  If for example the chosen identifier is G_,
+use (define-syntax G_ sgettext)."
+  (syntax-case x ()
+    ((id exp)
+     (let* ((msgid (sexp->msgid (syntax->datum #'exp)))
+            (new-exp (deconstruct (syntax->datum #'exp)
+                                  (gettext msgid))))
+       (datum->syntax #'id new-exp)))))
+
+;; gettext’s share/gettext/gettext.h tells us we can prepend a msgctxt
+;; and #\eot before a msgid in a gettext call.
+
+(define (spgettext x)
+  "After choosing an identifier for behavior similar to pgettext:1c,2,
+make it usable like (define-syntax C_ spgettext)."
+  (syntax-case x ()
+    ((id msgctxt exp)
+     (let* ((gettext-context-glue #\eot) ;as defined in gettext.h
+            (lookup (string-append (syntax->datum #'msgctxt)
+                                   (string gettext-context-glue)
+                                   (sexp->msgid (syntax->datum #'exp))))
+            (msgstr (car (reverse (string-split (gettext lookup)
+                                                gettext-context-glue))))
+            (new-exp (deconstruct (syntax->datum #'exp)
+                                  msgstr)))
+       (datum->syntax #'id new-exp)))))
+
+(define %plural-numbers
+  ;; Hard-coded list of input numbers such that for each language’s
+  ;; plural formula, for each possible output grammatical number,
+  ;; there is an n among %plural-numbers that yields this output (for
+  ;; any language documented when running “info "(gettext) Plural
+  ;; forms"”), except 1 is omitted from this list because it is a
+  ;; special case for sngettext.  That is, calling ngettext with each
+  ;; number from %plural-numbers and with 1 in any locale is
+  ;; guaranteed to return each plural form at least once.  It would be
+  ;; more resilient towards new languages if instead of hard-coding we
+  ;; computed this from the Plural-Forms in the MO file header entry,
+  ;; but that is not worth the incurred code complexity.
+  '(0 2 3 11 100))
+
+(define (sngettext x)
+  "After choosing an identifier for behavior similar to ngettext:1,2,
+make it usable like (define-syntax N_ sngettext).  sngettext takes
+into account that not all languages have only singular and plural
+forms."
+  (syntax-case x ()
+    ((id exp1 exp2 n)
+     (let* ((msgid1 (sexp->msgid (syntax->datum #'exp1)))
+            (msgid2 (sexp->msgid (syntax->datum #'exp2)))
+            (msgstr1 (ngettext msgid1 msgid2 1))
+            (result (acons ;return an association list msgstr->deconstructed
+                     ;; msgstr for n=1:
+                     msgstr1
+                     `(,'unquote ,(deconstruct (syntax->datum #'exp1)
+                                               msgstr1))
+                     ;; other msgstr for n of each plural form:
+                     (map
+                      (lambda (n)
+                        (let ((msgstr (ngettext msgid1 msgid2 n)))
+                          (cons msgstr `(,'unquote
+                                         ,(deconstruct (syntax->datum #'exp2)
+                                                       msgstr)))))
+                      %plural-numbers))))
+       (datum->syntax
+        #'id
+        `(,assoc-ref (,'quasiquote ,result)
+                     (,ngettext ,msgid1 ,msgid2 ,(syntax->datum #'n))))))))
+
+(define (snpgettext x)
+  "After choosing an identifier for behavior similar to npgettext:1c,2,3,
+make it usable like (define-syntax NC_ snpgettext)."
+  (syntax-case x ()
+    ((id msgctxt exp1 exp2 n)
+     (let* ((gettext-context-glue #\eot) ;as defined in gettext.h
+            (msgid1 (string-append (syntax->datum #'msgctxt)
+                                   (string gettext-context-glue)
+                                   (sexp->msgid (syntax->datum #'exp1))))
+            ;; gettext.h implementation shows: msgctxt is only part of msgid1.
+            (msgid2 (sexp->msgid (syntax->datum #'exp2)))
+            (msgstr1 (car
+                      (reverse
+                       (string-split
+                        (ngettext msgid1 msgid2 1)
+                        gettext-context-glue))))
+            (result (acons ;return an association list msgstr->deconstructed
+                     ;; msgstr for n=1:
+                     msgstr1
+                     `(,'unquote ,(deconstruct (syntax->datum #'exp1)
+                                               msgstr1))
+                     ;; other msgstr for n of each plural form:
+                     (map
+                      (lambda (n)
+                        (let ((msgstr (car
+                                       (reverse
+                                        (string-split
+                                         (ngettext msgid1 msgid2 n)
+                                         gettext-context-glue)))))
+                          (cons msgstr `(,'unquote
+                                         ,(deconstruct (syntax->datum #'exp2)
+                                                       msgstr)))))
+                      %plural-numbers))))
+       (datum->syntax
+        #'id
+        `(,assoc-ref (,'quasiquote ,result)
+                     (,car
+                      (,reverse
+                       (,string-split
+                        (,ngettext ,msgid1 ,msgid2 ,(syntax->datum #'n))
+                        ,gettext-context-glue)))))))))
+
+(define %linguas
+  (with-input-from-file "po/LINGUAS"
+    (lambda _
+      (let loop ((line (read-line)))
+        (if (eof-object? line)
+            '()
+            ;; else read linguas before comment
+            (let ((before-comment (car (string-split line #\#))))
+              (append
+               (map match:substring (list-matches "[^ \t]+" before-comment))
+               (loop (read-line)))))))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]