From: Juri Linkov <juri@linkov.net>
To: emacs-devel <emacs-devel@gnu.org>
Subject: Re: Emacs i18n
Date: Sun, 17 Mar 2019 23:23:03 +0200 [thread overview]
Message-ID: <87va0htcss.fsf@mail.linkov.net> (raw)
In-Reply-To: <87d0mvu6wo.fsf@mail.linkov.net> (Juri Linkov's message of "Tue, 12 Mar 2019 23:45:07 +0200")
[-- Attachment #1: Type: text/plain, Size: 2627 bytes --]
>>> Please note that you have to handle not only format-strings of
>>> ‘message’, but also ‘error’ and even more low-level ‘format’, i.e. all
>>> these (error STRING &rest ARGS) (message FORMAT-STRING &rest ARGS)
>>> (format-message STRING &rest OBJECTS) (format STRING &rest OBJECTS)
>>>
>> I expect that 'format' won't translate its first argument, whereas
>> 'error', 'message', and 'format-message' will. This will be for the same
>> reason that 'format' does not translate quotes.
>
> Then it should be sufficient to add a gettext call to 'format-message' only,
> because all other related functions 'message', 'error', 'tramp-message',
> 'tramp-error', etc. all they use 'format-message' directly or indirectly.
Maybe I'm too stupid to comprehend the complexity of this task in its entirety,
but I tried to install gettext infrastructure in Emacs with gettextize,
and then tried to run xgettext on source code, and see no technical problems.
What I tried is to run this command, and it extracts all messages:
xgettext --from-code=UTF-8 -kformat-message -kmessage -kerror -ktramp-message -ktramp-error *.el
then this command extracts all Gnus messages into a separate file:
xgettext --from-code=UTF-8 -kformat-message -kmessage -kerror gnus/*.el -o gnus_messages.po
this command extracts all menu items:
xgettext --from-code=UTF-8 -kmenu-item *.el **/*.el -o menus.po
and this extracts all docstrings:
xgettext --from-code=UTF-8 -kdefcustom:3 -kdefvar:3 -kdefun:3 *.el **/*.el -o docstrings.po
The size of docstrings.po is about 9MB, so perhaps it should reside in
a separate catalog defined by e.g.
(defdomain emacs-docstrings
with semantics similar to defgroup, but I have no opinion about this.
I think this project urgently needs a coordinator: to negotiate with
package authors and translation teams about how to better split
translations to message catalogs. So there are not so much technical
problems, but mostly organizational ones.
> IIUC, using standard gettext functions this would rather correspond to
>
> (message (ngettext "Replaced %1$d occurrence%s"
> "Replaced %1$d occurrences%s"
> replace-count)
It seems better to start with this standard function
and add more optimizations like ‘nmessage’ later.
Other Lisp implementations use ‘ngettext’ as well, e.g.:
https://clisp.sourceforge.io/impnotes.html#ggettext
So I'm going to start with more obvious parts of the task
by fixing the current bugs of incorrect English syntax
in a forward-compatible way:
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: i18n-ngettext.patch --]
[-- Type: text/x-diff, Size: 6767 bytes --]
diff --git a/lisp/subr.el b/lisp/subr.el
index 6c0ad00afa..1f000f77ad 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -342,6 +342,13 @@ define-error
(delete-dups (copy-sequence (cons name conditions))))
(when message (put name 'error-message message))))
+(defun ngettext (msgid msgid_plural n &optional _domain _category)
+ "Return the plural form of the translation for of MSGID and N.
+In the given DOMAIN, depending on the given CATEGORY. MSGID and
+MSGID_PLURAL should be ASCII strings, and are normally the English singular
+and English plural variant of the message, respectively."
+ (if (/= n 1) msgid_plural msgid))
+
;; We put this here instead of in frame.el so that it's defined even on
;; systems where frame.el isn't loaded.
(defun frame-configuration-p (object)
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index a5427dd8b7..c0f47159c9 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))
@@ -552,7 +552,10 @@ grep-exit-message
;; so the buffer is still unmodified if there is no output.
(cond ((and (zerop code) (buffer-modified-p))
(if (> grep-num-matches-found 0)
- (cons (format "finished with %d matches found\n" grep-num-matches-found)
+ (cons (format (ngettext "finished with %d match found\n"
+ "finished with %d matches found\n"
+ grep-num-matches-found)
+ grep-num-matches-found)
"matched")
'("finished with matches found\n" . "matched")))
((not (buffer-modified-p))
diff --git a/lisp/replace.el b/lisp/replace.el
index 59ad1a375b..318a9fb025 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -983,7 +983,10 @@ flush-lines
(progn (forward-line 1) (point)))
(setq count (1+ count))))
(set-marker rend nil)
- (when interactive (message "Deleted %d matching lines" count))
+ (when interactive (message (ngettext "Deleted %d matching line"
+ "Deleted %d matching lines"
+ count)
+ count))
count))
(defun how-many (regexp &optional rstart rend interactive)
@@ -1032,9 +1035,10 @@ 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 (ngettext "%d occurrence"
+ "%d occurrences"
+ count)
+ count))
count)))
\f
@@ -1617,11 +1621,12 @@ occur-1
(not (eq occur-excluded-properties t))))))
(let* ((bufcount (length active-bufs))
(diff (- (length bufs) bufcount)))
- (message "Searched %d buffer%s%s; %s match%s%s"
- bufcount (if (= bufcount 1) "" "s")
+ (message "Searched %d %s%s; %s %s%s"
+ bufcount
+ (ngettext "buffer" "buffers" bufcount)
(if (zerop diff) "" (format " (%d killed)" diff))
(if (zerop count) "no" (format "%d" count))
- (if (= count 1) "" "es")
+ (ngettext "match" "matches" count)
;; Don't display regexp if with remaining text
;; it is longer than window-width.
(if (> (+ (length (or (get-text-property 0 'isearch-string regexp)
@@ -1856,14 +1861,15 @@ occur-engine
(let ((beg (point))
end)
(insert (propertize
- (format "%d match%s%s%s in buffer: %s%s\n"
- matches (if (= matches 1) "" "es")
+ (format "%d %s%s%s in buffer: %s%s\n"
+ matches
+ (ngettext "match" "matches" matches)
;; Don't display the same number of lines
;; and matches in case of 1 match per line.
(if (= lines matches)
- "" (format " in %d line%s"
+ "" (format " in %d %s"
lines
- (if (= lines 1) "" "s")))
+ (ngettext "line" "lines" lines)))
;; Don't display regexp for multi-buffer.
(if (> (length buffers) 1)
"" (occur-regexp-descr regexp))
@@ -1889,13 +1895,15 @@ occur-engine
(goto-char (point-min))
(let ((beg (point))
end)
- (insert (format "%d match%s%s total%s:\n"
- global-matches (if (= global-matches 1) "" "es")
+ (insert (format "%d %s%s total%s:\n"
+ global-matches
+ (ngettext "match" "matches" global-matches)
;; Don't display the same number of lines
;; and matches in case of 1 match per line.
(if (= global-lines global-matches)
- "" (format " in %d line%s"
- global-lines (if (= global-lines 1) "" "s")))
+ "" (format " in %d %s"
+ global-lines
+ (ngettext "line" "lines" global-lines)))
(occur-regexp-descr regexp)))
(setq end (point))
(when title-face
@@ -2730,10 +2738,10 @@ 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 (ngettext "Undid %d replacement"
+ "Undid %d replacements"
+ num-replacements)
+ num-replacements)
(ding 'no-terminate)
(sit-for 1)))
(setq replaced nil last-was-undo t last-was-act-and-show nil)))
@@ -2859,9 +2867,10 @@ perform-replace
last-was-act-and-show nil))))))
(replace-dehighlight))
(or unread-command-events
- (message "Replaced %d occurrence%s%s"
+ (message (ngettext "Replaced %d occurrence%s"
+ "Replaced %d occurrences%s"
+ replace-count)
replace-count
- (if (= replace-count 1) "" "s")
(if (> (+ skip-read-only-count
skip-filtered-count
skip-invisible-count)
next prev parent reply other threads:[~2019-03-17 21:23 UTC|newest]
Thread overview: 161+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <87o97aq6gz.fsf@jidanni.org>
[not found] ` <87tvgoud56.fsf@mail.linkov.net>
[not found] ` <83o96wk2mi.fsf@gnu.org>
[not found] ` <87k1hjfvjd.fsf@mail.linkov.net>
[not found] ` <E1gzZKP-0000kS-Iw@fencepost.gnu.org>
[not found] ` <871s3p0zdz.fsf@mail.linkov.net>
2019-03-03 3:04 ` bug#34520: delete-matching-lines should report how many lines it deleted Richard Stallman
2019-03-03 15:31 ` Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted) Eli Zaretskii
2019-03-03 20:57 ` Emacs i18n Juri Linkov
2019-03-04 1:46 ` Jean-Christophe Helary
2019-03-06 9:38 ` Elias Mårtenson
2019-03-06 11:23 ` Jean-Christophe Helary
2019-03-21 20:33 ` Clément Pit-Claudel
2019-03-21 20:50 ` Eli Zaretskii
2019-03-21 21:03 ` Clément Pit-Claudel
2019-03-21 21:21 ` Jean-Christophe Helary
2019-03-21 21:34 ` Clément Pit-Claudel
2019-03-21 21:56 ` Jean-Christophe Helary
2019-03-21 22:05 ` Clément Pit-Claudel
2019-03-21 23:46 ` Jean-Christophe Helary
2019-03-22 8:22 ` Eli Zaretskii
2019-03-22 16:10 ` Clément Pit-Claudel
2019-03-22 16:35 ` Eli Zaretskii
2019-03-22 17:16 ` Clément Pit-Claudel
2019-03-22 17:35 ` Eli Zaretskii
2019-03-22 23:17 ` Clément Pit-Claudel
2019-03-21 21:17 ` Jean-Christophe Helary
2019-03-21 21:59 ` Juri Linkov
2019-03-22 8:22 ` Eli Zaretskii
2019-03-23 21:50 ` Juri Linkov
2019-03-24 3:36 ` Eli Zaretskii
2019-03-24 21:55 ` Juri Linkov
2019-03-24 23:31 ` Jean-Christophe Helary
2019-03-25 21:32 ` Juri Linkov
2019-03-25 22:31 ` Paul Eggert
2019-03-26 16:11 ` Eli Zaretskii
2019-03-26 16:22 ` Stefan Monnier
2019-03-26 16:55 ` Eli Zaretskii
2019-03-26 22:35 ` Paul Eggert
2019-03-27 3:43 ` Eli Zaretskii
2019-03-28 14:56 ` Clément Pit-Claudel
2019-03-28 15:52 ` Eli Zaretskii
2019-03-27 2:34 ` Jean-Christophe Helary
2019-03-26 23:16 ` Juri Linkov
2019-03-27 1:35 ` Paul Eggert
2019-04-24 6:39 ` Jean-Christophe Helary
2019-04-24 20:18 ` Juri Linkov
2019-03-25 3:35 ` Eli Zaretskii
2019-03-25 9:04 ` Jean-Christophe Helary
2019-03-25 21:02 ` Juri Linkov
2019-03-26 3:27 ` Eli Zaretskii
2019-03-27 23:06 ` Richard Stallman
2019-03-25 10:52 ` Mattias Engdegård
2019-03-25 15:37 ` Eli Zaretskii
2019-03-25 21:11 ` Juri Linkov
2019-03-25 22:05 ` Mattias Engdegård
2019-03-27 21:22 ` Juri Linkov
2019-03-28 11:03 ` Mattias Engdegård
2019-03-04 3:27 ` Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted) Richard Stallman
2019-03-04 16:36 ` Eli Zaretskii
2019-03-04 18:37 ` Paul Eggert
2019-03-04 19:07 ` Eli Zaretskii
2019-03-05 2:09 ` Paul Eggert
2019-03-05 21:58 ` Emacs i18n Juri Linkov
2019-03-06 2:16 ` Richard Stallman
2019-03-06 18:15 ` Eli Zaretskii
2019-03-06 19:47 ` Paul Eggert
2019-03-06 20:19 ` Eli Zaretskii
2019-03-07 1:52 ` Paul Eggert
2019-03-07 3:37 ` Eli Zaretskii
2019-03-08 4:07 ` Richard Stallman
2019-03-08 8:16 ` Eli Zaretskii
2019-03-08 4:07 ` Richard Stallman
2019-03-08 4:33 ` Elias Mårtenson
2019-03-08 8:22 ` Eli Zaretskii
2019-03-09 3:11 ` Richard Stallman
2019-03-09 7:54 ` Paul Eggert
2019-03-09 10:30 ` Eli Zaretskii
2019-03-10 3:05 ` Richard Stallman
2019-03-10 6:07 ` Paul Eggert
2019-03-11 1:20 ` Richard Stallman
2019-03-11 3:52 ` Paul Eggert
2019-03-12 3:31 ` Richard Stallman
2019-03-12 3:31 ` Richard Stallman
2019-03-10 8:45 ` Yuri Khan
2019-03-10 3:05 ` Richard Stallman
2019-03-10 6:14 ` Paul Eggert
2019-03-10 3:05 ` Richard Stallman
2019-03-07 3:42 ` Richard Stallman
2019-03-07 14:46 ` Eli Zaretskii
2019-03-07 17:19 ` Paul Eggert
2019-03-07 18:24 ` martin rudalics
2019-03-07 18:44 ` Paul Eggert
2019-03-07 20:22 ` Eli Zaretskii
2019-03-07 22:25 ` Paul Eggert
2019-03-08 7:29 ` Eli Zaretskii
2019-03-08 4:18 ` Richard Stallman
2019-03-08 4:11 ` Richard Stallman
2019-03-06 17:30 ` Eli Zaretskii
2019-03-06 18:09 ` Eli Zaretskii
2019-03-06 19:39 ` Paul Eggert
2019-03-06 19:49 ` Eli Zaretskii
2019-03-07 1:33 ` Paul Eggert
2019-03-07 3:30 ` Eli Zaretskii
2019-03-07 16:06 ` Paul Eggert
2019-03-07 4:35 ` Jean-Christophe Helary
2019-03-07 16:04 ` Paul Eggert
2019-03-08 4:09 ` Richard Stallman
2019-03-11 21:48 ` Juri Linkov
2019-03-11 22:51 ` Paul Eggert
2019-03-12 21:45 ` Juri Linkov
2019-03-17 21:23 ` Juri Linkov [this message]
2019-03-18 21:20 ` Juri Linkov
2019-03-18 21:55 ` Paul Eggert
2019-03-19 20:40 ` Juri Linkov
2019-03-11 23:59 ` Jean-Christophe Helary
2019-03-12 9:16 ` Michael Albinus
2019-03-06 19:47 ` Paul Eggert
2019-03-06 20:21 ` Eli Zaretskii
2019-03-07 1:43 ` Paul Eggert
2019-03-07 3:31 ` Eli Zaretskii
2019-03-07 3:44 ` Richard Stallman
2019-03-07 14:48 ` Eli Zaretskii
2019-03-07 22:29 ` Juri Linkov
2019-03-08 1:48 ` Jean-Christophe Helary
2019-03-08 8:08 ` Eli Zaretskii
2019-03-08 15:11 ` Jean-Christophe Helary
2019-03-08 20:11 ` Eli Zaretskii
2019-03-09 2:44 ` Jean-Christophe Helary
2019-03-09 6:40 ` Eli Zaretskii
2019-03-09 8:37 ` Michael Albinus
2019-03-09 10:45 ` Eli Zaretskii
2019-03-09 11:27 ` Michael Albinus
2019-03-09 17:23 ` Eli Zaretskii
2019-03-09 19:55 ` Paul Eggert
2019-03-09 20:07 ` Eli Zaretskii
2019-03-09 20:47 ` Paul Eggert
2019-03-09 20:04 ` Michael Albinus
2019-03-09 20:14 ` Eli Zaretskii
2019-03-09 19:22 ` Paul Eggert
2019-03-09 19:39 ` Eli Zaretskii
2019-03-09 20:48 ` Paul Eggert
2019-03-09 20:08 ` Michael Albinus
2019-03-10 3:09 ` Richard Stallman
2019-03-10 13:38 ` Eli Zaretskii
2019-03-08 7:37 ` Eli Zaretskii
2019-03-09 3:12 ` Richard Stallman
2019-03-08 4:11 ` Richard Stallman
2019-03-06 2:09 ` Emacs i18n (was: bug#34520: delete-matching-lines should report how many lines it deleted) Richard Stallman
2019-03-05 2:49 ` Richard Stallman
2019-03-05 3:31 ` Eli Zaretskii
2019-03-20 11:59 Emacs i18n Bruno Haible
2019-03-20 16:36 ` Paul Eggert
2019-03-20 21:32 ` Juri Linkov
2019-03-21 2:14 ` Richard Stallman
[not found] ` <E1h6nE3-0000bt-SW-iW7gFb+/I3LZHJUXO5efmti2O/JbrIOy@public.gmane.org>
2019-03-21 21:45 ` Juri Linkov
2019-03-23 2:28 ` Richard Stallman
2019-03-23 7:55 ` Yuri Khan
[not found] ` <CAP_d_8WjQwAtcWCfkjXHtc-dqYyBfnaP0+9L8KK6eCp4r_ZsPQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2019-03-23 17:50 ` Ineiev
2019-03-24 1:43 ` Richard Stallman
[not found] ` <E1h7WOF-0006T8-Be-iW7gFb+/I3LZHJUXO5efmti2O/JbrIOy@public.gmane.org>
2019-03-23 21:48 ` Juri Linkov
2019-03-24 1:47 ` Richard Stallman
2019-03-22 20:50 ` Chusslove Illich
[not found] ` <87h8bx5ijn.fsf-i9wRM+HIrmlRTR8OWt4JRw@public.gmane.org>
2019-03-21 2:55 ` Bruno Haible
2019-03-21 2:14 ` Richard Stallman
2019-03-22 1:26 ` Bruno Haible
2019-03-23 2:29 ` Richard Stallman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87va0htcss.fsf@mail.linkov.net \
--to=juri@linkov.net \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).