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

Reply via email to