emacs-devel
[Top][All Lists]
Advanced

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

Re: Internationalize Emacs's messages (swahili)


From: Daniel Brooks
Subject: Re: Internationalize Emacs's messages (swahili)
Date: Fri, 25 Dec 2020 22:06:15 -0800
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux)

Daniel Brooks <db48x@db48x.net> writes:

> Thus, each translation should provide a package full of functions rather
> than a file full of strings. It could literally be that simple, though I

As a concrete example of what I see in my head when I talk about this,
here's some code that generates a moderately complex message, which I
have taken from todo-mode.el:

    (let ((pl (> (length deleted) 1))
          (names (mapconcat (lambda (f) (concat "\"" f "\"")) deleted ", ")))
      (message (concat "File" (if pl "s" "") " %s ha" (if pl "ve" "s")
                       " been deleted and removed from\n"
                       "the list of category completion files")
               names))

I think that the best way to factor this code is this:

    (message (todo-msg-delete-from-category-completion-files deleted))

Basically, todo-msg-delete-from-category-completion-files is a new
function which bundles up all the work of creating the message to be
displayed. We pass it the list of deleted files, and it inspects the
user's preferences and does all the rest of the work. The naming scheme
is just what I came up with on the fly; it seems to me that it would
work, but it's a detail we should work out.

An obvious implementation of this function is this:

    (defun todo-msg-delete-from-category-completion-files (names)
        (let ((name (intern (concat 
"todo-msg-delete-from-category-completion-files-"
                                    (current-language-preference)))))
          (if (fboundp name)
              (funcall name names)
            (todo-msg-delete-from-category-completion-files-eng names))))

It just dispatches based on the user's currently-chosen language. The
function it dispatches to does the work of building or choosing a string
to return.  In those cases where we write code that should show a
message that is _not_ in the user's preferred language, we would call
the correct function ourselves. A good example of that would be the UI
that allows the user to choose a preferred language.

This could be included in todo-mode.el directly, but since todo-mode has
many messages to display, we would obviously want some macro for
declaring them in bulk. (That's another detail to be worked out.)

    (defun todo-msg-delete-from-category-completion-files-eng (names)
        (let ((category (plural-category-eng (length names)))
              (names (quoted-comma-sep names)))
          (format
           (cond ((eq :one category)
                  "File %s has been deleted and removed from\n the list of 
category completion files")
                 ((eq :other category)
                  "Files %s have been deleted and removed from\n the list of 
category completion files"))
           names)))

This is the one that does the real work. It chooses which string to use
based on the plural category (there are obviously briefer ways to write
this, but I wrote it this way for effect). It substitutes in the list of
files the normal way because english doesn't make us do anything odd for
that. (It would be nice if the formatted list had an "and" before the
final item though.)

This code could be written by hand with no trouble, and a translator who
knows Elisp could come in and supply a new group of functions that
implement the same interface but for their own language.

If we did the additional work of writing a Fluent→Elisp compiler, we
could write the same function something like this:

    delete-from-category-completion-files = { $names ->
        [one] File { quoted-comma-sep($names) } has been deleted and removed 
from\n the list of category completion files
       *[other] Files { quoted-comma-sep($names) } have been deleted and 
removed from\n the list of category completion files
    }

This is rather more succinct, and possible to teach to translators
without having to teach them all of elisp. We can compile this to Elisp
at build time, but I'd like to be able to load them dynamically as well.

The helper functions are all fairly obvious, and obviously incomplete:

    (defun plural-category-eng (count)
      (cond ((= count 1) :one)
            (t :other)))

    (defun current-language-preference ()
      "eng")

    (defun quoted-comma-sep (list)
      (mapconcat (lambda (f) (concat "\"" f "\"")) list ", "))

The current language preference could be a cusomizable variable, or some
other mechanism (a fallback to the LANG variable, for instance).

Each translation would have something like the quoted-comma-sep
function, though they might not all call it the same thing. This would
allow each translator to choose the most appropriate type of quotation
characters, list separators, conjunctions, and so on for the translation
that they are writing.

I think that all of this is doable in a reasonable amount of time, if
two or three people are working on it together. The broader work of
actually factoring messages out of the code and into translatable
functions would need to be spread out more broadly, but it could happen
over a longer period of time. It's a task of Herculean proportions, and
there is no conveniently-located river that we can divert.

It occurs to me that I haven't considered messages generated by C code
at all; I hope that they are few and far between, and that we can just
call back into lisp to generate those messages. There are bound to be
some messages that we just never end up translating.

db48x



reply via email to

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