Re: bug#34520: delete-matching-lines should report how many lines it deleted

classic Classic list List threaded Threaded
161 messages Options
1234 ... 9
Reply | Threaded
Open this post in threaded view
|

Re: bug#34520: delete-matching-lines should report how many lines it deleted

Richard Stallman
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

You wrote:

======================================================================
Here is an experimental but extensible implementation
that handles the case of formatting the recently added message
taking into account grammatical number of its argument:

  (defvar i18n-translations-hash (make-hash-table :test 'equal))

  (defun i18n-add-translation (_language-environment from to)
    (puthash from to i18n-translations-hash))

  (i18n-add-translation
   "English"
   "Deleted %d matching lines"
   (lambda (format-string count)
     (if (= count 1)
         "Deleted %d matching line"
         "Deleted %d matching lines")))

  (defun i18n-get-translation (format-string &rest args)
    (pcase (gethash format-string i18n-translations-hash)
      ((and (pred functionp) f) (apply f format-string args))
      ((and (pred stringp) s) s)
      (_ format-string)))

  (advice-add 'message :around
              (lambda (orig-fun format-string &rest args)
                (apply orig-fun (apply 'i18n-get-translation format-string args) args))
              '((name . message-i18n)))
======================================================================

It seems pretty good.  When installing it, it should not use
`advice-add'.  Rather, `message' should call a list of functions.

--
Dr Richard Stallman
President, Free Software Foundation (https://gnu.org, https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)



Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Eli Zaretskii
> From: Richard Stallman <[hidden email]>
> Date: Sat, 02 Mar 2019 22:04:06 -0500
> Cc: [hidden email]
>
>   (advice-add 'message :around
>               (lambda (orig-fun format-string &rest args)
>                 (apply orig-fun (apply 'i18n-get-translation format-string args) args))
>               '((name . message-i18n)))
> ======================================================================
>
> It seems pretty good.  When installing it, it should not use
> `advice-add'.  Rather, `message' should call a list of functions.

This has come up several times in the past.  The main problem with
i18n in Emacs is that, unlike in many text-mode programs, 'message'
covers a tiny portion of the Emacs UI.  We have help commands that pop
up buffers; we have commands that prompt in the minibuffer; we have
menu items and labels on tool-bar buttons; we have help-echo on menus,
tool bar, the mode line, and mouse-sensitive text; we have tooltips;
etc. etc.  What's worse, most of the text shown by these features is
computed dynamically by the commands that display the text.

Any reasonably relevant i18n infrastructure for Emacs should address
at least some of the above.  For example, a significant progress could
be made if we had infrastructure for translating doc strings, which
would allow translators to provide message catalogs for individual
Lisp packages.  Past discussions revealed that even this limited
progress is not really trivial.

Unfortunately, past discussions didn't lead to any significant
progress wrt this.  While doing some progress would be welcome, I
suggest that we don't pretend the solution is as easy as advice around
'message', but instead try to attack the more significant parts of the
problem.

Volunteers are welcome.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Juri Linkov-2
>> It seems pretty good.  When installing it, it should not use
>> `advice-add'.  Rather, `message' should call a list of functions.
>
> Unfortunately, past discussions didn't lead to any significant
> progress wrt this.

My intention was to fix the bug which manifests itself in
grammatically incorrect sentences displayed by ‘message’ like

  Deleted 1 matching lines
  1 matches found
  ...

After searching for available packages I found only this page
https://savannah.nongnu.org/projects/emacs-i18n
that shows no progress for many years.

So here is a patch that fixes the bug by translating currently
invalid messages into grammatically correct English.  It also
opens the gate towards translation of messages in many languages.
Currently this feature is activated by (require 'i18n-message):

diff --git a/lisp/replace.el b/lisp/replace.el
index 59ad1a375b..b05bb51353 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -986,6 +986,12 @@ flush-lines
     (when interactive (message "Deleted %d matching lines" count))
     count))
 
+(eval-after-load "i18n-message"
+  '(i18n-add-translation "English"
+                         "Deleted %d matching lines"
+                         '("Deleted %d matching line"
+                           "Deleted %d matching lines")))
+
 (defun how-many (regexp &optional rstart rend interactive)
   "Print and return number of matches for REGEXP following point.
 When called from Lisp and INTERACTIVE is omitted or nil, just return
@@ -1032,11 +1038,15 @@ how-many
  (if (= opoint (point))
     (forward-char 1)
   (setq count (1+ count))))
-      (when interactive (message "%d occurrence%s"
- count
- (if (= count 1) "" "s")))
+      (when interactive (message "%d occurrences" count))
       count)))
 
+(eval-after-load "i18n-message"
+  '(i18n-add-translation "English"
+                         "%d occurrences"
+                         '("%d occurrence"
+                           "%d occurrences")))
+
 
 (defvar occur-menu-map
   (let ((map (make-sparse-keymap)))
@@ -2730,10 +2740,7 @@ perform-replace
                                            (1+ num-replacements))))))
                              (when (and (eq def 'undo-all)
                                         (null (zerop num-replacements)))
-                               (message "Undid %d %s" num-replacements
-                                        (if (= num-replacements 1)
-                                            "replacement"
-                                          "replacements"))
+                               (message "Undid %d replacements" num-replacements)
                                (ding 'no-terminate)
                                (sit-for 1)))
    (setq replaced nil last-was-undo t last-was-act-and-show nil)))
