From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Daniel Brooks Newsgroups: gmane.emacs.devel Subject: Re: Internationalize Emacs's messages (swahili) Date: Fri, 25 Dec 2020 22:06:15 -0800 Message-ID: <87y2hlt82w.fsf@db48x.net> References: <87o8ivumn5.fsf@telefonica.net> <87v9d3nkxk.fsf@gnus.org> <83sg7xrgr5.fsf@gnu.org> <83h7odrdwy.fsf@gnu.org> <86sg7w39fh.fsf@163.com> <83pn30pku5.fsf@gnu.org> <86wnx8otoj.fsf@163.com> <834kkbp9vr.fsf@gnu.org> <87czyxuxw6.fsf@db48x.net> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="31231"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) Cc: rms@gnu.org, bugs@gnu.support, dimech@gmx.com, abrochard@gmx.com, emacs-devel@gnu.org, Zhu Zihao To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Dec 26 07:07:27 2020 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kt2jf-00080j-Ab for ged-emacs-devel@m.gmane-mx.org; Sat, 26 Dec 2020 07:07:27 +0100 Original-Received: from localhost ([::1]:32836 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kt2je-0001sr-D6 for ged-emacs-devel@m.gmane-mx.org; Sat, 26 Dec 2020 01:07:26 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:48330) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kt2ie-0001Rz-4y for emacs-devel@gnu.org; Sat, 26 Dec 2020 01:06:24 -0500 Original-Received: from smtp-out-4.mxes.net ([198.205.123.69]:58465) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kt2ia-000385-PL for emacs-devel@gnu.org; Sat, 26 Dec 2020 01:06:23 -0500 Original-Received: from Customer-MUA (mua.mxes.net [10.0.0.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.mxes.net (Postfix) with ESMTPSA id 1B8EE75983; Sat, 26 Dec 2020 01:06:16 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mxes.net; s=mta; t=1608962779; bh=+9wkJAJx/tQjmWTSVxiI7lIQeGGlJrYMQo82p7b4l2c=; h=From:To:Subject:References:Date:In-Reply-To:Message-ID: MIME-Version:Content-Type; b=fctmiiq31uXRCrXOPI9qU/HHvUvsKaomY5tsAjJ7U5LUO0kpd/bdcYBDwZkm6TnJO VeWsz4dOGlpeHz/6N3UimF2mkljPcMd8U7qJj7jmw3WqrJBvsHS6ZVfIubd5GAZwRt auTWf+YXjfTX2PMPurioryg7ag3l3w1zvQxUNdBo= Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAABGdBTUEAALGOfPtRkwAAABJQ TFRFpKfbdou67PD6JjJgAwUWXGSeIcyLHgAAAkZJREFUOI1VU8Fy6yAMxLi+Q13fCZ3cnQL3dqTc 7RD+/1feStDXVnXHDuvVSivZTMba2GPdw3gyCGcMAFxTyrTd9dwGoxHiZX9PmRFUHYAQlGGtXY+F Uk0SJOxgJiUEnH1qkitT9D+pQub7qGAmUbR6bu3CvI96Yv6QqkBBMrsyfZccr1/RDXGDTLf4P7ZY glVxe2V+/ACXWO1gvDO9/gDRpFFVmPluvLcmBjd5H6d8DEte+Pbk4rcY/Fa5tLKLOtCZsuQKYhpa LOkYDT7hESya7/WIET3lfQBqX0pwFtbI832Is0ayMUR9B+12xjgPCQ089cfwkCkX6L5TPmRelJTh zMS0Sz1PyjLAMCUWjcmgQLWQMds+e3aaauZDf9dU9A2/8kPVF2odCUoMKHkfjJR+mbgC+DRiycw5 3XSqGe6HmhN/AWjHypkAXOAFW5EiuA1ge2GiZuMb0s1fSEXcATeLUfbyEY2L8yPOmdSsdghQXx3K pz2eoeXuYvMCINVFDrCdNfVUp4eJ6cSEbjbgFjBEvonGGTrgv9cHjAc8aVgSAPoxaONbzfwhDIhR at7IIS7fAGiDSwIA9alhhTBzfA7YM2FY6eMwayrIGK8FDFmshmUA43WqhFtpvoqG9HHaJ7fqtgTz 8EWVkgZgtsylFliHDgk0MB7KAEC45C/rgnGvanNLXyzOeTzcT2nw/N44gfrtYXRQLoz9Q3TgmJRx 2Mx/Q51qzpm+l3m8z2SWBqC5+PZXAtNYlGFf/gKfHfjFkDT4x7od7R+w3Ls+ZdQBuQAAAABJRU5E rkJggg== In-Reply-To: <87czyxuxw6.fsf@db48x.net> (Daniel Brooks's message of "Fri, 25 Dec 2020 18:03:21 -0800") X-Sent-To: Received-SPF: none client-ip=198.205.123.69; envelope-from=db48x@db48x.net; helo=smtp-out-4.mxes.net X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_NONE=0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:261787 Archived-At: Daniel Brooks 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-completi= on-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 o= f 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=E2=86=92Elisp compiler, we could write the same function something like this: delete-from-category-completion-files =3D { $names -> [one] File { quoted-comma-sep($names) } has been deleted and remove= d from\n the list of category completion files *[other] Files { quoted-comma-sep($names) } have been deleted and re= moved 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 ((=3D 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