;;; find a string
(defun translatable-search ()
  (let ((start nil)
        (end nil))
    (while (and (not (eq ?\" (nth 3 (syntax-ppss))))
                (search-forward "\"" nil t)))
    (when (setq start (nth 8 (syntax-ppss)))
      (while (and (search-forward "\"" nil t)
                  (eq ?\" (nth 3 (syntax-ppss)))))
      (when (not (eq ?\" (nth 3 (syntax-ppss))))
        (list (+ start 1) (- (point) 1))))))

;;; find a string which is embedded in another string
(defun translatable-search-embedded (limit)
  (let ((start nil)
        (end nil))
    (when (re-search-forward 
           (concat "\\\\\"\\([^\000]*?\\)\\\\\"" "\\|"
                   "[^\\\\]\"\\(\\|[^\000]*?[^\\\\]\\)\"" "\\|"
                   "<label>\\([^\000]*?\\)</label>" "\\|"
                   "<wtitle>\\([^\000]*?\\)</wtitle>" "\\|"
                   "<frame \\([^\000]*?\\)>")
           limit t)
      (dolist (elt '(1 2 3 4 5) (list start end))
        (setq start (or start (match-beginning elt)))
        (setq end (or end (match-end elt)))))))

;;; analyze whether a string needs to be translated
(defun translatable-analyze (start end)
  (let ((score 0)
        (str nil))
    (save-excursion
      (setq str (buffer-substring start end))
      (goto-char start)

      (when (or (looking-back "<label>")
                (looking-back "<wtitle>")
                (looking-back "<window[[:blank:]]+title=\\\\\"")
                (looking-back "<frame ")
                (looking-back "[[:blank:]]+-?-title[[:blank:]]+[\\\\]?\""))
        (setq score (+ score 100)))
      (when (or (looking-back "[[:alnum:]]*MSG[[:alnum:]]*=\""))
        (setq score (+ score 50)))

      (when (or (string-match "\\`[[:blank:]]*`" str)
                (string-match "\\`[[:blank:]]*\\$(" str)
                (string-match "\\`[[:blank:]]*\\'" str)
                (string-match "\\`\\\\\"[[:blank:]]*\\\\\"\\'" str)
                (string-match "\\`\\$[[:alnum:]_]+\\'" str)
                (string-match "\\`\\${[[:alnum:]_]+}\\'" str)
                (string-match "\\`[^[:alpha:]]+\\'" str)
                )
        (setq score (- score 150)))
      (when (or (looking-back (concat "[^[:alnum:]]\\(?:" 
                                      "icon-name" "\\|"
                                      "type" "\\|"
                                      "icon" "\\|"
                                      "stock" "\\|"
                                      "use-markup"
                                      "\\)=\\\\\""))
                (looking-back (concat "[[:blank:]]+-?-\\(?:"
                                      "bg" "\\|"
                                      "font" "\\|"
                                      "name" "\\|"
                                      "wmclass"
                                      "\\)[[:blank:]]+[\\\\]?\""))
                (looking-back "[[:blank:]]*yaf-splash.*")
                (and (looking-back " = \"")
                     (save-excursion
                       (goto-char end)
                       (looking-at "\" \\]")))
                (looking-back " | grep \"")
                (string= "true" str)
                (string= "false" str)
                (string= "yes" str)
                (string= "no" str)
                (and (string-match "\\([^\000]*<\\)\\{10\\}" str)
                     (string-match "\\([^\000]*>\\)\\{10\\}" str))
                )
        (setq score (- score 50)))
      (>= score 0)
      )))

;;; mark a string translatable by putting it in a eval_gettext clause
(defun translatable-mark (start end)
  (interactive "r")
  (let ((limit (copy-marker end)))
    (save-excursion
      (undo-boundary)
      (setq buffer-undo-list (cons (point) buffer-undo-list))
      (goto-char start)
      (while (search-forward "$" limit t)
        (replace-match "\\\\$"))
      (goto-char limit)
      (insert "\")")
      (goto-char start)
      (insert "$(eval_gettext \""))
    (syntax-ppss-flush-cache (point-min))
    ))

;;; highlight a region
(defun translatable-highlight (start end)
  (goto-char start)
  (transient-mark-mode nil)
  (push-mark)
  (goto-char end)
  (transient-mark-mode t))

;;; search and mark translatable string
(defun translatable ()
  (interactive)
  (let ((ret nil)
        (start nil)
        (limit nil)
        (start2 nil)
        (limit2 nil))
    (while (setq ret (translatable-search))
      (save-excursion
        (setq start (car ret))
        (setq limit (copy-marker (cadr ret)))
        (translatable-highlight start limit)
        (if (and (translatable-analyze start limit)
                 (y-or-n-p "Mark this string translatable? "))
            (translatable-mark start limit)
          (progn 
            (goto-char start)
            (while (setq ret (translatable-search-embedded limit))
              (save-excursion
                (setq start2 (car ret))
                (setq limit2 (copy-marker (cadr ret)))
                (translatable-highlight start2 limit2)
                (when (and (translatable-analyze start2 limit2)
                           (y-or-n-p "Mark this string translatable? "))
                  (translatable-mark start2 limit2))
                ))))
        (kill-region start limit)
        (yank)
        ))))


(add-hook 
 'sh-mode-hook
 (lambda () 
   (local-set-key [f6] 'translatable-mark)
   (local-set-key [f7] 'translatable)
   (local-set-key [f5] 
                  (lambda ()
                    (interactive)
                    (let ((start nil)
                          (limit nil))
                      (setq ret (translatable-search-embedded (point-max)))
                      (save-excursion
                        (translatable-mark (car ret) (cadr ret))
                        ))))
   ))
   

(provide 'translatable)