@@ -2859,9 +2866,8 @@ perform-replace
                       last-was-act-and-show     nil))))))
       (replace-dehighlight))
     (or unread-command-events
- (message "Replaced %d occurrence%s%s"
+ (message "Replaced %d occurrences%s"
  replace-count
- (if (= replace-count 1) "" "s")
  (if (> (+ skip-read-only-count
    skip-filtered-count
    skip-invisible-count)
@@ -2883,6 +2889,16 @@ perform-replace
    "")))
     (or (and keep-going stack) multi-buffer)))
 
+(eval-after-load "i18n-message"
+  '(i18n-add-translations
+    "English"
+    '(("Undid %d replacements"
+       ("Undid %d replacement"
+        "Undid %d replacements"))
+      ("Replaced %d occurrences%s"
+       ("Replaced %d occurrence%s"
+        "Replaced %d occurrences%s")))))
+
 (provide 'replace)
 
 ;;; replace.el ends here
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index 3fd2a7e701..d2d748fca3 100644
--- a/lisp/progmodes/grep.el
+++ b/lisp/progmodes/grep.el
@@ -459,7 +459,7 @@ grep-mode-font-lock-keywords
      ;; remove match from grep-regexp-alist before fontifying
      ("^Grep[/a-zA-z]* started.*"
       (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t))
-     ("^Grep[/a-zA-z]* finished with \\(?:\\(\\(?:[0-9]+ \\)?matches found\\)\\|\\(no matches found\\)\\).*"
+     ("^Grep[/a-zA-z]* finished with \\(?:\\(\\(?:[0-9]+ \\)?match\\(?:es\\)? found\\)\\|\\(no matches found\\)\\).*"
       (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
       (1 compilation-info-face nil t)
       (2 compilation-warning-face nil t))
@@ -561,6 +561,12 @@ grep-exit-message
      (cons msg code)))
     (cons msg code)))
 
