Answers inline
On 19/09/2026 12:47, Ihor Radchenko wrote:
"Pedro A. Aranda" <[email protected]> writes:
It's a base for discussion (hence the 'Towards' in the Subject).
I have initially in ox-latex, because it's where I need it and have it
under control. We can make it available to all backends, just by moving
it to `org-export-options-alist' in ox.el.
I am not sure if we can simply move it.
Neither was I and the stress was on TOWARDS... And the Rationale in the
commit message, just to show my thoughts.
At least, not in the form presented in the patch.
+ (:other-languages "OTHER_LANGUAGES" nil
org-latex-doc-metadata-other-languages t)
mixes generic keyword OTHER_LANGUAGES and latex-specific -
org-latex-doc-metadata-other-languages.
So, /something/ will need to be changed if we decide to move the export
option. We cannot keep org-latex-doc-metadata-other-languages in ox.el.
I personally do not feel comfortable keeping generic keywords in
ox-latex. I would like it better if we either make it fully
latex-specific and keep in ox-latex, or fully generic, and move to ox.el.
What would be your preference?
My preference is generic too. My only concern was that currently the
only consumer is ox-latex.
But as said. Look at the attached version of the patch.
--
Pedro A. Aranda
ox-latex & ox-beamer maintainer
Sagen's Paradeiser! (ORF: Als Radiohören gefährlich war) => write BE!
IA: onomatopeya del rebuzno
2nd year of the New Koprocracy
From 082519966760e747886e98b55a7281cc7bfb9b1f Mon Sep 17 00:00:00 2001
From: "Pedro A. Aranda" <[email protected]>
Date: Sat, 12 Sep 2026 18:03:43 +0200
Subject: [PATCH] Add OTHER_LANGUAGES to the metadata
* doc/org-manual.org : Document the OTHER_LANGUAGES keyword.
* lisp/ox.el (backend): New keyword OTHER_LANGUAGES.
(org-export-other-languages): New variable for the secondary
languages list.
* lisp/ox-latex.el (org-latex--build-metadata-string): New function
to build the metadata from the INFO channel. Treat shorthand DOC_LANGS
to ease defining multiple languages for a document.
(org-latex-make-preamble): Use new function instead of extracting from
the INFO channel directly.
* testing/lisp/test-ox-latex.el (org-latex-make-preamble): Silence first
Metadata test.
(test-ox-latex/metadata-langs): New test, checking the OTHER_LANGUAGES
keyword and the DOC_LANGS shorthand.
(test-ox-latex/example-env-options),
(test-ox-latex/lualatex-fontspec-fallback),
(test-ox-latex/lualatex-fontspec-latex-header-not-lost): Fix #+LANGUAGE
keyword; remove second language there.
---
doc/org-manual.org | 36 +++++++++++++++++++++++++++++++++++
etc/ORG-NEWS | 21 +++++++++++++++++++-
lisp/ox-latex.el | 25 +++++++++++++++++++++++-
lisp/ox.el | 15 +++++++++++++++
testing/lisp/test-ox-latex.el | 33 +++++++++++++++++++++++++++-----
5 files changed, 123 insertions(+), 7 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index f68de4690..91cc5143e 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -12399,6 +12399,12 @@ global variables, include:
example, Org translates =Table of contents= to the French =Table des
matières=[fn:42].
+- =OTHER_LANGUAGES= ::
+
+ #+cindex: @samp{OTHER_LANGUAGES}, keyword
+ #+vindex: org-export-other-languages
+ List of secondary languages present in the document.
+
- =SELECT_TAGS= ::
#+cindex: @samp{SELECT_TAGS}, keyword
@@ -14421,6 +14427,36 @@ general options (see [[*Export Settings]]).
'("AUTO" "polyglossia" t ("xelatex" "lualatex")))
#+end_src
+- =OTHER_LANGUAGES= ::
+
+ #+cindex: @samp{OTHER_LANGUAGES}, keyword
+ #+vindex: org-export-other-languages
+ #+vindex: org-latex-doc-metadata
+
+ This is the list of secondary languages you want to declare in the
+ document metadata. When the document metadata include the shorthand
+ ~DOC_LANGS~, it will be replaced by the main language declared with
+ the ~LANGUAGE~ keyword and this list.
+
+ For example:
+ #+begin_src org
+,#+LANGUAGE: en
+,#+OTHER_LANGUAGES: es, fr
+,#+LATEX_DOC_METADATA: tagging = on, DOC_LANGS
+ #+end_src
+
+ will result in
+ #+BEGIN_SRC latex
+\DocumentMetadata{tagging=on, language=en, other-languages={es, fr}}
+ #+END_SRC
+ as the first line of the LaTeX preamble *before* the document class
+ declaration.
+
+ *NOTE:* In the absence of a declaration for ~OTHER_LANGUAGES~, the
+ language declared with the ~LANGUAGE~ keyword will be the only one
+ added to the metadata, when ~DOC_LANGS~ is found in
+ ~LATEX_DOC_METADATA~.
+
- =LATEX_CLASS= ::
#+cindex: @samp{LATEX_CLASS}, keyword
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 80cd17ba6..4b1dff252 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -252,7 +252,7 @@ keywords or with =#+OPTIONS:=.
Option keys can be used like this:
: #+OPTIONS: html-self-link-headlines:t
: #+OPTIONS: html-tag-class-prefix:"myorg-"
-
+
To know which value you can use, find the allowed option values in the
manual or in the default variable documentation.
@@ -431,6 +431,25 @@ description are always previewed by ~org-link-preview~. When set to
non-nil, previewing links with description will not require =[C-u 1]=
toggle.
+*** New custom variable ~org-export-other-languages~
+
+Org allows you to indicate secondary languages (as opposed to the main
+language stored in ~org-export-default-language~). This variable is a
+lisp of Org language codes and can also be indicated with keyword
+~#+OTHER_LANGUAGES~, for example:
+
+#+BEGIN_SRC org
+,#+LANGUAGE: en
+,#+OTHER_LANGUAGES: es de
+#+END_SRC
+
+indicates that the document is mainly written in American English and
+includes passages in Spanish and German.
+
+It is up to the exporters to use this variable or not. Currently, it
+is used to add language information in the PDF metadata when exporting
+through ox-latex or ox-beamer.
+
** New functions and changes in function arguments
# This also includes changes in function behavior from Elisp perspective.
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index 56ca3ada9..5c9906975 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -665,6 +665,29 @@ (defcustom org-latex-doc-metadata nil
(string :tag "Metadata for the PDF output"))
:safe #'string-or-null-p)
+
+(defun org-latex--build-metadata-string (info)
+ "Extract the document metadata from INFO and handle language info.
+
+When metadata are defined and contain the keyword DOC_LANGS, replace it with
+\"language=<main>[, other-languages={<other>}]\", where <main> is defined in
+#+LANGUAGE and <other> is defined in #+OTHER_LANGUAGES. Omit the other-languages
+if not defined.
+
+Cf. URL `https://latex3.github.io/babel/news/whats-new-in-babel-26.9.html'."
+ (let ((doc-metadata (plist-get info :latex-doc-metadata)))
+ (when (and doc-metadata
+ (string-match-p "DOC_LANGS" doc-metadata))
+ (let* ((replace-other (plist-get info :other-languages))
+ (replace-other (and replace-other
+ ;; We might need a function here to translate from
+ ;; Org to BCP-47 language codes
+ (mapconcat #'identity replace-other ", ")))
+ (replace-langs (concat "language=" (plist-get info :language)
+ (and replace-other (concat ", other-languages={ " replace-other " }")))))
+ (setq doc-metadata (string-replace "DOC_LANGS" replace-langs doc-metadata))))
+ doc-metadata))
+
;;;; Headline
(defcustom org-latex-format-headline-function
@@ -2247,7 +2270,7 @@ (defun org-latex-make-preamble (info &optional template snippet?)
specified in `org-latex-default-packages-alist' or
`org-latex-packages-alist'."
(let* ((class (plist-get info :latex-class))
- (doc-metadata (plist-get info :latex-doc-metadata))
+ (doc-metadata (org-latex--build-metadata-string info))
(class-template
(or template
(let* ((class-options (org-latex--mk-options (plist-get info :latex-class-options)))
diff --git a/lisp/ox.el b/lisp/ox.el
index 182aefc1b..b3782be99 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -112,6 +112,7 @@ (defconst org-export-options-alist
(:author "AUTHOR" nil user-full-name parse)
(:email "EMAIL" nil user-mail-address t)
(:language "LANGUAGE" nil org-export-default-language t)
+ (:other-languages "OTHER_LANGUAGES" nil org-export-other-languages split)
(:select-tags "SELECT_TAGS" nil org-export-select-tags split)
(:exclude-tags "EXCLUDE_TAGS" nil org-export-exclude-tags split)
(:creator "CREATOR" nil org-export-creator-string)
@@ -553,6 +554,20 @@ (defcustom org-export-default-language "en"
:type '(string :tag "Language")
:safe #'stringp)
+(defcustom org-export-other-languages nil
+ "A list of strings with the Org language codes for other languages
+used in the document.
+
+ox-latex inserts this list as \"other-languages = {<list>}\" in
+the document metadata (along with the export language defined in the
+#+LANGUAGE: keyword as \"language = {<lang>}\"), when the shorthand
+DOC_LANGS is included in `org-latex-doc-metadata'."
+
+ :package-version '(Org . "10")
+ :type '(repeat
+ (string :tag "Other languages used in the document."))
+ :safe #'string-or-null-p)
+
(defcustom org-export-preserve-breaks nil
"Non-nil means preserve all line breaks when exporting.
This option can also be set with the OPTIONS keyword,
diff --git a/testing/lisp/test-ox-latex.el b/testing/lisp/test-ox-latex.el
index 9cd83d509..24ef90b4a 100644
--- a/testing/lisp/test-ox-latex.el
+++ b/testing/lisp/test-ox-latex.el
@@ -527,7 +527,8 @@ (ert-deftest test-ox-latex/pdf-metadata ()
(org-test-with-exported-text
'latex
"#+TITLE: PDF Metadata
-#+LANGUAGE: en-gb es
+#+LANGUAGE: en-gb
+#+OTHER_LANGUAGE: es
#+OPTIONS: toc:nil H:3 num:nil
#+LATEX_COMPILER: pdflatex
#+LATEX_DOC_METADATA: tagging = on
@@ -537,12 +538,34 @@ (ert-deftest test-ox-latex/pdf-metadata ()
Just to see that DocumentMetadata comes before PassOptions and documentclass
"
- (message "pdf-metadata: %s" (buffer-string))
+ ;; (message "pdf-metadata: %s" (buffer-string))
(goto-char (point-min))
(should (search-forward "\\DocumentMetadata{tagging = on}" nil t))
(should (search-forward "\\PassOptionsToPackage{dvipsnames}{xcolor}" nil t))
(should (re-search-forward "^\\\\documentclass\\[.+?]{report}" nil t))))
+(ert-deftest test-ox-latex/metadata-langs ()
+ "Test that DocumentMetadata treat the DOC_LANGS keyword correctly."
+ (org-test-with-exported-text
+ 'latex
+ "#+TITLE: PDF Metadata with languages
+#+LANGUAGE: en
+#+OTHER_LANGUAGES: es fr
+#+OPTIONS: toc:nil H:3 num:nil
+#+LATEX_COMPILER: pdflatex
+#+LATEX_DOC_METADATA: tagging = on,
+#+LATEX_DOC_METADATA: DOC_LANGS
+#+LATEX_CLASS: report
+* Testing
+
+Just to see that DocumentMetadata has the right languages
+"
+ ;; (message "pdf-metadata: %s" (buffer-string))
+ (goto-char (point-min))
+ (should (search-forward "\\DocumentMetadata{tagging = on," nil t))
+ (should (search-forward "language=en, other-languages={ es, fr }}" nil t))
+ (should (re-search-forward "^\\\\documentclass\\[.+?]{report}" nil t))))
+
(ert-deftest test-ox-latex/example-env-options ()
"We can set and override the options in an EXAMPLE block."
(let ((org-latex-default-example-environment "Verbatim")
@@ -587,7 +610,7 @@ (ert-deftest test-ox-latex/lualatex-fontspec-recognised ()
(org-test-with-exported-text
'latex
"#+TITLE: LuaLaTeX fonts
-#+LANGUAGE: en-gb es
+#+LANGUAGE: en-gb
#+OPTIONS: toc:nil H:3 num:nil
#+LATEX_COMPILER: lualatex
#+LATEX_CLASS: report
@@ -611,7 +634,7 @@ (ert-deftest test-ox-latex/lualatex-fontspec-fallback ()
(org-test-with-exported-text
'latex
"#+TITLE: LuaLaTeX fonts with emojis
-#+LANGUAGE: en-gb es
+#+LANGUAGE: en-gb
#+OPTIONS: toc:nil H:3 num:nil
#+LATEX_COMPILER: lualatex
#+LATEX_CLASS: report
@@ -639,7 +662,7 @@ (ert-deftest test-ox-latex/lualatex-fontspec-latex-header-not-lost ()
(org-test-with-exported-text
'latex
"#+TITLE: LuaLaTeX fonts
-#+LANGUAGE: en-gb es
+#+LANGUAGE: en-gb
#+OPTIONS: toc:nil H:3 num:nil
#+LATEX_COMPILER: lualatex
#+LATEX_HEADER: \\setsansfont{TeX Gyre Heros}
--
2.43.0