;;; ox-i18n.el --- Internationalization Bindings for Org Export Framework ;; Copyright (C) 2013 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou ;; Keywords: outlines, hypermedia, calendar, wp ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; This library introduces variables and functions for ;; internationalization of Org mode buffer during export. It ;; comprises two parts: "Translations" and "Smart Quotes". ;;; Code: ;;; Translations ;; ;; `org-export-translate' translates a string according to language ;; specified by LANGUAGE keyword or `org-export-language-setup' ;; variable and a specified charset. `org-export-dictionary' contains ;; the dictionary used for the translation. (defconst org-export-dictionary '(("%e %n: %c" ("fr" :default "%e %n : %c" :html "%e %n : %c")) ("Author" ("ca" :default "Autor") ("cs" :default "Autor") ("da" :default "Ophavsmand") ("de" :default "Autor") ("eo" :html "Aŭtoro") ("es" :default "Autor") ("fi" :html "Tekijä") ("fr" :default "Auteur") ("hu" :default "Szerzõ") ("is" :html "Höfundur") ("it" :default "Autore") ("ja" :html "著者" :utf-8 "著者") ("nl" :default "Auteur") ("no" :default "Forfatter") ("nb" :default "Forfatter") ("nn" :default "Forfattar") ("pl" :default "Autor") ("ru" :html "Автор" :utf-8 "Автор") ("sv" :html "Författare") ("uk" :html "Автор" :utf-8 "Автор") ("zh-CN" :html "作者" :utf-8 "作者") ("zh-TW" :html "作者" :utf-8 "作者")) ("Date" ("ca" :default "Data") ("cs" :default "Datum") ("da" :default "Dato") ("de" :default "Datum") ("eo" :default "Dato") ("es" :default "Fecha") ("fi" :html "Päivämäärä") ("hu" :html "Dátum") ("is" :default "Dagsetning") ("it" :default "Data") ("ja" :html "日付" :utf-8 "日付") ("nl" :default "Datum") ("no" :default "Dato") ("nb" :default "Dato") ("nn" :default "Dato") ("pl" :default "Data") ("ru" :html "Дата" :utf-8 "Дата") ("sv" :default "Datum") ("uk" :html "Дата" :utf-8 "Дата") ("zh-CN" :html "日期" :utf-8 "日期") ("zh-TW" :html "日期" :utf-8 "日期")) ("Equation" ("de" :default "Gleichung") ("es" :html "Ecuación" :default "Ecuación") ("fr" :ascii "Equation" :default "Équation")) ("Figure" ("de" :default "Abbildung") ("es" :default "Figura") ("ja" :html "図" :utf-8 "図")) ("Figure %d:" ("de" :default "Abbildung %d:") ("es" :default "Figura %d:") ("fr" :default "Figure %d :" :html "Figure %d :") ("ja" :html "図%d:" :utf-8 "図%d:")) ("Footnotes" ("ca" :html "Peus de pàgina") ("cs" :default "Pozn\xe1mky pod carou") ("da" :default "Fodnoter") ("de" :html "Fußnoten" :default "Fußnoten") ("eo" :default "Piednotoj") ("es" :html "Nota al pie de página" :default "Nota al pie de página") ("fi" :default "Alaviitteet") ("fr" :default "Notes de bas de page") ("hu" :html "Lábjegyzet") ("is" :html "Aftanmálsgreinar") ("it" :html "Note a piè di pagina") ("ja" :html "脚注" :utf-8 "脚注") ("nl" :default "Voetnoten") ("no" :default "Fotnoter") ("nb" :default "Fotnoter") ("nn" :default "Fotnotar") ("pl" :default "Przypis") ("ru" :html "Сноски" :utf-8 "Сноски") ("sv" :default "Fotnoter") ("uk" :html "Примітки" :utf-8 "Примітки") ("zh-CN" :html "脚注" :utf-8 "脚注") ("zh-TW" :html "腳註" :utf-8 "腳註")) ("List of Listings" ("de" :default "Programmauflistungsverzeichnis") ("es" :default "Indice de Listados de programas") ("fr" :default "Liste des programmes")) ("List of Tables" ("de" :default "Tabellenverzeichnis") ("es" :default "Indice de tablas") ("fr" :default "Liste des tableaux")) ("Listing %d:" ("de" :default "Programmlisting %d") ("es" :default "Listado de programa %d") ("fr" :default "Programme %d :" :html "Programme %d :")) ("See section %s" ("de" :default "siehe Abschnitt %s") ("es" :default "vea seccion %s") ("fr" :default "cf. section %s")) ("Table" ("de" :default "Tabelle") ("es" :default "Tabla") ("fr" :default "Tableau") ("ja" :html "表" :utf-8 "表")) ("Table %d:" ("de" :default "Tabelle %d") ("es" :default "Tabla %d") ("fr" :default "Tableau %d :") ("ja" :html "表%d:" :utf-8 "表%d:")) ("Table of Contents" ("ca" :html "Índex") ("cs" :default "Obsah") ("da" :default "Indhold") ("de" :default "Inhaltsverzeichnis") ("eo" :default "Enhavo") ("es" :html "Índice") ("fi" :html "Sisällysluettelo") ("fr" :ascii "Sommaire" :default "Table des matières") ("hu" :html "Tartalomjegyzék") ("is" :default "Efnisyfirlit") ("it" :default "Indice") ("ja" :html "目次" :utf-8 "目次") ("nl" :default "Inhoudsopgave") ("no" :default "Innhold") ("nb" :default "Innhold") ("nn" :default "Innhald") ("pl" :html "Spis treści") ("ru" :html "Содержание" :utf-8 "Содержание") ("sv" :html "Innehåll") ("uk" :html "Зміст" :utf-8 "Зміст") ("zh-CN" :html "目录" :utf-8 "目录") ("zh-TW" :html "目錄" :utf-8 "目錄")) ("Unknown reference" ("de" :default "Unbekannter Verweis") ("es" :default "referencia desconocida") ("fr" :ascii "Destination inconnue" :default "Référence inconnue"))) "Dictionary for export engine. Alist whose CAR is the string to translate and CDR is an alist whose CAR is the language string and CDR is a plist whose properties are possible charsets and values translated terms. It is used as a database for `org-export-translate'. Since this function returns the string as-is if no translation was found, the variable only needs to record values different from the entry.") (defun org-export-translate (s encoding info) "Translate string S according to language specification. ENCODING is a symbol among `:ascii', `:html', `:latex', `:latin1' and `:utf-8'. INFO is a plist used as a communication channel. Translation depends on `:language' property. Return the translated string. If no translation is found, try to fall back to `:default' encoding. If it fails, return S." (let* ((lang (plist-get info :language)) (translations (cdr (assoc lang (cdr (assoc s org-export-dictionary)))))) (or (plist-get translations encoding) (plist-get translations :default) s))) ;;; Smart Quotes ;; ;; The main function for the smart quotes sub-system is ;; `org-export-activate-smart-quotes', which replaces every quote in ;; a given string from the parse tree with its "smart" counterpart. ;; ;; Dictionary for smart quotes is stored in ;; `org-export-smart-quotes-alist'. ;; ;; Internally, regexps matching potential smart quotes (checks at ;; string boundaries are also necessary) are defined in ;; `org-export-smart-quotes-regexps'. (defconst org-export-smart-quotes-alist '(("de" (opening-double-quote :utf-8 "„" :html "„" :latex "\"`" :texinfo "@quotedblbase{}") (closing-double-quote :utf-8 "“" :html "“" :latex "\"'" :texinfo "@quotedblleft{}") (opening-single-quote :utf-8 "‚" :html "‚" :latex "\\glq{}" :texinfo "@quotesinglbase{}") (closing-single-quote :utf-8 "‘" :html "‘" :latex "\\grq{}" :texinfo "@quoteleft{}") (apostrophe :utf-8 "’" :html "’")) ("en" (opening-double-quote :utf-8 "“" :html "“" :latex "``" :texinfo "``") (closing-double-quote :utf-8 "”" :html "”" :latex "''" :texinfo "''") (opening-single-quote :utf-8 "‘" :html "‘" :latex "`" :texinfo "`") (closing-single-quote :utf-8 "’" :html "’" :latex "'" :texinfo "'") (apostrophe :utf-8 "’" :html "’")) ("es" (opening-double-quote :utf-8 "«" :html "«" :latex "\\guillemotleft{}" :texinfo "@guillemetleft{}") (closing-double-quote :utf-8 "»" :html "»" :latex "\\guillemotright{}" :texinfo "@guillemetright{}") (opening-single-quote :utf-8 "“" :html "“" :latex "``" :texinfo "``") (closing-single-quote :utf-8 "”" :html "”" :latex "''" :texinfo "''") (apostrophe :utf-8 "’" :html "’")) ("fr" (opening-double-quote :utf-8 "« " :html "« " :latex "\\og " :texinfo "@guillemetleft{}@tie{}") (closing-double-quote :utf-8 " »" :html " »" :latex "\\fg{}" :texinfo "@tie{}@guillemetright{}") (opening-single-quote :utf-8 "« " :html "« " :latex "\\og " :texinfo "@guillemetleft{}@tie{}") (closing-single-quote :utf-8 " »" :html " »" :latex "\\fg{}" :texinfo "@tie{}@guillemetright{}") (apostrophe :utf-8 "’" :html "’"))) "Smart quotes translations. Alist whose CAR is a language string and CDR is an alist with quote type as key and a plist associating various encodings to their translation as value. A quote type can be any symbol among `opening-double-quote', `closing-double-quote', `opening-single-quote', `closing-single-quote' and `apostrophe'. Valid encodings include `:utf-8', `:html', `:latex' and `:texinfo'. If no translation is found, the quote character is left as-is.") (defconst org-export-smart-quotes-regexps (list ;; Possible opening quote at beginning of string. "\\`\\([\"']\\)\\(\\w\\|\\s.\\|\\s_\\)" ;; Possible closing quote at beginning of string. "\\`\\([\"']\\)\\(\\s-\\|\\s)\\|\\s.\\)" ;; Possible apostrophe at beginning of string. "\\`\\('\\)\\S-" ;; Opening single and double quotes. "\\(?:\\s-\\|\\s(\\)\\([\"']\\)\\(?:\\w\\|\\s.\\|\\s_\\)" ;; Closing single and double quotes. "\\(?:\\w\\|\\s.\\|\\s_\\)\\([\"']\\)\\(?:\\s-\\|\\s)\\|\\s.\\)" ;; Apostrophe. "\\S-\\('\\)\\S-" ;; Possible opening quote at end of string. "\\(?:\\s-\\|\\s(\\)\\([\"']\\)\\'" ;; Possible closing quote at end of string. "\\(?:\\w\\|\\s.\\|\\s_\\)\\([\"']\\)\\'" ;; Possible apostrophe at end of string. "\\S-\\('\\)\\'") "List of regexps matching a quote or an apostrophe. In every regexp, quote or apostrophe matched is put in group 1.") (declare-function org-export-get-next-element "org-export" (blob info &optional n)) (declare-function org-export-get-previous-element "org-export" (blob info &optional n)) (defun org-export-activate-smart-quotes (s encoding info &optional original) "Replace regular quotes with \"smart\" quotes in string S. ENCODING is a symbol among `:html', `:latex', `:texinfo' and `:utf-8'. INFO is a plist used as a communication channel. The function has to retrieve information about string surroundings in parse tree. It can only happen with an unmodified string. Thus, if S has already been through another process, a non-nil ORIGINAL optional argument will provide that original string. Return the new string." (if (equal s "") "" (let* ((prev (org-export-get-previous-element (or original s) info)) ;; Try to be flexible when computing number of blanks ;; before object. The previous object may be a string ;; introduced by the back-end and not completely parsed. (pre-blank (and prev (or (org-element-property :post-blank prev) ;; A string with missing `:post-blank' ;; property. (and (stringp prev) (string-match " *\\'" prev) (length (match-string 0 prev))) ;; Fallback value. 0))) (next (org-export-get-next-element (or original s) info)) (get-smart-quote (lambda (q type) ;; Return smart quote associated to a give quote Q, as ;; a string. TYPE is a symbol among `open', `close' and ;; `apostrophe'. (let ((key (case type (apostrophe 'apostrophe) (open (if (equal "'" q) 'opening-single-quote 'opening-double-quote)) (otherwise (if (equal "'" q) 'closing-single-quote 'closing-double-quote))))) (or (plist-get (cdr (assq key (cdr (assoc (plist-get info :language) org-export-smart-quotes-alist)))) encoding) q))))) (if (or (equal "\"" s) (equal "'" s)) ;; Only a quote: no regexp can match. We have to check both ;; sides and decide what to do. (cond ((and (not prev) (not next)) s) ((not prev) (funcall get-smart-quote s 'open)) ((and (not next) (zerop pre-blank)) (funcall get-smart-quote s 'close)) ((not next) s) ((zerop pre-blank) (funcall get-smart-quote s 'apostrophe)) (t (funcall get-smart-quote 'open))) ;; 1. Replace quote character at the beginning of S. (cond ;; Apostrophe? ((and prev (zerop pre-blank) (string-match (nth 2 org-export-smart-quotes-regexps) s)) (setq s (replace-match (funcall get-smart-quote (match-string 1 s) 'apostrophe) nil t s 1))) ;; Closing quote? ((and prev (zerop pre-blank) (string-match (nth 1 org-export-smart-quotes-regexps) s)) (setq s (replace-match (funcall get-smart-quote (match-string 1 s) 'close) nil t s 1))) ;; Opening quote? ((and (or (not prev) (> pre-blank 0)) (string-match (nth 0 org-export-smart-quotes-regexps) s)) (setq s (replace-match (funcall get-smart-quote (match-string 1 s) 'open) nil t s 1)))) ;; 2. Replace quotes in the middle of the string. (setq s (replace-regexp-in-string ;; Opening quotes. (nth 3 org-export-smart-quotes-regexps) (lambda (text) (funcall get-smart-quote (match-string 1 text) 'open)) s nil t 1)) (setq s (replace-regexp-in-string ;; Closing quotes. (nth 4 org-export-smart-quotes-regexps) (lambda (text) (funcall get-smart-quote (match-string 1 text) 'close)) s nil t 1)) (setq s (replace-regexp-in-string ;; Apostrophes. (nth 5 org-export-smart-quotes-regexps) (lambda (text) (funcall get-smart-quote (match-string 1 text) 'apostrophe)) s nil t 1)) ;; 3. Replace quote character at the end of S. (cond ;; Apostrophe? ((and next (string-match (nth 8 org-export-smart-quotes-regexps) s)) (setq s (replace-match (funcall get-smart-quote (match-string 1 s) 'apostrophe) nil t s 1))) ;; Closing quote? ((and (not next) (string-match (nth 7 org-export-smart-quotes-regexps) s)) (setq s (replace-match (funcall get-smart-quote (match-string 1 s) 'close) nil t s 1))) ;; Opening quote? ((and next (string-match (nth 6 org-export-smart-quotes-regexps) s)) (setq s (replace-match (funcall get-smart-quote (match-string 1 s) 'open) nil t s 1)))) ;; Return string with smart quotes. s)))) (provide 'ox-i18n) ;;; ox-i18n.el ends here