+(eval-after-load "i18n-message"
+  '(i18n-add-translation "English"
+                         "finished with %d matches found\n"
+                         '("finished with %d match found\n"
+                           "finished with %d matches found\n")))
+
 (defun grep-filter ()
   "Handle match highlighting escape sequences inserted by the grep process.
 This function is called from `compilation-filter-hook'."
diff --git a/lisp/international/i18n-message.el b/lisp/international/i18n-message.el
new file mode 100644
index 0000000000..14755966e0
--- /dev/null
+++ b/lisp/international/i18n-message.el
@@ -0,0 +1,118 @@
+;;; i18n-message.el --- internationalization of messages  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+
+;; Author: Juri Linkov <[hidden email]>
+;; Maintainer: [hidden email]
+;; Keywords: i18n, multilingual
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(defcustom i18n-fallbacks
+  '(("en" "English"))
+  "An alist mapping the current language to possible fallbacks.
+Each element should look like (\"LANG\" . FALLBACK-LIST), where
+FALLBACK-LIST is a list of languages to try to find a translation."
+  :type '(alist :key-type (string :tag "Current language")
+                :value-type (repeat :tag "A list of fallbacks" string))
+  :group 'i18n
+  :version "27.1")
+
+(defvar i18n-dictionaries (make-hash-table :test 'equal))
+
+(defun i18n-add-dictionary (lang)
+  (unless (gethash lang i18n-dictionaries)
+    (puthash lang (make-hash-table :test 'equal) i18n-dictionaries)))
+
+;;;###autoload
+(defun i18n-add-translation (lang from to)
+  (let ((dict (gethash lang i18n-dictionaries)))
+    (unless dict
+      (setq dict (i18n-add-dictionary lang)))
+    (puthash from to dict)))
+
+;;;###autoload
+(defun i18n-add-translations (lang translations)
+  (dolist (translation translations)
+    (i18n-add-translation lang (nth 0 translation) (nth 1 translation))))
+
+(defun i18n-get-plural (lang n)
+  ;; Source: (info "(gettext) Plural forms")
+  (pcase lang
+    ((or "Japanese" "Vietnamese" "Korean" "Thai")
+     0)
+    ((or "English" "German" "Dutch" "Swedish" "Danish" "Norwegian"
+         "Faroese" "Spanish" "Portuguese" "Italian" "Bulgarian" "Greek"
+         "Finnish" "Estonian" "Hebrew" "Bahasa Indonesian" "Esperanto"
+         "Hungarian" "Turkish")
+     (if (/= n 1) 1 0))
+    ((or "Brazilian Portuguese" "French")
+     (if (> n 1) 1 0))
+    ((or "Latvian")
+     (if (and (= (% n 10) 1) (/= (% n 100) 11)) 0 (if (/= n 0) 1 2)))
+    ((or "Gaeilge" "Irish")
+     (if (= n 1) 0 (if (= n 2) 1 2)))
+    ((or "Romanian")
+     (if (= n 1) 0 (if (or (= n 0) (and (> (% n 100) 0) (< (% n 100) 20))) 1 2)))
+    ((or "Lithuanian")
+     (if (and (= (% n 10) 1) (/= (% n 100) 11)) 0
+       (if (and (>= (% n 10) 2) (or (< (% n 100) 10) (>= (% n 100) 20))) 1 2)))
+    ((or "Russian" "Ukrainian" "Belarusian" "Serbian" "Croatian")
+     (if (and (= (% n 10) 1) (/= (% n 100) 11)) 0
+       (if (and (>= (% n 10) 2) (<= (% n 10) 4) (or (< (% n 100) 10) (>= (% n 100) 20))) 1 2)))
+    ((or "Czech" "Slovak")
+     (if (= n 1) 0 (if (and (>= n 2) (<= n 4)) 1 2)))
+    ((or "Polish")
+     (if (= n 1) 0
+       (if (and (>= (% n 10) 2) (<= (% n 10) 4) (or (< (% n 100) 10) (>= (% n 100) 20))) 1 2)))
+    ((or "Slovenian")
+     (if (= (% n 100) 1) 0 (if (= (% n 100) 2) 1 (if (or (= (% n 100) 3) (= (% n 100) 4)) 2 3))))
+    ((or "Arabic")
+     (if (= n 0) 0 (if (= n 1) 1 (if (= n 2) 2 (if (and (>= (% n 100) 3) (<= (% n 100) 10)) 3
+                                                 (if (>= (% n 100) 11) 4 5))))))))
+
+(defun i18n-get-translation (format-string &rest args)
+  (let* ((lang current-language-environment)
+ (fallbacks (cdr (assoc lang i18n-fallbacks)))
+ dict found)
+    (while (and (not found) lang)
+      (when (setq dict (gethash lang i18n-dictionaries))
+ (setq found
+              (pcase (gethash format-string dict)
+ ((and (pred functionp) f) (apply f format-string args))
+ ((and (pred stringp) s) s)
+ ((and (pred consp) l)
+ (let ((n (i18n-get-plural lang (car args))))
+   (when n (nth n l)))))))
+      (unless found
+ (setq lang (pop fallbacks))))
+    (or found format-string)))
+
+(defun i18n-message-translate (&rest args)
+  (apply 'i18n-get-translation args))
+
+(defvar message-translate-function)
+
+(setq message-translate-function 'i18n-message-translate)
+
+(provide 'i18n-message)
+;;; i18n-message.el ends here
diff --git a/src/editfns.c b/src/editfns.c
index bffb5db43e..f517679576 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -3050,6 +3050,14 @@ produced text.
 usage: (format STRING &rest OBJECTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
+  if (!NILP (Vmessage_translate_function) && nargs > 0)
+    {
+      Lisp_Object format = apply1 (Vmessage_translate_function,
+   Flist (nargs, args));
+      if (STRINGP (format))
+ args[0] = format;
+    }
+
   return styled_format (nargs, args, false);
 }
 
@@ -3066,6 +3074,14 @@ and right quote replacement characters are specified by
 usage: (format-message STRING &rest OBJECTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
+  if (!NILP (Vmessage_translate_function) && nargs > 0)
+    {
+      Lisp_Object format = apply1 (Vmessage_translate_function,
+   Flist (nargs, args));
+      if (STRINGP (format))
+ args[0] = format;
+    }
+
   return styled_format (nargs, args, true);
 }
 
@@ -4462,6 +4478,11 @@ of the buffer being accessed.  */);
 functions if all the text being accessed has this property.  */);
   Vbuffer_access_fontified_property = Qnil;
 
+  DEFVAR_LISP ("message-translate-function",
+       Vmessage_translate_function,
+       doc: /* Function that translates messages.  */);
+  Vmessage_translate_function = Qnil;
+
   DEFVAR_LISP ("system-name", Vsystem_name,
        doc: /* The host name of the machine Emacs is running on.  */);
   Vsystem_name = cached_system_name = Qnil;
Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Jean-Christophe Helary-3


On Mar 4, 2019, at 5:57, Juri Linkov <[hidden email]> wrote:

My intention was to fix the bug which manifests itself in
grammatically incorrect sentences displayed by ‘message’ like

 Deleted 1 matching lines
 1 matches found
 ...

The best way to do that (I fixed the almost 100% of the package.el code with that) is to not use such syntax but rather things like:

Number of matches found: %d


Jean-Christophe Helary
-----------------------------------------------
http://mac4translators.blogspot.com @brandelune


Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Richard Stallman
In reply to this post by Eli Zaretskii
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > This has come up several times in the past.  The main problem with
  > i18n in Emacs is that, unlike in many text-mode programs, 'message'
  > covers a tiny portion of the Emacs UI.  We have help commands that pop
  > up buffers; we have commands that prompt in the minibuffer; we have
  > menu items and labels on tool-bar buttons; we have help-echo on menus,

That is quite true.  However, I recommend a different approach to
doing the job.  An incremental one.

Let's install the lookup code and make `message' call it -- not using
advice.  Perhaps we should rewrite it into C, since it is short
and we will want to call it from C code.

Let's develop something to load translations from po files.  Let's
develop software to generate and write lists of messages that need
translating.

Then people can start developing useful sets of translations.

Meanwhile, we can also hook it into other interfaces where it
appropriate.

--
Dr Richard Stallman
President, Free Software Foundation (https://gnu.org, https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)



Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Eli Zaretskii
> From: Richard Stallman <[hidden email]>
> Cc: [hidden email], [hidden email]
> Date: Sun, 03 Mar 2019 22:27:36 -0500
>
> That is quite true.  However, I recommend a different approach to
> doing the job.  An incremental one.
>
> Let's install the lookup code and make `message' call it -- not using
> advice.  Perhaps we should rewrite it into C, since it is short
> and we will want to call it from C code.
>
> Let's develop something to load translations from po files.  Let's
> develop software to generate and write lists of messages that need
> translating.
>
> Then people can start developing useful sets of translations.
>
> Meanwhile, we can also hook it into other interfaces where it
> appropriate.

The incremental approach is a great approach, but it does have its
limitations.  Especially when several non-trivial features will
eventually need to be compatible with each other to be true parts of a
greater whole, which is i18n for Emacs.

For example, it is IMO pointless to be able to display translated
strings from 'message' without also having a convenient automated way
of collecting translatable messages and creating a message catalog
that such a 'message' could use, or without being able to install
such message catalogs for different ELisp packages.

IOW, this feature, like many other large features, cannot be
implemented in increments that are too small.  Each increment should
be large enough to make sense.  And then there's a more complex issue
of how the increments will work together; some thought must be
invested in that up front.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Paul Eggert
On 3/4/19 8:36 AM, Eli Zaretskii wrote:
> For example, it is IMO pointless to be able to display translated
> strings from 'message' without also having a convenient automated way
> of collecting translatable messages and creating a message catalog
> that such a 'message' could use

There is longstanding technology to do that for C code. We could apply
that to Emacs, and then at least the builtin C-level messages will be
translated. Later, we could extend this to Elisp.


Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Eli Zaretskii
> Cc: [hidden email], [hidden email]
> From: Paul Eggert <[hidden email]>
> Date: Mon, 4 Mar 2019 10:37:31 -0800
>
> On 3/4/19 8:36 AM, Eli Zaretskii wrote:
> > For example, it is IMO pointless to be able to display translated
> > strings from 'message' without also having a convenient automated way
> > of collecting translatable messages and creating a message catalog
> > that such a 'message' could use
>
> There is longstanding technology to do that for C code. We could apply
> that to Emacs, and then at least the builtin C-level messages will be
> translated. Later, we could extend this to Elisp.

I'm saying that IMO it makes no sense at all to do this only for C.
The infrastructure used for that will most probably not work for Lisp,
let alone allow separate translations for separate packages to be
brought together and used in the same Emacs session.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Paul Eggert
On 3/4/19 11:07 AM, Eli Zaretskii wrote:
> I'm saying that IMO it makes no sense at all to do this only for C.

Yes, of course it should also work for Elisp. I mentioned C only as a
way to get it started, since the C infrastructure already exists and we
need to do it for the C messages anyway.

> The infrastructure used for that will most probably not work for Lisp,
> let alone allow separate translations for separate packages to be
> brought together and used in the same Emacs session.

I don't see why it wouldn't work for Elisp. The gettext infrastructure
allows multiple message catalogs in the same session. Obviously some
hacking would be involved, since Elisp currently doesn't do any of this;
but it could be built atop the existing infrastructure used by other GNU
applications rather than being rewritten from scratch.


Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Richard Stallman
In reply to this post by Eli Zaretskii
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > I'm saying that IMO it makes no sense at all to do this only for C.
  > The infrastructure used for that will most probably not work for Lisp,
  > let alone allow separate translations for separate packages to be
  > brought together and used in the same Emacs session.

I tend to agree.  But I think that a first try for the infrastructure
in Lisp won't be hard, and will enable us to get volunteers involvesd
in contributing.

--
Dr Richard Stallman
President, Free Software Foundation (https://gnu.org, https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)



Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Eli Zaretskii
> From: Richard Stallman <[hidden email]>
> Cc: [hidden email], [hidden email], [hidden email]
> Date: Mon, 04 Mar 2019 21:49:19 -0500
>
> I think that a first try for the infrastructure in Lisp won't be
> hard, and will enable us to get volunteers involvesd in
> contributing.

I certainly hope so.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Juri Linkov-2
In reply to this post by Paul Eggert
>> The infrastructure used for that will most probably not work for Lisp,
>> let alone allow separate translations for separate packages to be
>> brought together and used in the same Emacs session.
>
> I don't see why it wouldn't work for Elisp. The gettext infrastructure
> allows multiple message catalogs in the same session. Obviously some
> hacking would be involved, since Elisp currently doesn't do any of this;
> but it could be built atop the existing infrastructure used by other GNU
> applications rather than being rewritten from scratch.

One of the main decisions that has to be made is whether to wrap all
user-facing translatable strings in all Lisp files using a macro/function
'gettext' (alias '_') explicitly like is implemented in XEmacs' I18N3
that would help to extract translations from the source code, or to use
a low-level implicit translation without changing the existing code like
is implemented for handling text-quoting-style in format strings.
The latter will even allow translation of strings that a package author
forgot to mark with '_'.

Depending on this decision a translation file format has to be selected,
be it flat Gettext PO format files or even some YAML-like hierarchical
Lisp structures with scopes.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted)

Richard Stallman
In reply to this post by Paul Eggert
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > I don't see why it wouldn't work for Elisp. The gettext infrastructure
  > allows multiple message catalogs in the same session.

Please try it and see.

If we need to replace gettext.c with Lisp code, that won't be hard,
When we come to that point, people will be enthusiastic about
translations and someone will do it.

What I think is crucial is to be compatible with the gettext
infrastructure for writign, maintaining and distributing translations.
Moving the code in Emacs to Lisp would not conflict with that.

--
Dr Richard Stallman
President, Free Software Foundation (https://gnu.org, https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)



Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Richard Stallman
In reply to this post by Juri Linkov-2
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > One of the main decisions that has to be made is whether to wrap all
  > user-facing translatable strings in all Lisp files using a macro/function
  > 'gettext' (alias '_') explicitly like is implemented in XEmacs' I18N3
  > that would help to extract translations from the source code, or to use
  > a low-level implicit translation without changing the existing code like
  > is implemented for handling text-quoting-style in format strings.
  > The latter will even allow translation of strings that a package author
  > forgot to mark with '_'.

We could recognize all strings passed as certain arguments to certain
functions as translatable automatically, and have an explicit
way to mark other strings as translatable.  That could reduce
the amount of work for developers to mark them.

The translatability could be recorded as a text property in the
string.  Then, if a function such as 'message' gets a format string
that is not translatable, it could warn, or save up a record that
developers could optionally look at later.  This would help remind
developers to mark the strings that need it.



--
Dr Richard Stallman
President, Free Software Foundation (https://gnu.org, https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)



Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Elias Mårtenson
In reply to this post by Jean-Christophe Helary-3
On Mon, 4 Mar 2019 at 09:48, Jean-Christophe Helary <[hidden email]> wrote:

The best way to do that (I fixed the almost 100% of the package.el code with that) is to not use such syntax but rather things like:

Number of matches found: %d

That works for English most of the time (although I would argue that it isn't great). But it may be harder in other languages.
Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Jean-Christophe Helary-3


> On Mar 6, 2019, at 18:38, Elias Mårtenson
>
> The best way to do that (I fixed the almost 100% of the package.el code with that) is to not use such syntax but rather things like:
>
> Number of matches found: %d
>
> That works for English most of the time (although I would argue that it isn't great).

I'm not sure why that is not "great" here. But I know from what I saw in packages.el that what is even less great it to get lost in lisp that attempts to mimic natural language inflections.

> But it may be harder in other languages.

It is unlikely that this structure is harder in non English languages than the original. Removing the need to express the difference in number actually removes an order of complexity.

Jean-Christophe Helary
-----------------------------------------------
http://mac4translators.blogspot.com @brandelune



Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Eli Zaretskii
In reply to this post by Juri Linkov-2
> From: Juri Linkov <[hidden email]>
> Cc: Eli Zaretskii <[hidden email]>,  [hidden email],  [hidden email]
> Date: Tue, 05 Mar 2019 23:58:25 +0200
>
> One of the main decisions that has to be made is whether to wrap all
> user-facing translatable strings in all Lisp files using a macro/function
> 'gettext' (alias '_') explicitly like is implemented in XEmacs' I18N3
> that would help to extract translations from the source code, or to use
> a low-level implicit translation without changing the existing code like
> is implemented for handling text-quoting-style in format strings.
> The latter will even allow translation of strings that a package author
> forgot to mark with '_'.

I'd encourage people who want or consider working on this to read past
discussions about related topics.  Some very important conclusions and
ideas came out of those discussions, and it would be a pity if we'd
need to reiterate all of what was already said and argued time and
again, instead of starting from where those past discussions ended.

Significant discussions of this happened in Dec 2001, in July 2007,
and lately in Apr 2017.  Some of those are quite long, but please do
read them, even if you were part of those discussions.  This current
discussion will be much more fruitful if we first recollect what we
already talked over.

> Depending on this decision a translation file format has to be selected,
> be it flat Gettext PO format files or even some YAML-like hierarchical
> Lisp structures with scopes.

The first alternative we should consider is to use the PO format,
because that's what translation teams out there are used to work with.
If it turns out that we cannot use the PO format for some good reasons
(which will have to be very good), we can consider other formats, but
translation teams will be in general very unhappy about that.

And I think these technicalities are not the first, let alone main,
decisions we must make.  They are important, but there are more
important and complex problems we need to address first.  I will talk
about this separately.

Thanks.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Eli Zaretskii
In reply to this post by Juri Linkov-2
> From: Juri Linkov <[hidden email]>
> Cc: Eli Zaretskii <[hidden email]>,  [hidden email],  [hidden email]
> Date: Tue, 05 Mar 2019 23:58:25 +0200
>
> One of the main decisions that has to be made is whether to wrap all
> user-facing translatable strings in all Lisp files using a macro/function
> 'gettext'

First, AFAIR the conclusion back when this was discussed was that we
might not need to mark the translatable strings, because almost all of
them should be translatable.  If anything, we might consider marking
strings that do NOT need to be translated, as they are a very small
minority.  Just look at the strings in a typical Emacs source file and
try to find strings that you wouldn't want translated.  Unlike some
other programs, Emacs almost never says something that is not meant to
be read and understood by the user.

Second, I don't understand why we are still talking about 'message'.
Most of the user interaction in Emacs that will benefit the most from
translation is not messages we show in the echo area: Emacs actually
doesn't chatter there too much.  Most of the stuff that IMO is much
more important to have translated are the doc strings.  It's no
coincidence that Emacs has around 5000 calls to 'message', but almost
50000 doc strings, 10 times more than echo-area messages.  So even if
we do decide to attack the 'message' part first, we should consider
the doc strings as well, so that whatever infrastructure we develop
for messages will work for doc strings as well.  And that adds more
issues that the basic design must solve or be capable of solving.

Then there are some seemingly minor technical issues, but I think
Emacs will force us to deal with them up front, because Emacs is so
much different from a typical localized text-mode program.  Some of
the issues that came up in the past:

 . Do we use a separate message catalog for each Lisp package, or a
   single catalog for all of Emacs?  Each alternative has its merits
   and demerits.  For example, if we go with separate catalogs, then
   how do we make the correct bindtextdomain call, given that packages
   call each other?  If we go for a single catalog, how do we support
   installing and loading a new package without exiting Emacs?

 . How to specify which target language to use?  The locale is not
   necessarily correct, e.g., when editing with Tramp.  Also, since
   translating all of Emacs is such a humongous job, it's quite
   possible that some languages will have little or no translations,
   and the respective users might want to use translations for a
   "fallback" language, which they prefer to English.

 . Many user-facing text messages include portions that we generate
   directly from symbol names, which are of course in English.  We
   should have some idea for how to deal with that.

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Eli Zaretskii
In reply to this post by Richard Stallman
> From: Richard Stallman <[hidden email]>
> Cc: [hidden email], [hidden email], [hidden email]
> Date: Tue, 05 Mar 2019 21:16:07 -0500
>
> We could recognize all strings passed as certain arguments to certain
> functions as translatable automatically, and have an explicit
> way to mark other strings as translatable.  That could reduce
> the amount of work for developers to mark them.

You mean, the function, such as 'message', that receives the string
will translate it?  As opposed to the alternative of translating the
string _before_ it gets passed to the function?

If we do that, how do we deal with strings that are computed by
concatenation or formatting?  They get in one piece to functions like
'message', but the catalog will not hold that concatenated string, it
will have the parts separately.  How will the function be able to look
up the translation in such cases?

Reply | Threaded
Open this post in threaded view
|

Re: Emacs i18n

Paul Eggert
In reply to this post by Eli Zaretskii
On 3/6/19 10:09 AM, Eli Zaretskii wrote:
> we might consider marking
> strings that do NOT need to be translated, as they are a very small
> minority.  Just look at the strings in a typical Emacs source file and
> try to find strings that you wouldn't want translated.  Unlike some
> other programs, Emacs almost never says something that is not meant to
> be read and understood by the user.

My impression is just the opposite. Of course it depends on the module,
but I just now took a census of todo-mode.el (which I happened to be
editing anyway) and looked at the first 300 lines of source code (at
which point I got tired of counting). I counted 24 strings that should
not be translated, and 5 strings that should be. (I did not count doc
strings, which obviously should all be translated and shouldn't need to
be marked.)

Here are the strings needing translation:

"==--== DONE "
"DONE "
"Invalid value: must be distinct from `todo-item-mark'"
%s category %d: %s"
"Invalid value: must be a positive integer"

and here are the strings that don't need translation:

"todo/"
"\\.toda\\'"
"\\.todo\\'"
"--==-- "
"="
"["
"]"
"*"
"\\(?4:\\(?5:"
"\\)\\|"
"\\(?6:%s\\)"
"\\(?7:[0-9]+\\|\\*\\)"
"\\(?8:[0-9]+\\|\\*\\)"
"-?\\(?9:[0-9]+\\|\\*\\)"
""
"\\)"
"^\\("
"\\|"
"\\)?"
"^\\["
"\\("
"\\|"
"\\)"
""


1234 ... 9