ing through all files in tags table.
Disregard some simple strings which are most probably non-translatable.
With prefix argument, restart search at first file."
  (interactive "P")
  (require 'etags)
  ;; Ensure there is no highlighting, in case the search fails.
  (if po-highlighting
      (po-dehighlight po-marking-overlay))
  (setq po-string-contents nil)
  ;; Search for a string which might later be marked for translation.
  (let ((po-current-po-buffer (current-buffer))
        (po-current-po-keywords po-keywords))
    (pop-to-buffer po-string-buffer)
    (if (and (not restart)
             (eq (car tags-loop-operate) 'po-tags-loop-operate))
        ;; Continue last po-tags-search.
        (tags-loop-continue nil)
      ;; Start or restart po-tags-search all over.
      (setq tags-loop-scan '(po-tags-loop-scan)
            tags-loop-operate '(po-tags-loop-operate))
      (tags-loop-continue t))
    (select-window (get-buffer-window po-current-po-buffer)))
  (if po-string-contents
      (let ((window (selected-window))
            (buffer po-string-buffer)
            (start po-string-start)
            (end po-string-end))
        ;; Try to fit the string in the displayed part of its window.
        (select-window (get-buffer-window buffer))
        (goto-char start)
        (or (pos-visible-in-window-p start)
            (recenter '(nil)))
        (if (pos-visible-in-window-p end)
            (goto-char end)
          (goto-char end)
          (recenter -1))
        (select-window window)
        ;; Highlight the string as found.
        (and po-highlighting
             (po-highlight po-marking-overlay start end buffer)))))

(defun po-tags-loop-scan ()
  "Decide if the current buffer is still interesting for PO mode strings."
  ;; We have little choice, here.  The major mode is needed to dispatch to the
  ;; proper scanner, so we declare all files as interesting, to force Emacs
  ;; tags module to revisit files fully.  po-tags-loop-operate sets point at
  ;; end of buffer when it is done with a file.
  (not (eobp)))

(defun po-tags-loop-operate ()
  "Find an acceptable tag in the current buffer, according to mode.
Disregard some simple strings which are most probably non-translatable."
  (po-preset-string-functions)
  (let ((continue t)
        data)
    (while continue
      (setq data (apply po-find-string-function po-current-po-keywords nil))
      (if data
          ;; Push the string just found into a work buffer for study.
          (with-temp-buffer
           (insert (nth 0 data))
           (goto-char (point-min))
           ;; Accept if at least three letters in a row.
           (if (re-search-forward "[A-Za-z][A-Za-z][A-Za-z]" nil t)
               (setq continue nil)
             ;; Disregard if single letters or no letters at all.
             (if (re-search-forward "[A-Za-z][A-Za-z]" nil t)
                 ;; Here, we have two letters in a row, but never more.
                 ;; Accept only if more letters than punctuations.
                 (let ((total (buffer-size)))
                   (goto-char (point-min))
                   (while (re-search-forward "[A-Za-z]+" nil t)
                     (replace-match "" t t))
                   (if (< (* 2 (buffer-size)) total)
                       (setq continue nil))))))
        ;; No string left in this buffer.
        (setq continue nil)))
    (if data
        ;; Save information for marking functions.
        (let ((buffer (current-buffer)))
          (save-excursion
            (set-buffer po-current-po-buffer)
            (setq po-string-contents (nth 0 data)
                  po-string-buffer buffer
                  po-string-start (nth 1 data)
                  po-string-end (nth 2 data))))
      (goto-char (point-max)))
    ;; If nothing was found, trigger scanning of next file.
    (not data)))

(defun po-mark-found-string (keyword)
  "Mark last found string in program sources as translatable, using KEYWORD."
  (if (not po-string-contents)
    (error (_"No such string")))
  (and po-highlighting (po-dehighlight po-marking-overlay))
  (let ((contents po-string-contents)
        (buffer po-string-buffer)
        (start po-string-start)
        (end po-string-end)
        line string)
    ;; Mark string in program sources.
    (save-excursion
      (set-buffer buffer)
      (setq line (count-lines (point-min) start))
      (apply po-mark-string-function start end keyword nil))
    ;; Add PO file entry.
    (let ((buffer-read-only po-read-only))
      (goto-char (point-max))
      (insert "\n" (format "#: %s:%d\n"
                           (buffer-file-name po-string-buffer)
                           line))
      (save-excursion
        (insert (po-eval-requoted contents "msgid" nil) "msgstr \"\"\n"))
      (setq po-untranslated-counter (1+ po-untranslated-counter))
      (po-update-mode-line-string))
    (setq po-string-contents nil)))

(defun po-mark-translatable ()
  "Mark last found string in program sources as translatable, using '_'."
  (interactive)
  (po-mark-found-string "_"))

(defun po-select-mark-and-mark (arg)
  "Mark last found string in program sources as translatable, ask for keyword,
using completion.  With prefix argument, just ask the name of a preferred
keyword for subsequent commands, also added to possible completions."
  (interactive "P")
  (if arg
      (let ((keyword (list (read-from-minibuffer (_"Keyword: ")))))
        (setq po-keywords (cons keyword (delete keyword po-keywords))))
    (or po-string-contents (error (_"No such string")))
    (let* ((default (car (car po-keywords)))
           (keyword (completing-read (format (_"Mark with keyword? [%s] ")
                                             default)
                                     po-keywords nil t )))
      (if (string-equal keyword "") (setq keyword default))
      (po-mark-found-string keyword))))
