1
1
;;; po-mode.el -- major mode for GNU gettext PO files
3
;; Copyright (C) 1995-1999, 2000-2002, 2005-2007 Free Software Foundation, Inc.
3
;; Copyright (C) 1995-1999, 2000-2002, 2005-2008, 2010 Free Software Foundation, Inc.
5
;; Authors: Fran�ois Pinard <pinard@iro.umontreal.ca>
5
;; Authors: François Pinard <pinard@iro.umontreal.ca>
6
6
;; Greg McGary <gkm@magilla.cichlid.com>
7
7
;; Keywords: i18n gettext
10
10
;; This file is part of GNU gettext.
12
;; GNU gettext is free software; you can redistribute it and/or modify
12
;; This program is free software: you can redistribute it and/or modify
13
13
;; it under the terms of the GNU General Public License as published by
14
;; the Free Software Foundation; either version 2, or (at your option)
14
;; the Free Software Foundation; either version 3 of the License, or
15
;; (at your option) any later version.
17
;; GNU gettext is distributed in the hope that it will be useful,
17
;; This program is distributed in the hope that it will be useful,
18
18
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19
19
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
20
;; GNU General Public License for more details.
22
22
;; You should have received a copy of the GNU General Public License
23
;; along with GNU Emacs; see the file COPYING. If not, write to the
24
;; Free Software Foundation, 51 Franklin Street, Fifth Floor,
25
;; Boston, MA 02110-1301, USA.
23
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
120
(defcustom po-auto-delete-previous-msgid t
121
"*Automatically delete previous msgid (marked #|) when editing entry.
122
Value is nil, t, or ask."
123
:type '(choice (const nil)
122
128
(defcustom po-auto-select-on-unfuzzy nil
123
129
"*Automatically select some new entry while making an entry not fuzzy."
688
695
(defvar po-start-of-entry)
689
696
(defvar po-start-of-msgctxt) ; = po-start-of-msgid if there is no msgctxt
690
697
(defvar po-start-of-msgid)
698
(defvar po-start-of-msgid_plural) ; = nil if there is no msgid_plural
691
699
(defvar po-start-of-msgstr-block)
692
700
(defvar po-start-of-msgstr-form)
693
701
(defvar po-end-of-msgstr-form)
694
702
(defvar po-end-of-entry)
695
703
(defvar po-entry-type)
696
(defvar po-msgstr-form-flavor)
698
705
;; A few counters are usefully shown in the Emacs mode line.
699
706
(defvar po-translated-counter)
1002
1009
(error (_"I do not know how to mail to '%s'") to))))))
1003
1010
"Function to start composing an electronic message.")
1012
(defvar po-any-previous-msgctxt-regexp
1013
"^#\\(~\\)?|[ \t]*msgctxt.*\n\\(#\\(~\\)?|[ \t]*\".*\n\\)*"
1014
"Regexp matching a whole #| msgctxt field, whether obsolete or not.")
1016
(defvar po-any-previous-msgid-regexp
1017
"^#\\(~\\)?|[ \t]*msgid.*\n\\(#\\(~\\)?|[ \t]*\".*\n\\)*"
1018
"Regexp matching a whole #| msgid field, whether obsolete or not.")
1020
(defvar po-any-previous-msgid_plural-regexp
1021
"^#\\(~\\)?|[ \t]*msgid_plural.*\n\\(#\\(~\\)?|[ \t]*\".*\n\\)*"
1022
"Regexp matching a whole #| msgid_plural field, whether obsolete or not.")
1005
1024
(defvar po-any-msgctxt-msgid-regexp
1006
1025
"^\\(#~[ \t]*\\)?msg\\(ctxt\\|id\\).*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*"
1007
1026
"Regexp matching a whole msgctxt or msgid field, whether obsolete or not.")
1010
1029
"^\\(#~[ \t]*\\)?msgid.*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*"
1011
1030
"Regexp matching a whole msgid field, whether obsolete or not.")
1032
(defvar po-any-msgid_plural-regexp
1033
"^\\(#~[ \t]*\\)?msgid_plural.*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*"
1034
"Regexp matching a whole msgid_plural field, whether obsolete or not.")
1013
1036
(defvar po-any-msgstr-block-regexp
1014
"^\\(#~[ \t]*\\)?msgstr.*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*\\(\\(#~[ \t]*\\)?msgstr\\[[0-9]\\].*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*\\)*"
1037
"^\\(#~[ \t]*\\)?msgstr\\([ \t]\\|\\[0\\]\\).*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*\\(\\(#~[ \t]*\\)?msgstr\\[[0-9]\\].*\n\\(\\(#~[ \t]*\\)?\".*\n\\)*\\)*"
1015
1038
"Regexp matching a whole msgstr or msgstr[] field, whether obsolete or not.")
1017
1040
(defvar po-any-msgstr-form-regexp
1160
1183
(make-local-variable 'po-start-of-entry)
1161
1184
(make-local-variable 'po-start-of-msgctxt)
1162
1185
(make-local-variable 'po-start-of-msgid)
1186
(make-local-variable 'po-start-of-msgid_plural)
1163
1187
(make-local-variable 'po-start-of-msgstr-block)
1164
1188
(make-local-variable 'po-end-of-entry)
1165
1189
(make-local-variable 'po-entry-type)
1395
1419
(message (_"PO-Revision-Date should be adjusted...")))
1420
;; Return nil to indicate that the buffer has not yet been saved.
1398
1423
;;; Handling span of entry, entry type and entry attributes.
1400
1425
(defun po-find-span-of-entry ()
1401
1426
"Find the extent of the PO file entry where the cursor is.
1402
Set variables PO-START-OF-ENTRY, PO-START-OF-MSGCTXT, PO-START-OF-MSGID,
1403
po-start-of-msgstr-block, PO-END-OF-ENTRY and PO-ENTRY-TYPE to meaningful
1404
values. Decreasing priority of type interpretation is: obsolete, fuzzy,
1405
untranslated or translated."
1427
Set variables po-start-of-entry, po-start-of-msgctxt, po-start-of-msgid,
1428
po-start-of-msgid_plural, po-start-of-msgstr-block, po-end-of-entry, and
1429
po-entry-type to meaningful values. po-entry-type may be set to: obsolete,
1430
fuzzy, untranslated, or translated."
1406
1431
(let ((here (point)))
1407
1432
(if (re-search-backward po-any-msgstr-block-regexp nil t)
1460
1485
(re-search-forward po-any-msgid-regexp)
1461
1486
(setq po-start-of-msgid (match-beginning 0))
1462
1487
(save-excursion
1488
(goto-char po-start-of-msgid)
1489
(setq po-start-of-msgid_plural
1490
(if (re-search-forward po-any-msgid_plural-regexp
1491
po-start-of-msgstr-block t)
1463
1495
(when (>= here po-start-of-msgstr-block)
1464
1496
;; point was somewhere inside of msgstr*
1465
1497
(goto-char here)
1467
1499
(re-search-backward "^\\(#~[ \t]*\\)?msgstr"))
1468
;; Detect the bounderies of the msgstr we are interested in.
1500
;; Detect the boundaries of the msgstr we are interested in.
1469
1501
(re-search-forward po-any-msgstr-form-regexp)
1470
1502
(setq po-start-of-msgstr-form (match-beginning 0)
1471
1503
po-end-of-msgstr-form (match-end 0)))
1671
1703
(po-find-span-of-entry)
1672
1704
(if (or (eq po-entry-type 'untranslated)
1673
1705
(eq po-entry-type 'obsolete)
1674
(y-or-n-p (_"Really lose previous translation? ")))
1675
(po-set-msgstr-form (po-get-msgid)))
1706
(prog1 (y-or-n-p (_"Really lose previous translation? "))
1708
;; In an entry with plural forms, use the msgid_plural string,
1709
;; as it is more general than the msgid string.
1710
(if (po-set-msgstr-form (or (po-get-msgid_plural) (po-get-msgid)))
1711
(po-maybe-delete-previous-untranslated))))
1678
1713
;; Obsolete entries.
1714
1749
(cond ((eq po-entry-type 'fuzzy)
1715
1750
(po-decrease-type-counter)
1716
1751
(po-delete-attribute "fuzzy")
1752
(po-maybe-delete-previous-untranslated)
1717
1753
(po-current-entry)
1718
1754
(po-increase-type-counter)))
1719
1755
(if po-auto-select-on-unfuzzy
1741
1777
(po-previous-entry-with-regexp po-any-msgstr-block-regexp t)
1742
1778
(po-find-span-of-entry)
1743
1779
(while (not (eq po-entry-type 'translated))
1744
(po-previous-entry-with-regexp po-untranslated-regexp t)
1780
(po-previous-entry-with-regexp po-any-msgstr-block-regexp t)
1745
1781
(po-find-span-of-entry))))
1747
1783
;; Auto-selection feature.
1889
1925
"Extract and return the unquoted msgid string."
1890
1926
(let ((string (po-extract-unquoted (current-buffer)
1891
1927
po-start-of-msgid
1892
po-start-of-msgstr-block)))
1928
(or po-start-of-msgid_plural
1929
po-start-of-msgstr-block))))
1932
(defun po-get-msgid_plural ()
1933
"Extract and return the unquoted msgid_plural string.
1934
Return nil if it is not present."
1935
(if po-start-of-msgid_plural
1936
(let ((string (po-extract-unquoted (current-buffer)
1937
po-start-of-msgid_plural
1938
po-start-of-msgstr-block)))
1895
1942
(defun po-get-msgstr-flavor ()
1896
"Helper function to detect msgstr and msgstr[] variants."
1898
(re-search-forward "^\\(#~[ \t]*\\)?\\(msgstr\\(\\[[0-9]\\]\\)?\\)")
1943
"Helper function to detect msgstr and msgstr[] variants.
1944
Returns one of \"msgstr\" or \"msgstr[i]\" for some i."
1946
(goto-char po-start-of-msgstr-form)
1947
(re-search-forward "^\\(#~[ \t]*\\)?\\(msgstr\\(\\[[0-9]\\]\\)?\\)")
1901
1950
(defun po-get-msgstr-form ()
1902
1951
"Extract and return the unquoted msgstr string."
1903
(let ((flavor (po-get-msgstr-flavor))
1904
(string (po-extract-unquoted (current-buffer)
1952
(let ((string (po-extract-unquoted (current-buffer)
1905
1953
po-start-of-msgstr-form
1906
1954
po-end-of-msgstr-form)))
1907
(setq po-msgstr-form-flavor flavor)
1910
1957
(defun po-set-msgid (form)
1935
1982
Returns 'nil' if the buffer has not been modified, for if the new msgstr
1936
1983
described by FORM is merely identical to the msgstr already in place."
1937
1984
(let ((string (po-eval-requoted form
1938
po-msgstr-form-flavor
1985
(po-get-msgstr-flavor)
1939
1986
(eq po-entry-type 'obsolete))))
1940
1987
(save-excursion
1941
1988
(goto-char po-start-of-msgstr-form)
1961
2008
"Empty the msgstr string from current entry, pushing it on the kill ring."
1963
2010
(po-kill-ring-save-msgstr)
1964
(po-set-msgstr-form ""))
2011
(if (po-set-msgstr-form "")
2012
(po-maybe-delete-previous-untranslated)))
1966
2014
(defun po-yank-msgstr ()
1967
2015
"Replace the current msgstr string by the top of the kill ring."
1969
2017
(po-find-span-of-entry)
1970
(po-set-msgstr-form (if (eq last-command 'yank) '(yank-pop 1) '(yank)))
2018
(if (po-set-msgstr-form (if (eq last-command 'yank) '(yank-pop 1) '(yank)))
2019
(po-maybe-delete-previous-untranslated))
1971
2020
(setq this-command 'yank))
1973
2022
(defun po-fade-out-entry ()
2090
2139
(po-set-comment (if (eq last-command 'yank) '(yank-pop 1) '(yank)))
2091
2140
(setq this-command 'yank)
2092
2141
(po-redisplay))
2143
;;; Deleting the "previous untranslated" comment.
2145
(defun po-previous-untranslated-region-for (rx)
2146
"Return the list of previous untranslated regions (at most one) for the
2147
given regular expression RX."
2149
(goto-char po-start-of-entry)
2150
(if (re-search-forward rx po-start-of-msgctxt t)
2151
(list (cons (copy-marker (match-beginning 0))
2152
(copy-marker (match-end 0))))
2155
(defun po-previous-untranslated-regions ()
2156
"Return the list of previous untranslated regions in the current entry."
2157
(append (po-previous-untranslated-region-for po-any-previous-msgctxt-regexp)
2158
(po-previous-untranslated-region-for po-any-previous-msgid-regexp)
2159
(po-previous-untranslated-region-for po-any-previous-msgid_plural-regexp)))
2161
(defun po-delete-previous-untranslated ()
2162
"Delete the previous msgctxt, msgid, msgid_plural fields (marked as #|
2163
comments) from the current entry."
2165
(po-find-span-of-entry)
2166
(let ((buffer-read-only po-read-only))
2167
(dolist (region (po-previous-untranslated-regions))
2168
(delete-region (car region) (cdr region))))
2171
(defun po-maybe-delete-previous-untranslated ()
2172
"Delete the previous msgctxt, msgid, msgid_plural fields (marked as #|
2173
comments) from the current entry, if the user gives the permission."
2174
(po-find-span-of-entry)
2175
(let ((previous-regions (po-previous-untranslated-regions)))
2176
(if previous-regions
2177
(if (or (eq po-auto-delete-previous-msgid t)
2178
(and (eq po-auto-delete-previous-msgid 'ask)
2179
(let ((overlays nil))
2185
(let ((overlay (po-create-overlay)))
2186
(po-highlight overlay (car region) (cdr region))
2189
;; Scroll, to show the previous-regions.
2190
(goto-char (car (car previous-regions)))
2191
(prog1 (y-or-n-p (_"Delete previous msgid comments? "))
2193
(mapc 'po-dehighlight overlays)))))
2194
(let ((buffer-read-only po-read-only))
2195
(dolist (region previous-regions)
2196
(delete-region (car region) (cdr region))))))))
2094
2198
;;; Editing management and submode.
2096
2200
;; In a string edit buffer, BACK-POINTER points to one of the slots of the
2265
2369
(po-set-comment string)
2266
2370
(po-redisplay))
2267
2371
((= (point) po-start-of-msgstr-form)
2268
(let ((replaced (po-set-msgstr-form string)))
2270
po-auto-fuzzy-on-edit
2271
(eq po-entry-type 'translated))
2273
(po-decrease-type-counter)
2274
(po-add-attribute "fuzzy")
2276
(po-increase-type-counter)))))
2372
(if (po-set-msgstr-form string)
2374
(po-maybe-delete-previous-untranslated)
2375
(if (and po-auto-fuzzy-on-edit
2376
(eq po-entry-type 'translated))
2378
(po-decrease-type-counter)
2379
(po-add-attribute "fuzzy")
2381
(po-increase-type-counter))))))
2279
2384
(defun po-edit-string (string type expand-tabs)