From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.devel Subject: Re: Emacs i18n Date: Thu, 7 Mar 2019 09:19:35 -0800 Organization: UCLA Computer Science Department Message-ID: References: <87o97aq6gz.fsf@jidanni.org> <87tvgoud56.fsf@mail.linkov.net> <83o96wk2mi.fsf@gnu.org> <87k1hjfvjd.fsf@mail.linkov.net> <871s3p0zdz.fsf@mail.linkov.net> <83h8ckezyt.fsf@gnu.org> <83o96qegv1.fsf@gnu.org> <32b1ab1b-bef4-629a-8830-b1dcc6915087@cs.ucla.edu> <83a7iae9va.fsf@gnu.org> <05ed2dec-2a84-f7dc-1af5-c9d923992785@cs.ucla.edu> <87bm2p56gu.fsf@mail.linkov.net> <837edbdg33.fsf@gnu.org> <83o96mbv4l.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------BCFDBC9B2D1078D5309E4422" Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="104235"; mail-complaints-to="usenet@blaine.gmane.org" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.1 Cc: juri@linkov.net, emacs-devel@gnu.org To: Eli Zaretskii , rms@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Mar 07 18:20:32 2019 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1h1wh6-000R0v-MV for ged-emacs-devel@m.gmane.org; Thu, 07 Mar 2019 18:20:32 +0100 Original-Received: from localhost ([127.0.0.1]:55842 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h1wh5-0002DE-EB for ged-emacs-devel@m.gmane.org; Thu, 07 Mar 2019 12:20:31 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:35861) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1h1wgm-0002BV-3T for emacs-devel@gnu.org; Thu, 07 Mar 2019 12:20:13 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1h1wgh-0000e9-D7 for emacs-devel@gnu.org; Thu, 07 Mar 2019 12:20:10 -0500 Original-Received: from zimbra.cs.ucla.edu ([131.179.128.68]:49290) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1h1wgW-0000MV-4X; Thu, 07 Mar 2019 12:19:57 -0500 Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 2CA671614CC; Thu, 7 Mar 2019 09:19:38 -0800 (PST) Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id xbXnMdZseRRu; Thu, 7 Mar 2019 09:19:36 -0800 (PST) Original-Received: from localhost (localhost [127.0.0.1]) by zimbra.cs.ucla.edu (Postfix) with ESMTP id 8EA901614E5; Thu, 7 Mar 2019 09:19:36 -0800 (PST) X-Virus-Scanned: amavisd-new at zimbra.cs.ucla.edu Original-Received: from zimbra.cs.ucla.edu ([127.0.0.1]) by localhost (zimbra.cs.ucla.edu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id X1-DbxRpAA0k; Thu, 7 Mar 2019 09:19:36 -0800 (PST) Original-Received: from Penguin.CS.UCLA.EDU (Penguin.CS.UCLA.EDU [131.179.64.200]) by zimbra.cs.ucla.edu (Postfix) with ESMTPSA id 6E83C161486; Thu, 7 Mar 2019 09:19:36 -0800 (PST) Openpgp: preference=signencrypt Autocrypt: addr=eggert@cs.ucla.edu; prefer-encrypt=mutual; keydata= xsFNBEyAcmQBEADAAyH2xoTu7ppG5D3a8FMZEon74dCvc4+q1XA2J2tBy2pwaTqfhpxxdGA9 Jj50UJ3PD4bSUEgN8tLZ0san47l5XTAFLi2456ciSl5m8sKaHlGdt9XmAAtmXqeZVIYX/UFS 96fDzf4xhEmm/y7LbYEPQdUdxu47xA5KhTYp5bltF3WYDz1Ygd7gx07Auwp7iw7eNvnoDTAl KAl8KYDZzbDNCQGEbpY3efZIvPdeI+FWQN4W+kghy+P6au6PrIIhYraeua7XDdb2LS1en3Ss mE3QjqfRqI/A2ue8JMwsvXe/WK38Ezs6x74iTaqI3AFH6ilAhDqpMnd/msSESNFt76DiO1ZK QMr9amVPknjfPmJISqdhgB1DlEdw34sROf6V8mZw0xfqT6PKE46LcFefzs0kbg4GORf8vjG2 Sf1tk5eU8MBiyN/bZ03bKNjNYMpODDQQwuP84kYLkX2wBxxMAhBxwbDVZudzxDZJ1C2VXujC OJVxq2kljBM9ETYuUGqd75AW2LXrLw6+MuIsHFAYAgRr7+KcwDgBAfwhPBYX34nSSiHlmLC+ KaHLeCLF5ZI2vKm3HEeCTtlOg7xZEONgwzL+fdKo+D6SoC8RRxJKs8a3sVfI4t6CnrQzvJbB n6gxdgCu5i29J1QCYrCYvql2UyFPAK+do99/1jOXT4m2836j1wARAQABzSBQYXVsIEVnZ2Vy dCA8ZWdnZXJ0QGNzLnVjbGEuZWR1PsLBfgQTAQIAKAUCTIByZAIbAwUJEswDAAYLCQgHAwIG FQgCCQoLBBYCAwECH In-Reply-To: <83o96mbv4l.fsf@gnu.org> Content-Language: en-US X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 131.179.128.68 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 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.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:233893 Archived-At: This is a multi-part message in MIME format. --------------BCFDBC9B2D1078D5309E4422 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On 3/7/19 6:46 AM, Eli Zaretskii wrote: >> From: Richard Stallman Cc: eggert@cs.ucla.edu, >> emacs-devel@gnu.org, juri@linkov.net Date: Wed, 06 Mar 2019 22:42:06 >> -0500 > If we do that, how do we deal with strings that are computed >> by > concatenation or formatting? Feed them in through %s or >> something like that. >> > But then the strings that are formatted via %s will not be translated, > they will remain in English. > Yes, but the scenario you describe should not occur in a properly internationalized GNU application. We obviously can't assume that Emacs's translation subroutine acts like Google Translate and can translate any English-language string to the user's language. All we can assume is that the translation subroutine converts one of a fixed set of English-language strings to a string appropriate for the user's language. This limitation will cause problems with Elisp code that does extensive parsing or processing of English syntax (doctor.el, say), and that sort of Elisp code will remain English-only (unless someone takes the time to i18nize them specially). However most Elisp code does not parse English or generate idiomatic English on the fly: instead, it uses a fixed, stilted style that can routinely be converted to calls like (message FORMAT ARG1 ARG2 ...) where FORMAT is translated and the ARG values are n= ot. To get a quick feel for this issue, I did a simple grep for the string '(message (concat' in the Emacs source code. I found 41 instances of this string. Of these, 8 were erroneous because the result of the concatenation could contain an unwanted "%" or '`' that could cause 'message' to go awry, and I fixed them by installing the attached patch (by the way, it's routine for i18n efforts to find trivial bugs like this). The other 33 instances could easily be reworded to do proper i18n when 'message' translates just its first argument and only simple, xgettext-style static analysis is used to find the message strings. For example, this code in calc-do-embedded: =C2=A0 (message (concat =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 "Embed= ded Calc mode enabled; " =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (if ca= lc-embedded-quiet =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0 "Type `C-x * x'" =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0 "Give this command again") =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 " to r= eturn to normal")) can easily be rewritten to this: =C2=A0=C2=A0 (if calc-embedded-quiet =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 "Embedded Calc mode enabled; Type `C= -x * x' to return to normal" =C2=A0=C2=A0=C2=A0=C2=A0 "Embedded Calc mode enabled; Give this command a= gain to return to normal")) which is easier for translators to grok, and is arguably clearer even if we don't want to translate at all.Obviously my '(message (concat' exercises only a small part of the problem, but the results of this little sample are encouraging. So this problem is solvable. Sure, it'll require substantial work, but the work is routine and this sort of thing has been done for other packag= es. The main argument against doing all this is that it's too much work overall and nobody will have the time to do it all, so let's not even bother. I have some sympathy for this argument, as i18n is clearly too much work for any single contributor and the work will distract us from other things. On the other hand, there's no pressing need to do all the work quickly, it's a low-level task that can be farmed out to non-expert volunteers that could conceivably grow the volunteer population, and if we never even start the work then it will never get done and Emacs will remain unfriendly to users who don't grok English. --------------BCFDBC9B2D1078D5309E4422 Content-Type: text/x-patch; name="0001-Be-safer-about-in-message-formats.patch" Content-Disposition: attachment; filename="0001-Be-safer-about-in-message-formats.patch" Content-Transfer-Encoding: quoted-printable >From f15d0d0247ffe7bc3bbd5fbe10271c93b2e2fb1c Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 7 Mar 2019 09:02:15 -0800 Subject: [PATCH] Be safer about "%" in message formats MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit * lisp/calc/calc-store.el (calc-copy-special-constant): * lisp/net/rcirc.el (rcirc-handler-PART, rcirc-handler-KICK): * lisp/org/org-agenda.el (org-agenda): * lisp/org/org-clock.el (org-clock-out, org-clock-display): * lisp/org/org.el (org-refile): * lisp/progmodes/ada-xref.el (ada-goto-declaration): * lisp/progmodes/idlwave.el (idlwave-scan-library-catalogs): Don=E2=80=99t trust arbitrary strings to not contain "%" or "`" in (message (concat STRING1 STRING2 ...)). --- lisp/calc/calc-store.el | 4 ++-- lisp/net/rcirc.el | 4 ++-- lisp/org/org-agenda.el | 13 ++++++------- lisp/org/org-clock.el | 22 ++++++++++++---------- lisp/org/org.el | 3 ++- lisp/progmodes/ada-xref.el | 3 +-- lisp/progmodes/idlwave.el | 7 +++---- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/lisp/calc/calc-store.el b/lisp/calc/calc-store.el index 589a776c41..3987c129c2 100644 --- a/lisp/calc/calc-store.el +++ b/lisp/calc/calc-store.el @@ -405,8 +405,8 @@ calc-copy-special-constant sconst)))) (if var (let ((msg (calc-store-value var value ""))) - (message (concat "Special constant \"%s\" copied to \"%s\""= msg) - sconst (calc-var-name var))))))))) + (message "Special constant \"%s\" copied to \"%s\"%s" + sconst (calc-var-name var) msg)))))))) =20 (defun calc-copy-variable (&optional var1 var2) (interactive) diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el index b1a6c1ce8d..9d53cd4436 100644 --- a/lisp/net/rcirc.el +++ b/lisp/net/rcirc.el @@ -2685,7 +2685,7 @@ rcirc-handler-PART-or-KICK (defun rcirc-handler-PART (process sender args _text) (let* ((channel (car args)) (reason (cadr args)) - (message (concat channel " " reason))) + (message "%s %s" channel reason)) (rcirc-print process sender "PART" channel message) ;; print in private chat buffer if it exists (when (rcirc-get-buffer (rcirc-buffer-process) sender) @@ -2697,7 +2697,7 @@ rcirc-handler-KICK (let* ((channel (car args)) (nick (cadr args)) (reason (nth 2 args)) - (message (concat nick " " channel " " reason))) + (message "%s %s %s" nick channel reason)) (rcirc-print process sender "KICK" channel message t) ;; print in private chat buffer if it exists (when (rcirc-get-buffer (rcirc-buffer-process) nick) diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index e416f5f062..23ee8d71e6 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el @@ -2882,13 +2882,12 @@ org-agenda (let* ((m (org-agenda-get-any-marker)) (note (and m (org-entry-get m "THEFLAGGINGNOTE")))) (when note - (message (concat - "FLAGGING-NOTE ([?] for more info): " - (org-add-props - (replace-regexp-in-string - "\\\\n" "//" - (copy-sequence note)) - nil 'face 'org-warning))))))) + (message "FLAGGING-NOTE ([?] for more info): %s" + (org-add-props + (replace-regexp-in-string + "\\\\n" "//" + (copy-sequence note)) + nil 'face 'org-warning)))))) t t)) ((equal org-keys "#") (call-interactively 'org-agenda-list-stuck-= projects)) ((equal org-keys "/") (call-interactively 'org-occur-in-agenda-fi= les)) diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el index 34b694d487..62c7cd92d1 100644 --- a/lisp/org/org-clock.el +++ b/lisp/org/org-clock.el @@ -1622,9 +1622,10 @@ org-clock-out "\\>")))) (org-todo org-clock-out-switch-to-state)))))) (force-mode-line-update) - (message (concat "Clock stopped at %s after " - (org-duration-from-minutes (+ (* 60 h) m)) "%s") - te (if remove " =3D> LINE REMOVED" "")) + (message (if remove + "Clock stopped at %s after %s =3D> LINE REMOVED" + "Clock stopped at %s after %s") + te (org-duration-from-minutes (+ (* 60 h) m))) (run-hooks 'org-clock-out-hook) (unless (org-clocking-p) (setq org-clock-current-task nil))))))) @@ -1925,13 +1926,14 @@ org-clock-display nil 'local)))) (let* ((h (/ org-clock-file-total-minutes 60)) (m (- org-clock-file-total-minutes (* 60 h)))) - (message (concat (format "Total file time%s: " - (cond (todayp " for today") - (customp " (custom)") - (t ""))) - (org-duration-from-minutes - org-clock-file-total-minutes) - " (%d hours and %d minutes)") + (message (cond + (todayp + "Total file time for today: %s (%d hours and %d minutes)") + (customp + "Total file time (custom): %s (%d hours and %d minutes)") + (t + "Total file time: %s (%d hours and %d minutes)")) + (org-duration-from-minutes org-clock-file-total-minutes) h m)))) =20 (defvar-local org-clock-overlays nil) diff --git a/lisp/org/org.el b/lisp/org/org.el index 3a434d12df..e3c78ae90d 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el @@ -11878,7 +11878,8 @@ org-refile (when (featurep 'org-inlinetask) (org-inlinetask-remove-END-maybe)) (setq org-markers-to-move nil) - (message (concat actionmsg " to \"%s\" in file %s: done") (car it) = file))))))) + (message "%s to \"%s\" in file %s: done" actionmsg + (car it) file))))))) =20 (defun org-refile-goto-last-stored () "Go to the location where the last refile was stored." diff --git a/lisp/progmodes/ada-xref.el b/lisp/progmodes/ada-xref.el index 28c52b0653..c9c923e1d6 100644 --- a/lisp/progmodes/ada-xref.el +++ b/lisp/progmodes/ada-xref.el @@ -1133,8 +1133,7 @@ ada-goto-declaration (ada-find-in-ali identlist other-frame) ;; File not found: print explicit error message (ada-error-file-not-found - (message (concat (error-message-string err) - (nthcdr 1 err)))) + (message "%s%s" (error-message-string err) (nthcdr 1 err))) =20 (error (let ((ali-file (ada-get-ali-file-name (ada-file-of identlist)))) diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el index 476d935e8a..25bc788ffc 100644 --- a/lisp/progmodes/idlwave.el +++ b/lisp/progmodes/idlwave.el @@ -5588,7 +5588,7 @@ idlwave-scan-library-catalogs (mapcar 'car idlwave-path-alist))) (old-libname "") dir-entry dir catalog all-routines) - (if message-base (message message-base)) + (if message-base (message "%s" message-base)) (while (setq dir (pop dirs)) (catch 'continue (when (file-readable-p @@ -5603,8 +5603,7 @@ idlwave-scan-library-catalogs message-base (not (string=3D idlwave-library-catalog-libname old-libname))) - (message "%s" (concat message-base - idlwave-library-catalog-libname)) + (message "%s%s" message-base idlwave-library-catalog-libname) (setq old-libname idlwave-library-catalog-libname)) (when idlwave-library-catalog-routines (setq all-routines @@ -5618,7 +5617,7 @@ idlwave-scan-library-catalogs (setq dir-entry (assoc dir idlwave-path-alist))) (idlwave-path-alist-add-flag dir-entry 'lib))))) (unless no-load (setq idlwave-library-catalog-routines all-routine= s)) - (if message-base (message (concat message-base "done")))))) + (if message-base (message "%sdone" message-base))))) =20 ;;----- Communicating with the Shell ------------------- =20 --=20 2.20.1 --------------BCFDBC9B2D1078D5309E4422--