" (with-temp-buffer (or ;; Don't bother checking again. po-msgfmt-version-checked (and ;; Make sure 'msgfmt' is available. (condition-case nil (call-process po-msgfmt-program nil t nil "--verbose" "--version") (file-error nil)) ;; Make sure there's a version number in the output: ;; 0.11 or 0.10.36 or 0.19.5.1 or 0.11-pre1 or 0.16.2-pre1 (progn (goto-char (point-min)) (or (looking-at ".* \\([0-9]+\\)\\.\\([0-9]+\\)$") (looking-at ".* \\([0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)$") (looking-at ".* \\([0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)$") (looking-at ".* \\([0-9]+\\)\\.\\([0-9]+\\)[-_A-Za-z0-9]+$") (looking-at ".* \\([0-9]+\\)\\.\\([0-9]+\\)\\.\\([0-9]+\\)[-_A-Za-z0-9]+$"))) ;; Make sure the version is recent enough. (>= (string-to-number (format "%d%03d%03d" (string-to-number (match-string 1)) (string-to-number (match-string 2)) (string-to-number (or (match-string 3) "0")))) 010036) ;; Remember the outcome. (setq po-msgfmt-version-checked t)) (error (_"'msgfmt' from GNU gettext 0.10.36 or greater is required"))))) (defun po-guess-archive-name () "Return the ideal file name for this PO file in the central archives." (let ((filename (file-name-nondirectory buffer-file-name)) start-of-header end-of-header package version team) (save-excursion ;; Find the PO file header entry. (goto-char (point-min)) (re-search-forward po-any-msgstr-block-regexp) (setq start-of-header (match-beginning 0) end-of-header (match-end 0)) ;; Get the package and version. (goto-char start-of-header) (if (re-search-forward "\n\ \"Project-Id-Version: \\(GNU \\|Free \\)?\\([^\n ]+\\) \\([^\n ]+\\)\\\\n\"$" end-of-header t) (setq package (match-string-no-properties 2) version (match-string-no-properties 3))) (if (or (not package) (string-equal package "PACKAGE") (not version) (string-equal version "VERSION")) (error (_"Project-Id-Version field does not have a proper value"))) ;; File name version and Project-Id-Version must match (cond (;; A `filename' w/o package and version info at all (string-match "^[^\\.]*\\.po\\'" filename)) (;; TP Robot compatible `filename': PACKAGE-VERSION.LL.po (string-match (concat (regexp-quote package) "-\\(.*\\)\\.[^\\.]*\\.po\\'") filename) (if (not (equal version (match-string-no-properties 1 filename))) (error (_"\ Version mismatch: file name: %s; header: %s.\n\ Adjust Project-Id-Version field to match file name and try again") (match-string-no-properties 1 filename) version)))) ;; Get the team. (if (stringp po-team-name-to-code) (setq team po-team-name-to-code) (goto-char start-of-header) (if (re-search-forward "\n\ \"Language-Team: \\([^ ].*[^ ]\\) <.+@.+>\\\\n\"$" end-of-header t) (let ((name (match-string-no-properties 1))) (if name (let ((pair (assoc name po-team-name-to-code))) (if pair (setq team (cdr pair)) (setq team (read-string (format "\ Team name '%s' unknown. What is the team code? " name))))))))) (if (or (not team) (string-equal team "LL")) (error (_"Language-Team field does not have a proper value"))) ;; Compose the name. (concat package "-" version "." team ".po")))) (defun po-guess-team-address () "Return the team address related to this PO file." (let (team) (save-excursion (goto-char (point-min)) (re-search-forward po-any-msgstr-block-regexp) (goto-char (match-beginning 0)) (if (re-search-forward "\n\"Language-Team: +\\(.*<\\(.*\\)@.*>\\)\\\\n\"$" (match-end 0) t) (setq team (match-string-no-properties 2))) (if (or (not team) (string-equal team "LL")) (error (_"Language-Team field does not have a proper value"))) (match-string-no-properties 1)))) (defun po-send-mail () "Start composing a letter, possibly including the current PO file." (interactive) (let* ((team-flag (y-or-n-p (_"\ Write to your team? ('n' if writing to the Translation Project robot) "))) (address (if team-flag (po-guess-team-address) po-translation-project-address))) (if (not (y-or-n-p (_"Include current PO file in mail? "))) (apply po-compose-mail-function address (read-string (_"Subject? ")) nil) (if (buffer-modified-p) (error (_"The file is not even saved, you did not validate it."))) (if (and (y-or-n-p (_"You validated ('V') this file, didn't you? ")) (or (zerop po-untranslated-counter) (y-or-n-p (format (_"%d entries are untranslated, include anyway? ") po-untranslated-counter))) (or (zerop po-fuzzy-counter) (y-or-n-p (format (_"%d entries are still fuzzy, include anyway? ") po-fuzzy-counter))) (or (zerop po-obsolete-counter) (y-or-n-p (format (_"%d entries are obsolete, include anyway? ") po-obsolete-counter)))) (let ((buffer (current-buffer)) (name (po-guess-archive-name)) (transient-mark-mode nil) (coding-system-for-read buffer-file-coding-system) (coding-system-for-write buffer-file-coding-system)) (apply po-compose-mail-function address (if team-flag (read-string (_"Subject? ")) (format "%s %s" po-translation-project-mail-label name)) nil) (goto-char (point-min)) (re-search-forward (concat "^" (regexp-quote mail-header-separator) "\n")) (save-excursion (save-restriction (narrow-to-region (point) (point)) (insert-buffer-substring buffer) (shell-command-on-region (point-min) (point-max) (concat po-gzip-uuencode-command " " name ".gz") t t))))))) (message "")) (defun po-confirm-and-quit () "Confirm if quit should be attempted and then, do it. This is a failsafe. Confirmation is asked if only the real quit would not." (interactive) (if (po-check-all-pending-edits) (progn (if (or (buffer-modified-p) (> po-untranslated-counter 0) (> po-fuzzy-counter 0) (> po-obsolete-counter 0) (y-or-n-p (_"Really quit editing this PO file? "))) (po-quit)) (message "")))) (defun po-quit () "Save the PO file and kill buffer. However, offer validation if appropriate and ask confirmation if untranslated strings remain." (interactive) (if (po-check-all-pending-edits) (let ((quit t)) ;; Offer validation of newly modified entries. (if (and (buffer-modified-p) (not (y-or-n-p (_"File was modified; skip validation step? ")))) (progn (message "") (po-validate) ;; If we knew that the validation was all successful, we should ;; just quit. But since we do not know yet, as the validation ;; might be asynchronous with PO mode commands, the safest is to ;; stay within PO mode, even if this implies that another ;; 'po-quit' command will be later required to exit for true. (setq quit nil))) ;; Offer to work on untranslated entries. (if (and quit (or (> po-untranslated-counter 0) (> po-fuzzy-counter 0) (> po-obsolete-counter 0)) (not (y-or-n-p (_"Unprocessed entries remain; quit anyway? ")))) (progn (setq quit nil) (po-auto-select-entry))) ;; Clear message area. (message "") ;; Or else, kill buffers and quit for true. (if quit (progn (save-buffer) (kill-buffer (current-buffer))))))) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.po[tx]?\\'\\|\\.po\\." . po-mode)) ;;;###autoload (modify-coding-system-alist 'file "\\.po[tx]?\\'\\|\\.po\\." 'po-find-file-coding-system) (provide 'po-mode) ;; Hey Emacs! ;; Local Variables: ;; indent-tabs-mode: nil ;; coding: utf-8 ;; End: ;;; po-mode.el ends here