尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Pandoc 的 LaTeX 语言标记解析实战:从 `\foreignlanguage` 到 BCP 47 语言属性

Pandoc 的 LaTeX 语言标记解析实战:从 `\foreignlanguage` 到 BCP 47 语言属性 Pandoc 的 LaTeX 语言标记解析实战从\foreignlanguage到 BCP 47 语言属性【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc导读本文以 pandoc 官方命令测试用例 test/command/4199.md 为切入点深入讲解 pandoc 如何把 LaTeX 文档中的\foreignlanguage{ngerman}{...}等 babel/polyglossia 语言命令解析为统一的 BCP 47 语言属性并输出为带lang键值对的 Span 元素。读完本文你将掌握 pandoc LaTeX 读取器中语言命令的解析原理、babel 方言名到 BCP 47 的映射规则、相关命令与环境的完整清单以及与之对应的 LaTeX 写出行为可直接用于多语言文档的转换实践。一、测试用例 4199一行命令读懂解析目标test/command/4199.md 是 pandoc 的 golden 命令测试文件全文如下% pandoc -f latex -t native \foreignlanguage{ngerman}{foo} ^D [ Para [ Span ( , [] , [ ( lang , de-DE ) ] ) [ Str foo ] ] ]这个测试描述了一次完整的转换过程输入以% pandoc -f latex -t native开头的命令行随后是被转换的 LaTeX 片段\foreignlanguage{ngerman}{foo}^D表示输入流结束输出pandoc 的原生 ASTnative 格式显示\foreignlanguage{ngerman}{foo}被解析为一个Para其中包含一个带属性(lang, de-DE)的Span包裹着字符串foo。也就是说pandoc 的 LaTeX 读取器完成了两件关键工作识别\foreignlanguage命令及其第一个花括号参数babel 方言名ngerman将ngerman规范化为 BCP 47 语言标签de-DE并把后续内容包进一个Span (, [], [(lang, de-DE)])中。de-DE中的de是德语的语言代码DE是德国地区代码。ngermannew german是 babel 的现代德语拼写方言因此它没有带上1901这样的历史拼写变体标记。二、源码定位语言解析模块Lang.hs支撑该测试用例的核心实现位于 src/Text/Pandoc/Readers/LaTeX/Lang.hs。该模块的文档注释明确指出其职责是Functions for parsing polyglossia and babel language specifiers to BCP47 Lang.即把 polyglossia 与 babel 的语言说明符转换为 BCP 47Lang结构。模块导出了以下核心函数setDefaultLanguage处理\setdefaultlanguage等命令设置文档默认语言元数据polyglossiaLangToBCP47polyglossia 语言名到 BCP 47 的映射表babelLangToBCP47babel 方言名到 BCP 47 的映射函数enquoteCommands\enquote、\foreignquote、\hyphenquote等引号命令inlineLanguageCommands\foreignlanguage以及\textfrench、\textgerman这类内联语言命令。2.1foreignlanguage的解析实现foreignlanguage函数的实现非常简洁Lang.hsforeignlanguage :: PandocMonad m LP m Inlines - LP m Inlines foreignlanguage tok do babelLang - untokenize $ braced case babelLangToBCP47 babelLang of Just lang - spanWith (, [], [(lang, renderLang lang)]) $ tok _ - tok解析流程分三步读取\foreignlanguage{...}第一个花括号中的内容作为 babel 语言名braced调用babelLangToBCP47尝试把该名字映射为 BCP 47 语言映射成功则用spanWith (, [], [(lang, renderLang lang)])把后续内联内容包成带lang属性的 Span映射失败则原样透传内容不产生 Span。注意第三个分支对于未知的、无法识别的语言名pandoc 不会报错而是直接返回原内容。这一点对实际转换很友好——不会因为某个冷门方言导致整个文档转换失败。2.2 命令注册表在 src/Text/Pandoc/Readers/LaTeX.hs 中这些语言命令被注册进内联命令表, enquoteCommands tok , inlineLanguageCommands tok其中inlineLanguageCommandsLang.hs由两部分组成inlineLanguageCommands tok M.fromList $ (foreignlanguage, foreignlanguage tok) : (mk $ M.toList polyglossiaLangToBCP47)\foreignlanguage命令本身对polyglossiaLangToBCP47映射表中的每一个 polyglossia 语言名X自动生成\textX形式的内联命令如\textfrench、\textgerman、\textlatin等。因此pandoc 原生支持的全部 polyglossia 语言名Lang.hs 中的映射表涵盖 afrikaans、french、german、greek、hebrew、russian、spanish 等数十种都会自动获得对应的\text语言命令支持。三、babel 方言名 → BCP 47 的映射规则babelLangToBCP47Lang.hs是整个语言属性体系的关键函数。它处理 babel 的方言名其中德语相关的映射最具代表性babel 方言名输出 BCP 47说明germande-DE-1901传统德语拼写1901 拼写规则ngermande-DE现代德语拼写austriande-AT-1901奥地利 传统拼写naustriande-AT奥地利 现代拼写swissgermande-CH-1901瑞士 传统拼写nswissgermande-CH瑞士 现代拼写lowersorbiandsb下索布语uppersorbianhsb上索布语polytonicgreek/polutonikogreekel-polyton古希腊语多重音slovenesl斯洛文尼亚语australianen-AU澳大利亚英语canadianen-CA加拿大英语britishen-GB英式英语newzealanden-NZ新西兰英语americanen-US美式英语classiclatinla-x-classic古典拉丁语德语系列采用「地区代码 可选拼写变体」的三段式表达naustrian中的n前缀表示 new即现代拼写不带n的旧形式则附加1901变体标签这与 babel 文档中的拼写差异约定一一对应。当babelLangToBCP47在显式方言表中找不到时会回退到polyglossiaLangToBCP47查找Lang.hs最终仍找不到则返回Nothing。3.1 polyglossia 选项参数的处理polyglossiaLangToBCP47的值类型是Text - Lang即每个语言名对应一个函数函数接收方括号选项字符串如[variantamerican]。例如 Lang.hs 中的english(english, \o - case T.filter (/ ) o of variantaustralian - Lang en Nothing (Just AU) [] [] [] variantcanadian - Lang en Nothing (Just CA) [] [] [] variantbritish - Lang en Nothing (Just GB) [] [] [] variantnewzealand - Lang en Nothing (Just NZ) [] [] [] variantamerican - Lang en Nothing (Just US) [] [] [] _ - Lang en Nothing Nothing [] [] [])类似地arabic支持localealgeria、localemashriq等地区选项german支持spellingold、variantaustrian、variantswiss选项greek支持variantpoly、variantancient。例如\textgerman[variantswiss]{...}会被解析为de-CH。这些函数共同指向Text.Collate.Lang的Lang结构六个字段语言、文字、地区、变体等renderLang负责把它渲染成规范的 BCP 47 字符串。四、命令与环境的完整支持面除\foreignlanguage外pandoc 的 LaTeX 读取器还支持一整套语言相关结构4.1 内联语言命令\textlang命令测试 test/command/9202.md 展示了多种语言结构的解析例如% pandoc -f latex -t native \textfrench{Bonjour} ^D [ Para [ Span ( , [] , [ ( lang , fr ) ] ) [ Str Bonjour ] ] ]inlineLanguageLang.hs还会解析可选的方括号选项并使用extractSpaces把 Span 外层多余的空格剥离出去inlineLanguage tok bcp47Func do o - option $ T.filter (\c - c / [ c / ]) $ rawopt let lang renderLang $ bcp47Func o extractSpaces (spanWith (, [], [(lang, lang)])) $ tok4.2 块级语言环境otherlanguage与\begin{lang}块级语言环境由 LaTeX.hs 处理\begin{otherlanguage}{french}...\end{otherlanguage}解析为带langfr属性的Div见 test/command/9202.md 的第一个用例\begin{french}...\end{french}这类直接用 babel 语言名作环境名的写法由langEnvironment支持同样产出Div (, [], [(lang,fr)])otherlanguageEnv与foreignlanguage一样对无法识别的语言名采取「原样透传、不包 Div」的宽容策略带星号的otherlanguage*不做语言标记只保留otherlanguage*类名见 9202 用例 2。4.3 引号命令族enquoteCommandsLang.hs支持 babel 的本地化引号命令\enquote/\enquote*按当前语言上下文选择引号\foreignquote{lang}/\foreignquote*使用指定语言的原生引号同样会附加lang属性的 Span\hyphenquote{lang}/\hyphenquote*使用普通引号。4.4 默认语言设置setDefaultLanguageLang.hs处理\setdefaultlanguage/\setmainlanguage等 polyglossia 命令解析语言名与选项后不仅会setTranslations更新本地化文案还会通过setMeta lang把语言写入文档元数据——这正是多语言文档元数据lang字段的重要来源之一。4.5 单元测试印证test/Tests/Readers/LaTeX.hs 中的 polyglossia language spans 测试组直接验证了上述行为hello \textfrench{bonjour}→ 产出带langfr的 Span\textfrench{quelle cest \textlatin{primus}?}→ 嵌套 Spanfr 内嵌 la证明语言 Span 可以无限嵌套\textgerman[variantswiss]{hoechdeutsche}→langde-CH验证了选项参数解析无选项的\textgerman{...}→langde。五、写出方向LaTeX 写出器如何还原语言标记语言属性的处理是双向的不仅 LaTeX 读取器把\foreignlanguage读进来LaTeX 写出器也会把带lang属性的 Span 写回\foreignlanguage。在 src/Text/Pandoc/Writers/LaTeX.hs 的inlineToLaTeX中Span 处理逻辑会先通过toLang提取lang键值对再将其转换为 babel 方言名langCmds case lang toBabel of Just l - [foreignlanguage{ l }] Nothing - []如果 Span 的lang属性可以被toBabel转换写出器就会生成\foreignlanguage{方言}{...}否则不生成语言命令。命令测试 test/command/4102.md 展示了这一往返行为% pandoc -t latex -f markdown [Populus]{.smallcaps langla} [Romanus]{.smallcaps} ^D \foreignlanguage{latin}{\textsc{Populus}} \textsc{Romanus}可以看到带langla的 Span 被写出为\foreignlanguage{latin}{...}并用\textsc还原 smallcaps 类而不带lang属性的 Span 则直接输出\textsc{Romanus}不会生成\foreignlanguage。5.1 完整文档示例命令测试 test/command/9472.md 展示了一个更完整的往返场景——从 Markdown 写出独立-sLaTeX 文档% pandoc -t latex -s --- lang: de-DE --- More text in English. [Zitat auf Deutsch.]{langde} [Bonjour]{langfr} [café]{langfr-FR}生成结果的关键片段\documentclass[ french, ngerman, ]{article} ... \hypersetup{ pdflang{de-DE}, ... } ... More text in English. \foreignlanguage{ngerman}{Zitat auf Deutsch.} \foreignlanguage{french}{Bonjour} \foreignlanguage{french}{café}值得注意的细节文档级lang: de-DE会映射为\documentclass[french, ngerman]{article}的 babel 选项列表并写入\hypersetup的pdflang内联的langde被写回为\foreignlanguage{ngerman}{...}langfr和langfr-FR在写出时都归一化为\foreignlanguage{french}{...}——因为toBabel只能表达 babel 方言级的信息地区细分在写出方向会被合并。六、为什么输出de-DE而不是de映射表的作用回到 4199 用例的核心疑问为什么\foreignlanguage{ngerman}{foo}输出de-DE而不是de答案在babelLangToBCP47的映射表Lang.hsgerman - Just $ Lang de Nothing (Just DE) [1901] [] [] ngerman - Just $ Lang de Nothing (Just DE) [] [] []Lang的第三字段地区被显式指定为Just DE因此renderLang会渲染出de-DE。这不是 pandoc 的随意选择而是对 babel 语义的忠实映射babel 的german/ngerman方言隐含了德国地区这一语境。同理austrian/naustrian映射为de-ATswissgerman/nswissgerman映射为de-CH。这一规范化对下游转换意义重大统一为 BCP 47 后无论是写出 HTMLlangde-DE属性、EPUB语言元数据还是其他支持 BCP 47 的格式都能获得标准一致的语言标记而无需关心上游 LaTeX 用的是 babel 还是 polyglossia、用哪种方言名。七、实战建议与验证方法7.1 如何在本地验证如果你有可用的 pandoc 构建可以直接复现测试# 复现 4199 用例 echo \foreignlanguage{ngerman}{foo} | pandoc -f latex -t native # 验证方言变体 echo \foreignlanguage{swissgerman}{Guten tag} | pandoc -f latex -t native # → Span ( , [] , [ ( lang , de-CH-1901 ) ] ) [ Str Guten , Space , Str tag ] # 验证选项参数 echo \textgerman[variantswiss]{hoechdeutsche} | pandoc -f latex -t native # → lang de-CH # 块级语言环境 printf \\begin{otherlanguage}{french}\nBonjour.\n\\end{otherlanguage}\n | pandoc -f latex -t native # → Div ( , [] , [ ( lang , fr ) ] ) [ Para [ Str Bonjour. ] ]其中swissgerman用例在 test/command/9202.md 中有现成断言输出为de-CH-1901。7.2 多语言文档的推荐写法综合读取与写出两侧的行为推荐在多语言文档中按如下方式组织文档默认语言在 Markdown 元数据中设置lang:如lang: de-DE写出 LaTeX 时会映射为\documentclass的 babel 选项局部语言切换使用带lang属性的 Span/Div——Markdown 中可写为[Zitat]{langde}LaTeX 中对应\foreignlanguage{ngerman}{...}/\begin{otherlanguage}{...}引号本地化LaTeX 输入中的\foreignquote{lang}{...}会被解析为带语言属性的引号结构适合按语言切换引号样式的场景。7.3 已知边界未注册的 babel 方言名会被babelLangToBCP47判为Nothing此时\foreignlanguage的内容原样输出、不产生语言 Spanotherlanguage*带星号不产生lang属性只保留otherlanguage*类名LaTeX 写出时toBabel无法表达的地区细分如fr-FR与fr的差异会归一化为同一方言名french。八、小结test/command/4199.md 虽短却是理解 pandoc 多语言支持机制的一把钥匙。它串联起了三条主线读取方向Lang.hs 通过babelLangToBCP47与polyglossiaLangToBCP47两张映射表把 babel/polyglossia 语言名规范化为 BCP 47\foreignlanguage、\textlang、otherlanguage环境、引号命令族等结构统一产出带lang属性的 Span/DivAST 层面所有语言信息都以标准键值对(lang, BCP47)形式附着于 Span/Div与格式无关、可无限嵌套写出方向Writers/LaTeX.hs 再把lang属性映射回\foreignlanguage{...}配合文档级lang元数据生成 babel 选项构成完整的往返闭环。理解这一机制后无论是排查多语言文档转换中的语言标记问题还是设计自定义 writer 来处理语言语义你都能快速定位到 pandoc 中对应的解析与写出代码路径。【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表