git.iko.soy/typeable/pandoc
on master help sign in
Added support for translations (localization) (see #3559).

* readDataFile, readDefaultDataFile, getReferenceDocx, getReferenceODT have been removed from Shared and moved into Class. They are now defined in terms of PandocMonad primitives, rather than being primitve methods of the class. * toLang has been moved from BCP47 to Class. * NoTranslation and CouldNotLoudTranslations have been added to LogMessage. * New module, Text.Pandoc.Translations, exporting Term, Translations, readTranslations. * New functions in Class: translateTerm, setTranslations. Note that nothing is loaded from data files until translateTerm is used; setTranslation just sets the language to be used. * Added two translation data files in data/translations. * LaTeX reader: Support `\setmainlanguage` or `\setdefaultlanguage` (polyglossia) and `\figurename`.

commit74212eb1b0e1757fc0ac3e5d45b0ee18bac491e5 authorJohn MacFarlane <jgm@berkeley.edu> dateFri 11 Aug 2017, 11:56 (9 years ago) parent7892dcd
16 files changed+682−358
data/translations/en.trans+19 data/translations/fr.trans+19 pandoc.cabal+4−1 src/Text/Pandoc/App.hs+175−166 src/Text/Pandoc/BCP47.hs−14 src/Text/Pandoc/Class.hs+212−46 src/Text/Pandoc/Logging.hs+14 src/Text/Pandoc/Lua/PandocModule.hs+3−3 src/Text/Pandoc/Readers/LaTeX.hs+133−2 src/Text/Pandoc/Shared.hs+1−118 src/Text/Pandoc/Templates.hs+1−1 src/Text/Pandoc/Translations.hs+94 src/Text/Pandoc/Writers/ConTeXt.hs+1−1 src/Text/Pandoc/Writers/Docx.hs+2−2 src/Text/Pandoc/Writers/LaTeX.hs+2−2 src/Text/Pandoc/Writers/ODT.hs+2−2
data/translations/en.transnew file+19
@@ -0,0 +1,19 @@
1+Preface: Preface
2+References: References
3+Abstract: Abstract
4+Bibliography: Bibliography
5+Chapter: Chapter
6+Appendix: Appendix
7+Contents: Contents
8+ListOfFigures: ListOfFigures
9+ListOfTables: ListOfTables
10+Index: Index
11+Figure: Figure
12+Table: Table
13+Part: Part
14+Page: page
15+Proof: Proof
16+See: see
17+SeeAlso: see also
18+Cc: cc
19+To: To
data/translations/fr.transnew file+19
@@ -0,0 +1,19 @@
1+Preface: Préface
2+References: Références
3+Abstract: Résumé
4+Bibliography: Bibliographie
5+Chapter: Chaptire
6+Appendix: Annexe
7+Contents: Table des matières
8+ListOfFigures: Table des figures
9+ListOfTables: Liste des tableaux
10+Index: Index
11+Figure: Fig.
12+Table: Tab.
13+Part: partie
14+Page: page
15+Proof: Démonstration
16+See: voir
17+SeeAlso: voir aussi
18+Cc: Copie à
19+To:
pandoc.cabal+4−1
@@ -72,6 +72,8 @@ Data-Files:
7272 data/templates/default.org
7373 data/templates/default.epub2
7474 data/templates/default.epub3
75+ -- translations
76+ data/translations/*.trans
7577 -- source files for reference.docx
7678 data/docx/[Content_Types].xml
7779 data/docx/_rels/.rels
@@ -469,7 +471,8 @@ Library
469471 Text.Pandoc.CSS,
470472 Text.Pandoc.CSV,
471473 Text.Pandoc.UUID,
472− Text.Pandoc.BCP47
474+ Text.Pandoc.BCP47,
475+ Text.Pandoc.Translations,
473476 Text.Pandoc.Slides,
474477 Text.Pandoc.Compat.Time,
475478 Paths_pandoc
src/Text/Pandoc/App.hs+175−166
@@ -41,7 +41,7 @@ module Text.Pandoc.App (
4141import Control.Applicative ((<|>))
4242import qualified Control.Exception as E
4343import Control.Monad
44−import Control.Monad.Except (throwError)
44+import Control.Monad.Except (throwError, catchError)
4545import Control.Monad.Trans
4646import Data.Monoid
4747import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode',
@@ -71,21 +71,22 @@ import System.Environment (getArgs, getEnvironment, getProgName)
7171import System.Exit (ExitCode (..), exitSuccess)
7272import System.FilePath
7373import System.IO (nativeNewline, stdout)
74−import qualified System.IO as IO (Newline (..))
7574import System.IO.Error (isDoesNotExistError)
75+import qualified System.IO as IO (Newline (..))
7676import Text.Pandoc
7777import Text.Pandoc.Builder (setMeta)
7878import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog,
7979 setResourcePath, getMediaBag, setTrace, report,
80− setUserDataDir)
80+ setUserDataDir, readFileStrict, readDataFile,
81+ readDefaultDataFile, setTranslations)
8182import Text.Pandoc.Highlighting (highlightingStyles)
83+import Text.Pandoc.BCP47 (parseBCP47, Lang(..))
8284import Text.Pandoc.Lua (runLuaFilter, LuaException(..))
8385import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL)
8486import Text.Pandoc.PDF (makePDF)
8587import Text.Pandoc.Process (pipeProcess)
8688import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained)
87−import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile,
88− readDataFileUTF8, readDefaultDataFile,
89+import Text.Pandoc.Shared (headerShift, isURI, openURL,
8990 safeRead, tabFilter, eastAsianLineBreakFilter)
9091import qualified Text.Pandoc.UTF8 as UTF8
9192import Text.Pandoc.XML (toEntities)
@@ -215,84 +216,6 @@ convertWithOpts opts = do
215216 _ -> e
216217
217218 let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
218−
219− templ <- case optTemplate opts of
220− _ | not standalone -> return Nothing
221− Nothing -> do
222− deftemp <- runIO $ do
223− setUserDataDir datadir
224− getDefaultTemplate format
225− case deftemp of
226− Left e -> E.throwIO e
227− Right t -> return (Just t)
228− Just tp -> do
229− -- strip off extensions
230− let tp' = case takeExtension tp of
231− "" -> tp <.> format
232− _ -> tp
233− Just <$> E.catch (UTF8.readFile tp')
234− (\e -> if isDoesNotExistError e
235− then E.catch
236− (readDataFileUTF8 datadir
237− ("templates" </> tp'))
238− (\e' -> let _ = (e' :: E.SomeException)
239− in E.throwIO e')
240− else E.throwIO e)
241−
242− let addStringAsVariable varname s vars = return $ (varname, s) : vars
243−
244− let addContentsAsVariable varname fp vars = do
245− s <- UTF8.readFile fp
246− return $ (varname, s) : vars
247−
248− -- note: this reverses the list constructed in option parsing,
249− -- which in turn was reversed from the command-line order,
250− -- so we end up with the correct order in the variable list:
251− let withList _ [] vars = return vars
252− withList f (x:xs) vars = f x vars >>= withList f xs
253−
254− variables <-
255−
256− withList (addStringAsVariable "sourcefile")
257− (reverse $ optInputFiles opts) (("outputfile", optOutputFile opts) : optVariables opts)
258− -- we reverse this list because, unlike
259− -- the other option lists here, it is
260− -- not reversed when parsed from CLI arguments.
261− -- See withList, above.
262− >>=
263− withList (addContentsAsVariable "include-before")
264− (optIncludeBeforeBody opts)
265− >>=
266− withList (addContentsAsVariable "include-after")
267− (optIncludeAfterBody opts)
268− >>=
269− withList (addContentsAsVariable "header-includes")
270− (optIncludeInHeader opts)
271− >>=
272− withList (addStringAsVariable "css") (optCss opts)
273− >>=
274− maybe return (addStringAsVariable "title-prefix") (optTitlePrefix opts)
275− >>=
276− maybe return (addStringAsVariable "epub-cover-image")
277− (optEpubCoverImage opts)
278− >>=
279− (\vars -> case mathMethod of
280− LaTeXMathML Nothing -> do
281− s <- readDataFileUTF8 datadir "LaTeXMathML.js"
282− return $ ("mathml-script", s) : vars
283− _ -> return vars)
284− >>=
285− (\vars -> if format == "dzslides"
286− then do
287− dztempl <- readDataFileUTF8 datadir
288− ("dzslides" </> "template.html")
289− let dzline = "<!-- {{{{ dzslides core"
290− let dzcore = unlines
291− $ dropWhile (not . (dzline `isPrefixOf`))
292− $ lines dztempl
293− return $ ("dzslides-core", dzcore) : vars
294− else return vars)
295−
296219 let sourceURL = case sources of
297220 [] -> Nothing
298221 (x:_) -> case parseURI x of
@@ -302,21 +225,7 @@ convertWithOpts opts = do
302225 uriFragment = "" }
303226 _ -> Nothing
304227
305− abbrevs <- (Set.fromList . filter (not . null) . lines) <$>
306− case optAbbreviations opts of
307− Nothing -> readDataFileUTF8 datadir "abbreviations"
308− Just f -> UTF8.readFile f
309−
310− let readerOpts = def{ readerStandalone = standalone
311− , readerColumns = optColumns opts
312− , readerTabStop = optTabStop opts
313− , readerIndentedCodeClasses = optIndentedCodeClasses opts
314− , readerDefaultImageExtension =
315− optDefaultImageExtension opts
316− , readerTrackChanges = optTrackChanges opts
317− , readerAbbreviations = abbrevs
318− , readerExtensions = readerExts
319− }
228+ let addStringAsVariable varname s vars = return $ (varname, s) : vars
320229
321230 highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts
322231 let addSyntaxMap existingmap f = do
@@ -336,40 +245,6 @@ convertWithOpts opts = do
336245 (\(syn,dep) -> (T.unpack syn ++ " requires " ++
337246 T.unpack dep ++ " through IncludeRules.")) xs)
338247
339− let writerOptions = def { writerTemplate = templ,
340− writerVariables = variables,
341− writerTabStop = optTabStop opts,
342− writerTableOfContents = optTableOfContents opts,
343− writerHTMLMathMethod = mathMethod,
344− writerIncremental = optIncremental opts,
345− writerCiteMethod = optCiteMethod opts,
346− writerNumberSections = optNumberSections opts,
347− writerNumberOffset = optNumberOffset opts,
348− writerSectionDivs = optSectionDivs opts,
349− writerExtensions = writerExts,
350− writerReferenceLinks = optReferenceLinks opts,
351− writerReferenceLocation = optReferenceLocation opts,
352− writerDpi = optDpi opts,
353− writerWrapText = optWrapText opts,
354− writerColumns = optColumns opts,
355− writerEmailObfuscation = optEmailObfuscation opts,
356− writerIdentifierPrefix = optIdentifierPrefix opts,
357− writerSourceURL = sourceURL,
358− writerHtmlQTags = optHtmlQTags opts,
359− writerTopLevelDivision = optTopLevelDivision opts,
360− writerListings = optListings opts,
361− writerSlideLevel = optSlideLevel opts,
362− writerHighlightStyle = highlightStyle,
363− writerSetextHeaders = optSetextHeaders opts,
364− writerEpubSubdirectory = optEpubSubdirectory opts,
365− writerEpubMetadata = epubMetadata,
366− writerEpubFonts = optEpubFonts opts,
367− writerEpubChapterLevel = optEpubChapterLevel opts,
368− writerTOCDepth = optTOCDepth opts,
369− writerReferenceDoc = optReferenceDoc opts,
370− writerLaTeXArgs = optLaTeXEngineArgs opts,
371− writerSyntaxMap = syntaxMap
372− }
373248
374249
375250#ifdef _WINDOWS
@@ -383,18 +258,6 @@ convertWithOpts opts = do
383258 "Specify an output file using the -o option."
384259
385260
386− let transforms = (case optBaseHeaderLevel opts of
387− x | x > 1 -> (headerShift (x - 1) :)
388− | otherwise -> id) $
389− (if extensionEnabled Ext_east_asian_line_breaks
390− readerExts &&
391− not (extensionEnabled Ext_east_asian_line_breaks
392− writerExts &&
393− writerWrapText writerOptions == WrapPreserve)
394− then (eastAsianLineBreakFilter :)
395− else id)
396− []
397−
398261 let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t"
399262 then 0
400263 else optTabStop opts)
@@ -419,32 +282,175 @@ convertWithOpts opts = do
419282 E.throwIO PandocFailOnWarningError
420283 return res
421284
422− let sourceToDoc :: [FilePath] -> PandocIO Pandoc
423− sourceToDoc sources' =
424− case reader of
425− TextReader r
426− | optFileScope opts || readerName == "json" ->
427− mconcat <$> mapM (readSource >=> r readerOpts) sources
428− | otherwise ->
429− readSources sources' >>= r readerOpts
430− ByteStringReader r ->
431− mconcat <$> mapM (readFile' >=> r readerOpts) sources
432−
433− metadata <- if format == "jats" &&
434− isNothing (lookup "csl" (optMetadata opts)) &&
435− isNothing (lookup "citation-style" (optMetadata opts))
436− then do
437− jatsCSL <- readDataFile datadir "jats.csl"
438− let jatsEncoded = makeDataURI ("application/xml", jatsCSL)
439− return $ ("csl", jatsEncoded) : optMetadata opts
440− else return $ optMetadata opts
441−
442285 let eol = case optEol opts of
443286 CRLF -> IO.CRLF
444287 LF -> IO.LF
445288 Native -> nativeNewline
446289
290+ -- note: this reverses the list constructed in option parsing,
291+ -- which in turn was reversed from the command-line order,
292+ -- so we end up with the correct order in the variable list:
293+ let withList _ [] vars = return vars
294+ withList f (x:xs) vars = f x vars >>= withList f xs
295+
296+ let addContentsAsVariable varname fp vars = do
297+ s <- UTF8.toString <$> readFileStrict fp
298+ return $ (varname, s) : vars
299+
447300 runIO' $ do
301+ variables <-
302+ withList (addStringAsVariable "sourcefile")
303+ (reverse $ optInputFiles opts)
304+ (("outputfile", optOutputFile opts) : optVariables opts)
305+ -- we reverse this list because, unlike
306+ -- the other option lists here, it is
307+ -- not reversed when parsed from CLI arguments.
308+ -- See withList, above.
309+ >>=
310+ withList (addContentsAsVariable "include-before")
311+ (optIncludeBeforeBody opts)
312+ >>=
313+ withList (addContentsAsVariable "include-after")
314+ (optIncludeAfterBody opts)
315+ >>=
316+ withList (addContentsAsVariable "header-includes")
317+ (optIncludeInHeader opts)
318+ >>=
319+ withList (addStringAsVariable "css") (optCss opts)
320+ >>=
321+ maybe return (addStringAsVariable "title-prefix")
322+ (optTitlePrefix opts)
323+ >>=
324+ maybe return (addStringAsVariable "epub-cover-image")
325+ (optEpubCoverImage opts)
326+ >>=
327+ (\vars -> case mathMethod of
328+ LaTeXMathML Nothing -> do
329+ s <- UTF8.toString <$> readDataFile "LaTeXMathML.js"
330+ return $ ("mathml-script", s) : vars
331+ _ -> return vars)
332+ >>=
333+ (\vars -> if format == "dzslides"
334+ then do
335+ dztempl <- UTF8.toString <$> readDataFile
336+ ("dzslides" </> "template.html")
337+ let dzline = "<!-- {{{{ dzslides core"
338+ let dzcore = unlines
339+ $ dropWhile (not . (dzline `isPrefixOf`))
340+ $ lines dztempl
341+ return $ ("dzslides-core", dzcore) : vars
342+ else return vars)
343+
344+ abbrevs <- (Set.fromList . filter (not . null) . lines) <$>
345+ case optAbbreviations opts of
346+ Nothing -> UTF8.toString <$> readDataFile "abbreviations"
347+ Just f -> UTF8.toString <$> readFileStrict f
348+
349+ templ <- case optTemplate opts of
350+ _ | not standalone -> return Nothing
351+ Nothing -> Just <$> getDefaultTemplate format
352+ Just tp -> do
353+ -- strip off extensions
354+ let tp' = case takeExtension tp of
355+ "" -> tp <.> format
356+ _ -> tp
357+ Just . UTF8.toString <$>
358+ (readFileStrict tp' `catchError`
359+ (\e ->
360+ case e of
361+ PandocIOError _ e' |
362+ isDoesNotExistError e' ->
363+ readDataFile ("templates" </> tp')
364+ _ -> throwError e))
365+
366+ metadata <- if format == "jats" &&
367+ isNothing (lookup "csl" (optMetadata opts)) &&
368+ isNothing (lookup "citation-style" (optMetadata opts))
369+ then do
370+ jatsCSL <- readDataFile "jats.csl"
371+ let jatsEncoded = makeDataURI
372+ ("application/xml", jatsCSL)
373+ return $ ("csl", jatsEncoded) : optMetadata opts
374+ else return $ optMetadata opts
375+
376+ case lookup "lang" (optMetadata opts) of
377+ Just l -> case parseBCP47 l of
378+ Left _ -> return ()
379+ Right l' -> setTranslations l'
380+ Nothing -> setTranslations $ Lang "en" "" "US" []
381+
382+ let writerOptions = def {
383+ writerTemplate = templ
384+ , writerVariables = variables
385+ , writerTabStop = optTabStop opts
386+ , writerTableOfContents = optTableOfContents opts
387+ , writerHTMLMathMethod = mathMethod
388+ , writerIncremental = optIncremental opts
389+ , writerCiteMethod = optCiteMethod opts
390+ , writerNumberSections = optNumberSections opts
391+ , writerNumberOffset = optNumberOffset opts
392+ , writerSectionDivs = optSectionDivs opts
393+ , writerExtensions = writerExts
394+ , writerReferenceLinks = optReferenceLinks opts
395+ , writerReferenceLocation = optReferenceLocation opts
396+ , writerDpi = optDpi opts
397+ , writerWrapText = optWrapText opts
398+ , writerColumns = optColumns opts
399+ , writerEmailObfuscation = optEmailObfuscation opts
400+ , writerIdentifierPrefix = optIdentifierPrefix opts
401+ , writerSourceURL = sourceURL
402+ , writerHtmlQTags = optHtmlQTags opts
403+ , writerTopLevelDivision = optTopLevelDivision opts
404+ , writerListings = optListings opts
405+ , writerSlideLevel = optSlideLevel opts
406+ , writerHighlightStyle = highlightStyle
407+ , writerSetextHeaders = optSetextHeaders opts
408+ , writerEpubSubdirectory = optEpubSubdirectory opts
409+ , writerEpubMetadata = epubMetadata
410+ , writerEpubFonts = optEpubFonts opts
411+ , writerEpubChapterLevel = optEpubChapterLevel opts
412+ , writerTOCDepth = optTOCDepth opts
413+ , writerReferenceDoc = optReferenceDoc opts
414+ , writerLaTeXArgs = optLaTeXEngineArgs opts
415+ , writerSyntaxMap = syntaxMap
416+ }
417+
418+ let readerOpts = def{
419+ readerStandalone = standalone
420+ , readerColumns = optColumns opts
421+ , readerTabStop = optTabStop opts
422+ , readerIndentedCodeClasses = optIndentedCodeClasses opts
423+ , readerDefaultImageExtension =
424+ optDefaultImageExtension opts
425+ , readerTrackChanges = optTrackChanges opts
426+ , readerAbbreviations = abbrevs
427+ , readerExtensions = readerExts
428+ }
429+
430+ let transforms = (case optBaseHeaderLevel opts of
431+ x | x > 1 -> (headerShift (x - 1) :)
432+ | otherwise -> id) $
433+ (if extensionEnabled Ext_east_asian_line_breaks
434+ readerExts &&
435+ not (extensionEnabled Ext_east_asian_line_breaks
436+ writerExts &&
437+ writerWrapText writerOptions == WrapPreserve)
438+ then (eastAsianLineBreakFilter :)
439+ else id)
440+ []
441+
442+ let sourceToDoc :: [FilePath] -> PandocIO Pandoc
443+ sourceToDoc sources' =
444+ case reader of
445+ TextReader r
446+ | optFileScope opts || readerName == "json" ->
447+ mconcat <$> mapM (readSource >=> r readerOpts) sources
448+ | otherwise ->
449+ readSources sources' >>= r readerOpts
450+ ByteStringReader r ->
451+ mconcat <$> mapM (readFile' >=> r readerOpts) sources
452+
453+
448454 setUserDataDir datadir
449455 when (readerName == "markdown_github" ||
450456 writerName == "markdown_github") $
@@ -1011,7 +1017,8 @@ options =
10111017 , Option "" ["print-default-data-file"]
10121018 (ReqArg
10131019 (\arg _ -> do
1014− readDefaultDataFile arg >>= BS.hPutStr stdout
1020+ runIOorExplode $
1021+ readDefaultDataFile arg >>= liftIO . BS.hPutStr stdout
10151022 exitSuccess)
10161023 "FILE")
10171024 "" -- "Print default data file"
@@ -1469,7 +1476,9 @@ options =
14691476 (NoArg
14701477 (\_ -> do
14711478 ddir <- getDataDir
1472− tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
1479+ tpl <- runIOorExplode $
1480+ UTF8.toString <$>
1481+ readDefaultDataFile "bash_completion.tpl"
14731482 let optnames (Option shorts longs _ _) =
14741483 map (\c -> ['-',c]) shorts ++
14751484 map ("--" ++) longs
src/Text/Pandoc/BCP47.hs−14
@@ -29,7 +29,6 @@ Functions for parsing and rendering BCP47 language identifiers.
2929-}
3030module Text.Pandoc.BCP47 (
3131 getLang
32− , toLang
3332 , parseBCP47
3433 , Lang(..)
3534 , renderLang
@@ -40,8 +39,6 @@ import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower,
4039 isAlphaNum)
4140import Data.List (intercalate)
4241import Text.Pandoc.Definition
43−import Text.Pandoc.Class (PandocMonad, report)
44−import Text.Pandoc.Logging
4542import Text.Pandoc.Options
4643import qualified Text.Parsec as P
4744
@@ -68,17 +65,6 @@ getLang opts meta =
6865 Just (MetaString s) -> Just s
6966 _ -> Nothing
7067
71−-- | Convert BCP47 string to a Lang, issuing warning
72−-- if there are problems.
73−toLang :: PandocMonad m => Maybe String -> m (Maybe Lang)
74−toLang Nothing = return Nothing
75−toLang (Just s) =
76− case parseBCP47 s of
77− Left _ -> do
78− report $ InvalidLang s
79− return Nothing
80− Right l -> return (Just l)
81−
8268-- | Parse a BCP 47 string as a Lang. Currently we parse
8369-- extensions and private-use fields as "variants," even
8470-- though officially they aren't.
src/Text/Pandoc/Class.hs+212−46
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveFunctor #-}
23{-# LANGUAGE TypeSynonymInstances #-}
34{-# LANGUAGE FlexibleInstances #-}
@@ -71,8 +72,15 @@ module Text.Pandoc.Class ( PandocMonad(..)
7172 , runIO
7273 , runIOorExplode
7374 , runPure
75+ , readDefaultDataFile
76+ , readDataFile
7477 , fillMediaBag
7578 , extractMedia
79+ , toLang
80+ , setTranslations
81+ , translateTerm
82+ , Translations(..)
83+ , Term(..)
7684 ) where
7785
7886import Prelude hiding (readFile)
@@ -81,10 +89,9 @@ import qualified System.Random as IO (newStdGen)
8189import Codec.Archive.Zip (Archive, fromArchive, emptyArchive)
8290import Data.Unique (hashUnique)
8391import qualified Data.Unique as IO (newUnique)
84−import qualified Text.Pandoc.Shared as IO ( readDataFile
85− , readDefaultDataFile
86− , openURL )
92+import qualified Text.Pandoc.Shared as Shared
8793import qualified Text.Pandoc.UTF8 as UTF8
94+import qualified System.Directory as Directory
8895import Text.Pandoc.Compat.Time (UTCTime)
8996import Text.Pandoc.Logging
9097import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName)
@@ -98,6 +105,7 @@ import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds
98105 , posixSecondsToUTCTime
99106 , POSIXTime )
100107import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc)
108+import qualified System.FilePath.Posix as Posix
101109import Network.URI ( escapeURIString, nonStrictRelativeTo,
102110 unEscapeString, parseURIReference, isAllowedInURI,
103111 parseURI, URI(..) )
@@ -111,7 +119,7 @@ import qualified System.Environment as IO (lookupEnv)
111119import System.FilePath.Glob (match, compile)
112120import System.Directory (createDirectoryIfMissing, getDirectoryContents,
113121 doesDirectoryExist)
114−import System.FilePath ((</>), (<.>), takeDirectory,
122+import System.FilePath ((</>), (<.>), takeDirectory, splitDirectories,
115123 takeExtension, dropExtension, isRelative, normalise)
116124import qualified System.FilePath.Glob as IO (glob)
117125import qualified System.Directory as IO (getModificationTime)
@@ -121,13 +129,21 @@ import Control.Monad.State.Strict
121129import Control.Monad.Except
122130import Control.Monad.Writer (WriterT)
123131import Control.Monad.RWS (RWST)
132+import Codec.Archive.Zip
124133import Data.Word (Word8)
125134import Data.Default
126135import System.IO.Error
127136import System.IO (stderr)
128137import qualified Data.Map as M
129138import Text.Pandoc.Error
139+import Text.Pandoc.BCP47 (Lang(..), parseBCP47, renderLang)
140+import Text.Pandoc.Translations (Term(..), Translations(..), readTranslations)
130141import qualified Debug.Trace
142+#ifdef EMBED_DATA_FILES
143+import Text.Pandoc.Data (dataFiles)
144+#else
145+import Paths_pandoc (getDataFileName)
146+#endif
131147
132148-- | The PandocMonad typeclass contains all the potentially
133149-- IO-related functions used in pandoc's readers and writers.
@@ -155,15 +171,12 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m)
155171 -- | Read the strict ByteString contents from a file path,
156172 -- raising an error on failure.
157173 readFileStrict :: FilePath -> m B.ByteString
158− -- | Read file from from Cabal data directory.
159− readDefaultDataFile :: FilePath -> m B.ByteString
160− -- | Read file from user data directory or,
161− -- if not found there, from Cabal data directory.
162− readDataFile :: FilePath -> m B.ByteString
163174 -- | Return a list of paths that match a glob, relative to
164175 -- the working directory. See 'System.FilePath.Glob' for
165176 -- the glob syntax.
166177 glob :: String -> m [FilePath]
178+ -- | Returns True if file exists.
179+ fileExists :: FilePath -> m Bool
167180 -- | Return the modification time of a file.
168181 getModificationTime :: FilePath -> m UTCTime
169182 -- | Get the value of the 'CommonState' used by all instances
@@ -272,6 +285,9 @@ data CommonState = CommonState { stLog :: [LogMessage]
272285 -- ^ Directory to search for data files
273286 , stMediaBag :: MediaBag
274287 -- ^ Media parsed from binary containers
288+ , stTranslations :: Maybe
289+ (Lang, Maybe Translations)
290+ -- ^ Translations for localization
275291 , stInputFiles :: Maybe [FilePath]
276292 -- ^ List of input files from command line
277293 , stOutputFile :: Maybe FilePath
@@ -290,6 +306,7 @@ instance Default CommonState where
290306 def = CommonState { stLog = []
291307 , stUserDataDir = Nothing
292308 , stMediaBag = mempty
309+ , stTranslations = Nothing
293310 , stInputFiles = Nothing
294311 , stOutputFile = Nothing
295312 , stResourcePath = ["."]
@@ -297,6 +314,71 @@ instance Default CommonState where
297314 , stTrace = False
298315 }
299316
317+-- | Convert BCP47 string to a Lang, issuing warning
318+-- if there are problems.
319+toLang :: PandocMonad m => Maybe String -> m (Maybe Lang)
320+toLang Nothing = return Nothing
321+toLang (Just s) =
322+ case parseBCP47 s of
323+ Left _ -> do
324+ report $ InvalidLang s
325+ return Nothing
326+ Right l -> return (Just l)
327+
328+-- | Select the language to use with 'translateTerm'.
329+-- Note that this does not read a translation file;
330+-- that is only done the first time 'translateTerm' is
331+-- used.
332+setTranslations :: PandocMonad m => Lang -> m ()
333+setTranslations lang =
334+ modifyCommonState $ \st -> st{ stTranslations = Just (lang, Nothing) }
335+
336+-- | Load term map.
337+getTranslations :: PandocMonad m => m Translations
338+getTranslations = do
339+ mbtrans <- getsCommonState stTranslations
340+ case mbtrans of
341+ Nothing -> return mempty -- no language defined
342+ Just (_, Just t) -> return t
343+ Just (lang, Nothing) -> do -- read from file
344+ let translationFile = "translations/" ++ renderLang lang ++ ".trans"
345+ let fallbackFile = "translations/" ++ langLanguage lang ++ ".trans"
346+ let getTrans bs =
347+ case readTranslations (UTF8.toString bs) of
348+ Left e -> do
349+ report $ CouldNotLoadTranslations (renderLang lang) e
350+ -- make sure we don't try again...
351+ modifyCommonState $ \st ->
352+ st{ stTranslations = Nothing }
353+ return mempty
354+ Right t -> do
355+ modifyCommonState $ \st ->
356+ st{ stTranslations = Just (lang, Just t) }
357+ return t
358+ catchError (readDataFile translationFile >>= getTrans)
359+ (\_ ->
360+ catchError (readDataFile fallbackFile >>= getTrans)
361+ (\e -> do
362+ report $ CouldNotLoadTranslations (renderLang lang)
363+ $ case e of
364+ PandocCouldNotFindDataFileError _ ->
365+ ("data file " ++ fallbackFile ++ " not found")
366+ _ -> ""
367+ -- make sure we don't try again...
368+ modifyCommonState $ \st -> st{ stTranslations = Nothing }
369+ return mempty))
370+
371+-- | Get a translation from the current term map.
372+-- Issue a warning if the term is not defined.
373+translateTerm :: PandocMonad m => Term -> m String
374+translateTerm term = do
375+ Translations termMap <- getTranslations
376+ case M.lookup term termMap of
377+ Just s -> return s
378+ Nothing -> do
379+ report $ NoTranslation (show term)
380+ return ""
381+
300382-- | Evaluate a 'PandocIO' operation.
301383runIO :: PandocIO a -> IO (Either PandocError a)
302384runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma
@@ -317,7 +399,7 @@ liftIOError :: (String -> IO a) -> String -> PandocIO a
317399liftIOError f u = do
318400 res <- liftIO $ tryIOError $ f u
319401 case res of
320− Left e -> throwError $ PandocIOError u e
402+ Left e -> throwError $ PandocIOError u e
321403 Right r -> return r
322404
323405instance PandocMonad PandocIO where
@@ -328,17 +410,15 @@ instance PandocMonad PandocIO where
328410 newUniqueHash = hashUnique <$> liftIO IO.newUnique
329411 openURL u = do
330412 report $ Fetching u
331− res <- liftIO (IO.openURL u)
413+ res <- liftIOError Shared.openURL u
332414 case res of
333415 Right r -> return r
334416 Left e -> throwError $ PandocHttpError u e
335417 readFileLazy s = liftIOError BL.readFile s
336418 readFileStrict s = liftIOError B.readFile s
337− readDefaultDataFile fname = liftIOError IO.readDefaultDataFile fname
338− readDataFile fname = do
339− datadir <- getUserDataDir
340− liftIOError (IO.readDataFile datadir) fname
341− glob = liftIO . IO.glob
419+
420+ glob = liftIOError IO.glob
421+ fileExists = liftIOError Directory.doesFileExist
342422 getModificationTime fp = liftIOError IO.getModificationTime fp
343423 getCommonState = PandocIO $ lift get
344424 putCommonState x = PandocIO $ lift $ put x
@@ -432,6 +512,109 @@ downloadOrRead sourceURL s =
432512 convertSlash '\\' = '/'
433513 convertSlash x = x
434514
515+getDefaultReferenceDocx :: PandocMonad m => m Archive
516+getDefaultReferenceDocx = do
517+ let paths = ["[Content_Types].xml",
518+ "_rels/.rels",
519+ "docProps/app.xml",
520+ "docProps/core.xml",
521+ "word/document.xml",
522+ "word/fontTable.xml",
523+ "word/footnotes.xml",
524+ "word/numbering.xml",
525+ "word/settings.xml",
526+ "word/webSettings.xml",
527+ "word/styles.xml",
528+ "word/_rels/document.xml.rels",
529+ "word/_rels/footnotes.xml.rels",
530+ "word/theme/theme1.xml"]
531+ let toLazy = BL.fromChunks . (:[])
532+ let pathToEntry path = do
533+ epochtime <- (floor . utcTimeToPOSIXSeconds) <$> getCurrentTime
534+ contents <- toLazy <$> readDataFile ("docx/" ++ path)
535+ return $ toEntry path epochtime contents
536+ datadir <- getUserDataDir
537+ mbArchive <- case datadir of
538+ Nothing -> return Nothing
539+ Just d -> do
540+ exists <- fileExists (d </> "reference.docx")
541+ if exists
542+ then return (Just (d </> "reference.docx"))
543+ else return Nothing
544+ case mbArchive of
545+ Just arch -> toArchive <$> readFileLazy arch
546+ Nothing -> foldr addEntryToArchive emptyArchive <$>
547+ mapM pathToEntry paths
548+
549+getDefaultReferenceODT :: PandocMonad m => m Archive
550+getDefaultReferenceODT = do
551+ let paths = ["mimetype",
552+ "manifest.rdf",
553+ "styles.xml",
554+ "content.xml",
555+ "meta.xml",
556+ "settings.xml",
557+ "Configurations2/accelerator/current.xml",
558+ "Thumbnails/thumbnail.png",
559+ "META-INF/manifest.xml"]
560+ let pathToEntry path = do epochtime <- floor `fmap` getPOSIXTime
561+ contents <- (BL.fromChunks . (:[])) `fmap`
562+ readDataFile ("odt/" ++ path)
563+ return $ toEntry path epochtime contents
564+ datadir <- getUserDataDir
565+ mbArchive <- case datadir of
566+ Nothing -> return Nothing
567+ Just d -> do
568+ exists <- fileExists (d </> "reference.odt")
569+ if exists
570+ then return (Just (d </> "reference.odt"))
571+ else return Nothing
572+ case mbArchive of
573+ Just arch -> toArchive <$> readFileLazy arch
574+ Nothing -> foldr addEntryToArchive emptyArchive <$>
575+ mapM pathToEntry paths
576+
577+-- | Read file from user data directory or,
578+-- if not found there, from Cabal data directory.
579+readDataFile :: PandocMonad m => FilePath -> m B.ByteString
580+readDataFile fname = do
581+ datadir <- getUserDataDir
582+ case datadir of
583+ Nothing -> readDefaultDataFile fname
584+ Just userDir -> do
585+ exists <- fileExists (userDir </> fname)
586+ if exists
587+ then readFileStrict (userDir </> fname)
588+ else readDefaultDataFile fname
589+
590+-- | Read file from from Cabal data directory.
591+readDefaultDataFile :: PandocMonad m => FilePath -> m B.ByteString
592+readDefaultDataFile "reference.docx" =
593+ (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferenceDocx
594+readDefaultDataFile "reference.odt" =
595+ (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferenceODT
596+readDefaultDataFile fname =
597+#ifdef EMBED_DATA_FILES
598+ case lookup (makeCanonical fname) dataFiles of
599+ Nothing -> throwError $ PandocCouldNotFindDataFileError fname
600+ Just contents -> return contents
601+ where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
602+ transformPathParts = reverse . foldl go []
603+ go as "." = as
604+ go (_:as) ".." = as
605+ go as x = x : as
606+#else
607+ getDataFileName fname' >>= checkExistence >>= readFileStrict
608+ where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname
609+
610+checkExistence :: PandocMonad m => FilePath -> m FilePath
611+checkExistence fn = do
612+ exists <- fileExists fn
613+ if exists
614+ then return fn
615+ else throwError $ PandocCouldNotFindDataFileError fn
616+#endif
617+
435618withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a
436619withPaths [] _ fp = throwError $ PandocResourceNotFound fp
437620withPaths (p:ps) action fp =
@@ -491,9 +674,8 @@ writeMedia dir mediabag subpath = do
491674 Nothing -> throwError $ PandocResourceNotFound subpath
492675 Just (_, bs) -> do
493676 report $ Extracting fullpath
494− liftIO $ do
495− createDirectoryIfMissing True $ takeDirectory fullpath
496− BL.writeFile fullpath bs
677+ liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath)
678+ liftIOError (\p -> BL.writeFile p bs) fullpath
497679
498680adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
499681adjustImagePath dir paths (Image attr lab (src, tit))
@@ -624,28 +806,17 @@ instance PandocMonad PandocPure where
624806 case infoFileContents <$> getFileInfo fp fps of
625807 Just bs -> return bs
626808 Nothing -> throwError $ PandocResourceNotFound fp
627− readDefaultDataFile "reference.docx" =
628− (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx
629− readDefaultDataFile "reference.odt" =
630− (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT
631− readDefaultDataFile fname = do
632− let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname
633− readFileStrict fname'
634− readDataFile fname = do
635− datadir <- getUserDataDir
636− case datadir of
637− Just userDir -> do
638− userDirFiles <- getsPureState stUserDataFiles
639− case infoFileContents <$> getFileInfo (userDir </> fname)
640− userDirFiles of
641− Just bs -> return bs
642− Nothing -> readDefaultDataFile fname
643− Nothing -> readDefaultDataFile fname
644809
645810 glob s = do
646811 FileTree ftmap <- getsPureState stFiles
647812 return $ filter (match (compile s)) $ M.keys ftmap
648813
814+ fileExists fp = do
815+ fps <- getsPureState stFiles
816+ case getFileInfo fp fps of
817+ Nothing -> return False
818+ Just _ -> return True
819+
649820 getModificationTime fp = do
650821 fps <- getsPureState stFiles
651822 case infoFileMTime <$> getFileInfo fp fps of
@@ -667,9 +838,8 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where
667838 openURL = lift . openURL
668839 readFileLazy = lift . readFileLazy
669840 readFileStrict = lift . readFileStrict
670− readDefaultDataFile = lift . readDefaultDataFile
671− readDataFile = lift . readDataFile
672841 glob = lift . glob
842+ fileExists = lift . fileExists
673843 getModificationTime = lift . getModificationTime
674844 getCommonState = lift getCommonState
675845 putCommonState = lift . putCommonState
@@ -696,9 +866,8 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where
696866 openURL = lift . openURL
697867 readFileLazy = lift . readFileLazy
698868 readFileStrict = lift . readFileStrict
699− readDefaultDataFile = lift . readDefaultDataFile
700− readDataFile = lift . readDataFile
701869 glob = lift . glob
870+ fileExists = lift . fileExists
702871 getModificationTime = lift . getModificationTime
703872 getCommonState = lift getCommonState
704873 putCommonState = lift . putCommonState
@@ -713,9 +882,8 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where
713882 openURL = lift . openURL
714883 readFileLazy = lift . readFileLazy
715884 readFileStrict = lift . readFileStrict
716− readDefaultDataFile = lift . readDefaultDataFile
717− readDataFile = lift . readDataFile
718885 glob = lift . glob
886+ fileExists = lift . fileExists
719887 getModificationTime = lift . getModificationTime
720888 getCommonState = lift getCommonState
721889 putCommonState = lift . putCommonState
@@ -730,9 +898,8 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where
730898 openURL = lift . openURL
731899 readFileLazy = lift . readFileLazy
732900 readFileStrict = lift . readFileStrict
733− readDefaultDataFile = lift . readDefaultDataFile
734− readDataFile = lift . readDataFile
735901 glob = lift . glob
902+ fileExists = lift . fileExists
736903 getModificationTime = lift . getModificationTime
737904 getCommonState = lift getCommonState
738905 putCommonState = lift . putCommonState
@@ -747,9 +914,8 @@ instance PandocMonad m => PandocMonad (StateT st m) where
747914 openURL = lift . openURL
748915 readFileLazy = lift . readFileLazy
749916 readFileStrict = lift . readFileStrict
750− readDefaultDataFile = lift . readDefaultDataFile
751− readDataFile = lift . readDataFile
752917 glob = lift . glob
918+ fileExists = lift . fileExists
753919 getModificationTime = lift . getModificationTime
754920 getCommonState = lift getCommonState
755921 putCommonState = lift . putCommonState
src/Text/Pandoc/Logging.hs+14
@@ -95,6 +95,8 @@ data LogMessage =
9595 | CouldNotHighlight String
9696 | MissingCharacter String
9797 | Deprecated String String
98+ | NoTranslation String
99+ | CouldNotLoadTranslations String String
98100 deriving (Show, Eq, Data, Ord, Typeable, Generic)
99101
100102instance ToJSON LogMessage where
@@ -195,6 +197,11 @@ instance ToJSON LogMessage where
195197 Deprecated thing msg ->
196198 ["thing" .= Text.pack thing,
197199 "message" .= Text.pack msg]
200+ NoTranslation term ->
201+ ["term" .= Text.pack term]
202+ CouldNotLoadTranslations lang msg ->
203+ ["lang" .= Text.pack lang,
204+ "message" .= Text.pack msg]
198205
199206
200207showPos :: SourcePos -> String
@@ -282,6 +289,11 @@ showLogMessage msg =
282289 if null m
283290 then ""
284291 else ". " ++ m
292+ NoTranslation t ->
293+ "The term " ++ t ++ " has no translation defined."
294+ CouldNotLoadTranslations lang m ->
295+ "Could not load translations for " ++ lang ++
296+ if null m then "" else ('\n':m)
285297
286298messageVerbosity:: LogMessage -> Verbosity
287299messageVerbosity msg =
@@ -314,3 +326,5 @@ messageVerbosity msg =
314326 CouldNotHighlight{} -> WARNING
315327 MissingCharacter{} -> WARNING
316328 Deprecated{} -> WARNING
329+ NoTranslation{} -> WARNING
330+ CouldNotLoadTranslations{} -> WARNING
src/Text/Pandoc/Lua/PandocModule.hs+3−3
@@ -32,13 +32,12 @@ import Data.ByteString.Char8 (unpack)
3232import Data.Default (Default (..))
3333import Data.Text (pack)
3434import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset)
35−import Text.Pandoc.Class hiding (readDataFile)
35+import Text.Pandoc.Class
3636import Text.Pandoc.Definition (Pandoc)
3737import Text.Pandoc.Options (ReaderOptions(readerExtensions))
3838import Text.Pandoc.Lua.Compat (loadstring)
3939import Text.Pandoc.Lua.StackInstances ()
4040import Text.Pandoc.Readers (Reader (..), getReader)
41−import Text.Pandoc.Shared (readDataFile)
4241
4342-- | Push the "pandoc" on the lua stack.
4443pushPandocModule :: Maybe FilePath -> LuaState -> IO ()
@@ -52,7 +51,8 @@ pushPandocModule datadir lua = do
5251
5352-- | Get the string representation of the pandoc module
5453pandocModuleScript :: Maybe FilePath -> IO String
55−pandocModuleScript datadir = unpack <$> readDataFile datadir "pandoc.lua"
54+pandocModuleScript datadir = unpack <$>
55+ runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua")
5656
5757read_doc :: String -> String -> IO (Either String Pandoc)
5858read_doc formatSpec content = do
src/Text/Pandoc/Readers/LaTeX.hs+133−2
@@ -55,8 +55,11 @@ import Data.Maybe (fromMaybe, maybeToList)
5555import Safe (minimumDef)
5656import System.FilePath (addExtension, replaceExtension, takeExtension)
5757import Text.Pandoc.Builder
58−import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs,
59− report, setResourcePath, getResourcePath)
58+import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv,
59+ readFileFromDirs, report, setResourcePath,
60+ getResourcePath, setTranslations, translateTerm)
61+import qualified Text.Pandoc.Translations as Translations
62+import Text.Pandoc.BCP47 (Lang(..))
6063import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
6164import Text.Pandoc.ImageSize (numUnit, showFl)
6265import Text.Pandoc.Logging
@@ -1247,6 +1250,7 @@ inlineCommands = M.fromList $
12471250 removeDoubleQuotes . untokenize <$> braced
12481251 mkImage options src)
12491252 , ("enquote", enquote)
1253+ , ("figurename", doTerm Translations.Figure)
12501254 , ("cite", citation "cite" NormalCitation False)
12511255 , ("Cite", citation "Cite" NormalCitation False)
12521256 , ("citep", citation "citep" NormalCitation False)
@@ -1326,6 +1330,12 @@ inlineCommands = M.fromList $
13261330 , ("ifstrequal", ifstrequal)
13271331 ]
13281332
1333+doTerm :: PandocMonad m => Translations.Term -> LP m Inlines
1334+doTerm term = do
1335+ s <- (symbol '~' >> return (str "\160")) <|> return space
1336+ t <- translateTerm term
1337+ return (str t <> s)
1338+
13291339ifstrequal :: PandocMonad m => LP m Inlines
13301340ifstrequal = do
13311341 str1 <- tok
@@ -1759,6 +1769,9 @@ blockCommands = M.fromList $
17591769 -- includes
17601770 , ("lstinputlisting", inputListing)
17611771 , ("graphicspath", graphicsPath)
1772+ -- polyglossia
1773+ , ("setdefaultlanguage", setDefaultLanguage)
1774+ , ("setmainlanguage", setDefaultLanguage)
17621775 -- hyperlink
17631776 , ("hypertarget", try $ braced >> grouped block)
17641777 -- LaTeX colors
@@ -2206,3 +2219,121 @@ block = (mempty <$ spaces1)
22062219blocks :: PandocMonad m => LP m Blocks
22072220blocks = mconcat <$> many block
22082221
2222+setDefaultLanguage :: PandocMonad m => LP m Blocks
2223+setDefaultLanguage = do
2224+ o <- option "" $ (T.unpack . T.filter (\c -> c /= '[' && c /= ']'))
2225+ <$> rawopt
2226+ polylang <- toksToString <$> braced
2227+ case polyglossiaLangToBCP47 polylang o of
2228+ Nothing -> return () -- TODO mzero? warning?
2229+ Just l -> setTranslations l
2230+ return mempty
2231+
2232+polyglossiaLangToBCP47 :: String -> String -> Maybe Lang
2233+polyglossiaLangToBCP47 s o =
2234+ case (s, filter (/=' ') o) of
2235+ ("arabic", "locale=algeria") -> Just $ Lang "ar" "" "DZ" []
2236+ ("arabic", "locale=mashriq") -> Just $ Lang "ar" "" "SY" []
2237+ ("arabic", "locale=libya") -> Just $ Lang "ar" "" "LY" []
2238+ ("arabic", "locale=morocco") -> Just $ Lang "ar" "" "MA" []
2239+ ("arabic", "locale=mauritania") -> Just $ Lang "ar" "" "MR" []
2240+ ("arabic", "locale=tunisia") -> Just $ Lang "ar" "" "TN" []
2241+ ("german", "spelling=old") -> Just $ Lang "de" "" "DE" ["1901"]
2242+ ("german", "variant=austrian,spelling=old")
2243+ -> Just $ Lang "de" "" "AT" ["1901"]
2244+ ("german", "variant=austrian") -> Just $ Lang "de" "" "AT" []
2245+ ("german", "variant=swiss,spelling=old")
2246+ -> Just $ Lang "de" "" "CH" ["1901"]
2247+ ("german", "variant=swiss") -> Just $ Lang "de" "" "CH" []
2248+ ("german", _) -> Just $ Lang "de" "" "" []
2249+ ("lsorbian", _) -> Just $ Lang "dsb" "" "" []
2250+ ("greek", "variant=poly") -> Just $ Lang "el" "" "polyton" []
2251+ ("english", "variant=australian") -> Just $ Lang "en" "" "AU" []
2252+ ("english", "variant=canadian") -> Just $ Lang "en" "" "CA" []
2253+ ("english", "variant=british") -> Just $ Lang "en" "" "GB" []
2254+ ("english", "variant=newzealand") -> Just $ Lang "en" "" "NZ" []
2255+ ("english", "variant=american") -> Just $ Lang "en" "" "US" []
2256+ ("greek", "variant=ancient") -> Just $ Lang "grc" "" "" []
2257+ ("usorbian", _) -> Just $ Lang "hsb" "" "" []
2258+ ("latin", "variant=classic") -> Just $ Lang "la" "" "" ["x-classic"]
2259+ ("slovenian", _) -> Just $ Lang "sl" "" "" []
2260+ ("serbianc", _) -> Just $ Lang "sr" "cyrl" "" []
2261+ ("pinyin", _) -> Just $ Lang "zh" "Latn" "" ["pinyin"]
2262+ ("afrikaans", _) -> Just $ Lang "af" "" "" []
2263+ ("amharic", _) -> Just $ Lang "am" "" "" []
2264+ ("arabic", _) -> Just $ Lang "ar" "" "" []
2265+ ("assamese", _) -> Just $ Lang "as" "" "" []
2266+ ("asturian", _) -> Just $ Lang "ast" "" "" []
2267+ ("bulgarian", _) -> Just $ Lang "bg" "" "" []
2268+ ("bengali", _) -> Just $ Lang "bn" "" "" []
2269+ ("tibetan", _) -> Just $ Lang "bo" "" "" []
2270+ ("breton", _) -> Just $ Lang "br" "" "" []
2271+ ("catalan", _) -> Just $ Lang "ca" "" "" []
2272+ ("welsh", _) -> Just $ Lang "cy" "" "" []
2273+ ("czech", _) -> Just $ Lang "cs" "" "" []
2274+ ("coptic", _) -> Just $ Lang "cop" "" "" []
2275+ ("danish", _) -> Just $ Lang "da" "" "" []
2276+ ("divehi", _) -> Just $ Lang "dv" "" "" []
2277+ ("greek", _) -> Just $ Lang "el" "" "" []
2278+ ("english", _) -> Just $ Lang "en" "" "" []
2279+ ("esperanto", _) -> Just $ Lang "eo" "" "" []
2280+ ("spanish", _) -> Just $ Lang "es" "" "" []
2281+ ("estonian", _) -> Just $ Lang "et" "" "" []
2282+ ("basque", _) -> Just $ Lang "eu" "" "" []
2283+ ("farsi", _) -> Just $ Lang "fa" "" "" []
2284+ ("finnish", _) -> Just $ Lang "fi" "" "" []
2285+ ("french", _) -> Just $ Lang "fr" "" "" []
2286+ ("friulan", _) -> Just $ Lang "fur" "" "" []
2287+ ("irish", _) -> Just $ Lang "ga" "" "" []
2288+ ("scottish", _) -> Just $ Lang "gd" "" "" []
2289+ ("ethiopic", _) -> Just $ Lang "gez" "" "" []
2290+ ("galician", _) -> Just $ Lang "gl" "" "" []
2291+ ("hebrew", _) -> Just $ Lang "he" "" "" []
2292+ ("hindi", _) -> Just $ Lang "hi" "" "" []
2293+ ("croatian", _) -> Just $ Lang "hr" "" "" []
2294+ ("magyar", _) -> Just $ Lang "hu" "" "" []
2295+ ("armenian", _) -> Just $ Lang "hy" "" "" []
2296+ ("interlingua", _) -> Just $ Lang "ia" "" "" []
2297+ ("indonesian", _) -> Just $ Lang "id" "" "" []
2298+ ("icelandic", _) -> Just $ Lang "is" "" "" []
2299+ ("italian", _) -> Just $ Lang "it" "" "" []
2300+ ("japanese", _) -> Just $ Lang "jp" "" "" []
2301+ ("khmer", _) -> Just $ Lang "km" "" "" []
2302+ ("kurmanji", _) -> Just $ Lang "kmr" "" "" []
2303+ ("kannada", _) -> Just $ Lang "kn" "" "" []
2304+ ("korean", _) -> Just $ Lang "ko" "" "" []
2305+ ("latin", _) -> Just $ Lang "la" "" "" []
2306+ ("lao", _) -> Just $ Lang "lo" "" "" []
2307+ ("lithuanian", _) -> Just $ Lang "lt" "" "" []
2308+ ("latvian", _) -> Just $ Lang "lv" "" "" []
2309+ ("malayalam", _) -> Just $ Lang "ml" "" "" []
2310+ ("mongolian", _) -> Just $ Lang "mn" "" "" []
2311+ ("marathi", _) -> Just $ Lang "mr" "" "" []
2312+ ("dutch", _) -> Just $ Lang "nl" "" "" []
2313+ ("nynorsk", _) -> Just $ Lang "nn" "" "" []
2314+ ("norsk", _) -> Just $ Lang "no" "" "" []
2315+ ("nko", _) -> Just $ Lang "nqo" "" "" []
2316+ ("occitan", _) -> Just $ Lang "oc" "" "" []
2317+ ("panjabi", _) -> Just $ Lang "pa" "" "" []
2318+ ("polish", _) -> Just $ Lang "pl" "" "" []
2319+ ("piedmontese", _) -> Just $ Lang "pms" "" "" []
2320+ ("portuguese", _) -> Just $ Lang "pt" "" "" []
2321+ ("romansh", _) -> Just $ Lang "rm" "" "" []
2322+ ("romanian", _) -> Just $ Lang "ro" "" "" []
2323+ ("russian", _) -> Just $ Lang "ru" "" "" []
2324+ ("sanskrit", _) -> Just $ Lang "sa" "" "" []
2325+ ("samin", _) -> Just $ Lang "se" "" "" []
2326+ ("slovak", _) -> Just $ Lang "sk" "" "" []
2327+ ("albanian", _) -> Just $ Lang "sq" "" "" []
2328+ ("serbian", _) -> Just $ Lang "sr" "" "" []
2329+ ("swedish", _) -> Just $ Lang "sv" "" "" []
2330+ ("syriac", _) -> Just $ Lang "syr" "" "" []
2331+ ("tamil", _) -> Just $ Lang "ta" "" "" []
2332+ ("telugu", _) -> Just $ Lang "te" "" "" []
2333+ ("thai", _) -> Just $ Lang "th" "" "" []
2334+ ("turkmen", _) -> Just $ Lang "tk" "" "" []
2335+ ("turkish", _) -> Just $ Lang "tr" "" "" []
2336+ ("ukrainian", _) -> Just $ Lang "uk" "" "" []
2337+ ("urdu", _) -> Just $ Lang "ur" "" "" []
2338+ ("vietnamese", _) -> Just $ Lang "vi" "" "" []
2339+ _ -> Nothing
src/Text/Pandoc/Shared.hs+1−118
@@ -76,11 +76,6 @@ module Text.Pandoc.Shared (
7676 renderTags',
7777 -- * File handling
7878 inDirectory,
79− getDefaultReferenceDocx,
80− getDefaultReferenceODT,
81− readDefaultDataFile,
82− readDataFile,
83− readDataFileUTF8,
8479 openURL,
8580 collapseFilePath,
8681 filteredFilesFromArchive,
@@ -116,8 +111,6 @@ import System.Directory
116111import System.FilePath (splitDirectories, isPathSeparator)
117112import qualified System.FilePath.Posix as Posix
118113import Text.Pandoc.MIME (MimeType)
119−import Text.Pandoc.Error (PandocError(..))
120−import System.FilePath ( (</>) )
121114import Data.Generics (Typeable, Data)
122115import qualified Control.Monad.State.Strict as S
123116import qualified Control.Exception as E
@@ -125,7 +118,6 @@ import Control.Monad (msum, unless, MonadPlus(..))
125118import Text.Pandoc.Pretty (charWidth)
126119import Text.Pandoc.Generic (bottomUp)
127120import Text.Pandoc.Compat.Time
128−import Data.Time.Clock.POSIX
129121import System.IO.Error
130122import System.IO.Temp
131123import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..),
@@ -136,17 +128,12 @@ import qualified Data.ByteString.Char8 as B8
136128import Data.ByteString.Base64 (decodeLenient)
137129import Data.Sequence (ViewR(..), ViewL(..), viewl, viewr)
138130import qualified Data.Text as T
139−import Data.ByteString.Lazy (toChunks, fromChunks)
131+import Data.ByteString.Lazy (toChunks)
140132import qualified Data.ByteString.Lazy as BL
141133import Paths_pandoc (version)
142134
143135import Codec.Archive.Zip
144136
145−#ifdef EMBED_DATA_FILES
146−import Text.Pandoc.Data (dataFiles)
147−#else
148−import Paths_pandoc (getDataFileName)
149−#endif
150137import Network.HTTP.Client (httpLbs, responseBody, responseHeaders,
151138 Request(port,host,requestHeaders),
152139 HttpException)
@@ -612,110 +599,6 @@ inDirectory path action = E.bracket
612599 setCurrentDirectory
613600 (const $ setCurrentDirectory path >> action)
614601
615−getDefaultReferenceDocx :: Maybe FilePath -> IO Archive
616−getDefaultReferenceDocx datadir = do
617− let paths = ["[Content_Types].xml",
618− "_rels/.rels",
619− "docProps/app.xml",
620− "docProps/core.xml",
621− "word/document.xml",
622− "word/fontTable.xml",
623− "word/footnotes.xml",
624− "word/numbering.xml",
625− "word/settings.xml",
626− "word/webSettings.xml",
627− "word/styles.xml",
628− "word/_rels/document.xml.rels",
629− "word/_rels/footnotes.xml.rels",
630− "word/theme/theme1.xml"]
631− let toLazy = fromChunks . (:[])
632− let pathToEntry path = do epochtime <- (floor . utcTimeToPOSIXSeconds) <$>
633− getCurrentTime
634− contents <- toLazy <$> readDataFile datadir
635− ("docx/" ++ path)
636− return $ toEntry path epochtime contents
637− mbArchive <- case datadir of
638− Nothing -> return Nothing
639− Just d -> do
640− exists <- doesFileExist (d </> "reference.docx")
641− if exists
642− then return (Just (d </> "reference.docx"))
643− else return Nothing
644− case mbArchive of
645− Just arch -> toArchive <$> BL.readFile arch
646− Nothing -> foldr addEntryToArchive emptyArchive <$>
647− mapM pathToEntry paths
648−
649−getDefaultReferenceODT :: Maybe FilePath -> IO Archive
650−getDefaultReferenceODT datadir = do
651− let paths = ["mimetype",
652− "manifest.rdf",
653− "styles.xml",
654− "content.xml",
655− "meta.xml",
656− "settings.xml",
657− "Configurations2/accelerator/current.xml",
658− "Thumbnails/thumbnail.png",
659− "META-INF/manifest.xml"]
660− let pathToEntry path = do epochtime <- floor `fmap` getPOSIXTime
661− contents <- (fromChunks . (:[])) `fmap`
662− readDataFile datadir ("odt/" ++ path)
663− return $ toEntry path epochtime contents
664− mbArchive <- case datadir of
665− Nothing -> return Nothing
666− Just d -> do
667− exists <- doesFileExist (d </> "reference.odt")
668− if exists
669− then return (Just (d </> "reference.odt"))
670− else return Nothing
671− case mbArchive of
672− Just arch -> toArchive <$> BL.readFile arch
673− Nothing -> foldr addEntryToArchive emptyArchive <$>
674− mapM pathToEntry paths
675−
676−
677−readDefaultDataFile :: FilePath -> IO BS.ByteString
678−readDefaultDataFile "reference.docx" =
679− (BS.concat . toChunks . fromArchive) <$> getDefaultReferenceDocx Nothing
680−readDefaultDataFile "reference.odt" =
681− (BS.concat . toChunks . fromArchive) <$> getDefaultReferenceODT Nothing
682−readDefaultDataFile fname =
683−#ifdef EMBED_DATA_FILES
684− case lookup (makeCanonical fname) dataFiles of
685− Nothing -> E.throwIO $ PandocCouldNotFindDataFileError fname
686− Just contents -> return contents
687− where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
688− transformPathParts = reverse . foldl go []
689− go as "." = as
690− go (_:as) ".." = as
691− go as x = x : as
692−#else
693− getDataFileName fname' >>= checkExistence >>= BS.readFile
694− where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname
695−
696−checkExistence :: FilePath -> IO FilePath
697−checkExistence fn = do
698− exists <- doesFileExist fn
699− if exists
700− then return fn
701− else E.throwIO $ PandocCouldNotFindDataFileError fn
702−#endif
703−
704−-- | Read file from specified user data directory or, if not found there, from
705−-- Cabal data directory.
706−readDataFile :: Maybe FilePath -> FilePath -> IO BS.ByteString
707−readDataFile Nothing fname = readDefaultDataFile fname
708−readDataFile (Just userDir) fname = do
709− exists <- doesFileExist (userDir </> fname)
710− if exists
711− then BS.readFile (userDir </> fname)
712− else readDefaultDataFile fname
713−
714−-- | Same as 'readDataFile' but returns a String instead of a ByteString.
715−readDataFileUTF8 :: Maybe FilePath -> FilePath -> IO String
716−readDataFileUTF8 userDir fname =
717− UTF8.toString `fmap` readDataFile userDir fname
718−
719602-- | Read from a URL and return raw data and maybe mime type.
720603openURL :: String -> IO (Either HttpException (BS.ByteString, Maybe MimeType))
721604openURL u
src/Text/Pandoc/Templates.hs+1−1
@@ -44,7 +44,7 @@ import qualified Data.Text as T
4444import System.FilePath ((<.>), (</>))
4545import Text.DocTemplates (Template, TemplateTarget, applyTemplate,
4646 compileTemplate, renderTemplate, varListToJSON)
47−import Text.Pandoc.Class (PandocMonad(readDataFile))
47+import Text.Pandoc.Class (readDataFile, PandocMonad)
4848import Text.Pandoc.Error
4949import qualified Text.Pandoc.UTF8 as UTF8
5050
src/Text/Pandoc/Translations.hsnew file+94
@@ -0,0 +1,94 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-
4+Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
5+
6+This program is free software; you can redistribute it and/or modify
7+it under the terms of the GNU General Public License as published by
8+the Free Software Foundation; either version 2 of the License, or
9+(at your option) any later version.
10+
11+This program is distributed in the hope that it will be useful,
12+but WITHOUT ANY WARRANTY; without even the implied warranty of
13+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+GNU General Public License for more details.
15+
16+You should have received a copy of the GNU General Public License
17+along with this program; if not, write to the Free Software
18+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+-}
20+
21+{- |
22+ Module : Text.Pandoc.Translations
23+ Copyright : Copyright (C) 2017 John MacFarlane
24+ License : GNU GPL, version 2 or above
25+
26+ Maintainer : John MacFarlane <jgm@berkeley.edu>
27+ Stability : alpha
28+ Portability : portable
29+
30+Data types for localization.
31+
32+Translations are stored in @data/translations/langname.trans@,
33+where langname can be the full BCP47 language specifier, or
34+just the language part. File format is:
35+
36+> # A comment, ignored
37+> Figure: Figura
38+> Index: Indeksi
39+
40+-}
41+module Text.Pandoc.Translations (
42+ Term(..)
43+ , Translations(..)
44+ , readTranslations
45+ )
46+where
47+import qualified Data.Map as M
48+import GHC.Generics (Generic)
49+import Text.Pandoc.Shared (trim, safeRead)
50+
51+data Term =
52+ Preface
53+ | References
54+ | Abstract
55+ | Bibliography
56+ | Chapter
57+ | Appendix
58+ | Contents
59+ | ListOfFigures
60+ | ListOfTables
61+ | Index
62+ | Figure
63+ | Table
64+ | Part
65+ | Page
66+ | Proof
67+ | See
68+ | SeeAlso
69+ | Cc
70+ | To
71+ deriving (Show, Eq, Ord, Generic, Read)
72+
73+newtype Translations = Translations (M.Map Term String)
74+ deriving (Show, Eq, Ord, Generic, Monoid)
75+
76+readTranslations :: String -> Either String Translations
77+readTranslations = foldr parseLine (Right mempty) . lines
78+
79+parseLine :: String
80+ -> Either String Translations
81+ -> Either String Translations
82+parseLine _ (Left s) = Left s
83+parseLine ('#':_) x = x
84+parseLine [] x = x
85+parseLine t (Right (Translations tm)) =
86+ if null rest
87+ then Left $ "no colon in " ++ term
88+ else
89+ case safeRead term of
90+ Nothing -> Left $ term ++ " is not a recognized term name"
91+ Just term' -> Right (Translations $ (M.insert term' defn) tm)
92+ where (trm, rest) = break (\c -> c == ':') t
93+ defn = trim $ drop 1 rest
94+ term = trim trm
src/Text/Pandoc/Writers/ConTeXt.hs+1−1
@@ -37,7 +37,7 @@ import Data.Maybe (catMaybes)
3737import Data.Text (Text)
3838import Network.URI (unEscapeString)
3939import Text.Pandoc.BCP47
40−import Text.Pandoc.Class (PandocMonad, report)
40+import Text.Pandoc.Class (PandocMonad, report, toLang)
4141import Text.Pandoc.Logging
4242import Text.Pandoc.Definition
4343import Text.Pandoc.ImageSize
src/Text/Pandoc/Writers/Docx.hs+2−2
@@ -51,7 +51,7 @@ import qualified Data.Text as T
5151import Data.Time.Clock.POSIX
5252import Skylighting
5353import System.Random (randomR)
54−import Text.Pandoc.Class (PandocMonad, report)
54+import Text.Pandoc.Class (PandocMonad, report, toLang)
5555import qualified Text.Pandoc.Class as P
5656import Text.Pandoc.Compat.Time
5757import Text.Pandoc.Definition
@@ -68,7 +68,7 @@ import qualified Text.Pandoc.UTF8 as UTF8
6868import Text.Pandoc.Walk
6969import Text.Pandoc.Writers.Math
7070import Text.Pandoc.Writers.Shared (fixDisplayMath)
71−import Text.Pandoc.BCP47 (getLang, renderLang, toLang)
71+import Text.Pandoc.BCP47 (getLang, renderLang)
7272import Text.Printf (printf)
7373import Text.TeXMath
7474import Text.XML.Light as XML
src/Text/Pandoc/Writers/LaTeX.hs+2−2
@@ -45,8 +45,8 @@ import Data.Maybe (catMaybes, fromMaybe, isJust)
4545import Data.Text (Text)
4646import qualified Data.Text as T
4747import Network.URI (unEscapeString)
48−import Text.Pandoc.BCP47 (Lang(..), toLang, getLang, renderLang)
49−import Text.Pandoc.Class (PandocMonad, report)
48+import Text.Pandoc.BCP47 (Lang(..), getLang, renderLang)
49+import Text.Pandoc.Class (PandocMonad, report, toLang)
5050import Text.Pandoc.Definition
5151import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
5252 styleToLaTeX, toListingsLanguage)
src/Text/Pandoc/Writers/ODT.hs+2−2
@@ -38,7 +38,7 @@ import Data.List (isPrefixOf)
3838import Data.Maybe (fromMaybe)
3939import qualified Data.Text.Lazy as TL
4040import System.FilePath (takeDirectory, takeExtension, (<.>))
41−import Text.Pandoc.Class (PandocMonad, report)
41+import Text.Pandoc.Class (PandocMonad, report, toLang)
4242import qualified Text.Pandoc.Class as P
4343import Text.Pandoc.Definition
4444import Text.Pandoc.ImageSize
@@ -51,7 +51,7 @@ import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy)
5151import Text.Pandoc.Walk
5252import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
5353import Text.Pandoc.Writers.Shared (fixDisplayMath)
54−import Text.Pandoc.BCP47 (getLang, toLang, Lang(..), renderLang)
54+import Text.Pandoc.BCP47 (getLang, Lang(..), renderLang)
5555import Text.Pandoc.XML
5656import Text.TeXMath
5757import Text.XML.Light