From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2.migadu.com ([2001:41d0:303:e16b::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms8.migadu.com with LMTPS id aEhWMNh5CWYMOgEAe85BDQ:P1 (envelope-from ) for ; Sun, 31 Mar 2024 16:57:28 +0200 Received: from aspmx1.migadu.com ([2001:41d0:303:e16b::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2.migadu.com with LMTPS id aEhWMNh5CWYMOgEAe85BDQ (envelope-from ) for ; Sun, 31 Mar 2024 16:57:28 +0200 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=kXF7LoxD; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1711897048; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=ZvjyjV6uDqwnt0hYTfcSpw0CmLC1RNW4y9PqXGU2FC8=; b=MabVTwJzxJo8k1AC/x8NdGrrgxKTRuc8K5WlUlhsZBpHp/qI+65Kqqm7zW5h5N4qL1zZ/X BnsKvWapAAbBy+fT49Rt7X/btD8hrKi75nx3MvRMltvS2I27vQTDgVd8+8+o1NU5BCAnVb RLc2/HEmdRYc/Wyam/kc4F8xYnJubsjEyczuVL1hiKDr/cHGyJKR5EX/6/9pZKYDUTPwPy lTdVuwqJcZN3KYbUBnyyrZAih7pAfpPzvEpOQVlvI5ztwKyVwcNchmbtcWdp2R6euYDYPi rXPvA+Lvp28EPzf9QZ0YE6+TRq4uTc1PXlZWoEUQtgiA41BFKxOSLcDPs6HUMg== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=kXF7LoxD; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=key1; d=yhetil.org; t=1711897048; a=rsa-sha256; cv=none; b=pvDQFox35/r8jHKR/AHvE2vXsMtGiij++tX1bJgBIBj9suhrbFbrVfFxRSf6V5HyZvpPMh Dlm0cAt7uR2E9CzrlovRzTu6RKb2iO0XUGu11H/sKtoajo0JYAcPh9jIDFzi4g+OcbPtRq H7/CkZgX+e1o7swRdciHg8Ie+zW1UmKtKcTIFV66HPzNxTDbxQ5WSV3I3K/KDPEnGcaVTE 8scAXEg+RFU9m/ZCBcHYRoPWn467cYUg7Snzp+CBHv3qD3w7QG3YYM4DH6cEpIo6aJwI9v 4bes2Uchn6+7gIQTycqW9vmihqBSwjQY+87vVrlQrgLT6670ClstdfLhZiv0Yw== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 1D66210F96 for ; Sun, 31 Mar 2024 16:57:28 +0200 (CEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rqwc7-00014C-GO; Sun, 31 Mar 2024 10:56:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rqwc5-00013j-Va for emacs-orgmode@gnu.org; Sun, 31 Mar 2024 10:56:49 -0400 Received: from mail-vk1-xa34.google.com ([2607:f8b0:4864:20::a34]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1rqwc3-0003ta-AX for emacs-orgmode@gnu.org; Sun, 31 Mar 2024 10:56:49 -0400 Received: by mail-vk1-xa34.google.com with SMTP id 71dfb90a1353d-4d435a60217so1140212e0c.0 for ; Sun, 31 Mar 2024 07:56:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711897005; x=1712501805; darn=gnu.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=ZvjyjV6uDqwnt0hYTfcSpw0CmLC1RNW4y9PqXGU2FC8=; b=kXF7LoxDb8DXZUT1Y9IpZp/A6infzYKwzj1gixpxaLtRmvmwJKR64ERV6hvrElFQfb ZmS1ix1psPPH6UrEt2HtIHYhEm66W469gR/SyhHvH9RioEYXPdHtV/a/GJC0FcaMBfYN a8NRHKPJaACT0ILwMCxyjUGLZpIV6WrtLIyhEIoCwoc+IC3CIjzOgKmMQN2cTh2VaXHZ utxfrD3BqZk+aXUYxB3NkMof2KIEUbpg4pyxMm3xpkkEvz6WLsURg0Q9VZ375dgcpcfR BgbGd55r4bELL4aETvRzvyyn/PDSFzZj62blEXqu/XUZm8Zt9MTI1mDzt+8asBYcWXmn n32g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711897005; x=1712501805; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=ZvjyjV6uDqwnt0hYTfcSpw0CmLC1RNW4y9PqXGU2FC8=; b=DrTpp2w6eim0l7/gjcYzMXdmF6VWWM8KfNGzA8sWRQZcQUK4DamkyeTIKyBNLhuhtl ApRHWYws7XiVsvHNFDYF9Q2NceTXZ3gZej0AYEd26XMQq2Ztl07JUXGHUmw4a1P25NCT iJWzaK22PkAXOStlKO3wl1D54aHcx2WkrMvaZZIC5Z46v5p4lLqLIrXKB/71Yghrsjxs SilKeXyRW1xJIBoqiQDqp1uBPTJ5Kj7q9NQkx3lfsDHFnHOQuGOw8X31HQIEDnndFiFd 0wjBD9vIxhWIlMGdSmWyRaoWyO9FMi0DD9B2iGCVYPLOIOUWLHu0ZNCbSdjtBB3R46OF Q0SQ== X-Gm-Message-State: AOJu0YypvbQTq5tYYd7NkImXZdLN7KaZeAxMuoyfIVl8HKjP+m6Yyz0s Jgr9Zhz4k9TQ1OcJzj65NIzWIJr40vtVQSx0eMiK9CDJNLO32QUjIfp74eUtBzCXvqNVNEhivkj tEV+Crw1YMGxb1/HSJet3iAJEEDSUCzDtA2k= X-Google-Smtp-Source: AGHT+IGQdVe9PeY5utc/wqHDqhUCibbeW37o3JtYr52JkPTOZ1sHabIyUMx5tIQ7/crI1SWtR79h6ITz4f8Xicb9aUQ= X-Received: by 2002:a05:6122:a18:b0:4cd:20ea:35aa with SMTP id 24-20020a0561220a1800b004cd20ea35aamr4672365vkn.8.1711897004508; Sun, 31 Mar 2024 07:56:44 -0700 (PDT) MIME-Version: 1.0 References: <87msrudgcn.fsf@posteo.net> In-Reply-To: <87msrudgcn.fsf@posteo.net> From: Daniel Clemente Date: Sun, 31 Mar 2024 14:56:18 +0000 Message-ID: Subject: Re: [proof of concept] inline language blocks To: =?UTF-8?Q?Juan_Manuel_Mac=C3=ADas?= Cc: orgmode Content-Type: multipart/alternative; boundary="000000000000d88b6a0614f614ba" Received-SPF: pass client-ip=2607:f8b0:4864:20::a34; envelope-from=n142857@gmail.com; helo=mail-vk1-xa34.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Spam-Score: -9.64 X-Migadu-Queue-Id: 1D66210F96 X-Migadu-Scanner: mx13.migadu.com X-Migadu-Spam-Score: -9.64 X-TUID: ixRrzVOtdNYc --000000000000d88b6a0614f614ba Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable > I have thought of a syntax that is as least intrusive as possible, so as > not to make reading uncomfortable. I have tried the following: > > :fr{some text in French} :it{some text in Italian} :la{some text in Latin= } Sorry for joining the discussion a bit late. A long time ago I created a syntax to be able to mix languages in that way, and a program to separate each version into a different file. Info: https://www.danielclemente.com/dislines/ Sample: https://www.danielclemente.com/dislines/perl.txt Syntax: https://www.danielclemente.com/dislines/syntax.en.html Syntax in practice: https://www.danielclemente.com/dislines/quick.en.txt It's format-agnostic, and unrelated to org. I have used it in plain HTML and other file types. Many times I tried to use it for org-mode, and while it works, the challenges are more. For instance I'd like to integrate it into the export process (so that a single command produces many files), I want to use it to translate headers (but where do you keep the translations of the header name? in the header itself? in a property?), I want the TOC to use the translated headers, and I want to keep links working (and making sure each language only links to files in the same language). More difficult yet: what if a particular language requires another header structure (more headers, fewer headers, or headers arranged in another way). I tried several approaches (inline tasks, SRC blocks, my own syntax, tags in headers, one sub-header per language, selective export of tags, properties in headers, post-processing, exporting all and making language selection in JS, one manual TOC per language, =E2=80=A6). But I didn't have= time to think or discover a good system. Multi-language hypertext systems are hard. Maybe you can get some ideas from this, if you're still working on mixing human languages in org paragraphs/headers/lines/files. I see the discussion may have shifted from multilingual texts (i.e. human languages) to multi-backend texts (e.g. export HTML/LaTeX/=E2=80=A6 differently); multi-b= ackend variations might be an easier goal than dealing with multilingual texts and translations. Thanks for implementing code for your ideas. On Tue, 20 Feb 2024 at 20:36, Juan Manuel Mac=C3=ADas wrote: > Hi, > > I'm dedicating a local branch to developing this proof of concept and > testing it in my workflow, so far with good results. The basic idea is > to provide Org with multilingual features and various methods for > selecting languages. > > The inline-language-block is intended for small segments of text in a > language other than the main language. They can span several lines but > not more than a paragraph. They can be used for in-line textual quotes, > glosses, etc. They are constructions equivalent to, for example, LaTeX > \foreignlanguage{french}{text} or HTML text. > > I have thought of a syntax that is as least intrusive as possible, so as > not to make reading uncomfortable. I have tried the following: > > :fr{some text in French} :it{some text in Italian} :la{some text in Latin= } > > You can also use a literal term instead of a language code: > > :klingon!{some text in Klingon} > > The blocks can be nested: > > :klingon!{Some text in Klingon with :it{some text in Italian}} > > And they may include other elements: > > :el{Some text in Greek with a {{{macro}}} and a [[link]]} > > To this end, I have defined the following element: > > #+begin_src emacs-lisp > (defun org-element-inline-language-block-parser () > "Parse inline language block at point. > > When at an inline language block, return a new syntax node of > `inline-language-block' type containing `:begin', `:end', > `:type', `:contents-begin', `:contents-end' and `:post-blank' as > properties. Otherwise, return nil. > > Assume point is at the beginning of the block." > (save-excursion > (when (looking-at ":\\([A-Za-z!]+\\){") > (goto-char (match-end 0)) > (let* ((begin (match-beginning 0)) > (contents-begin (match-end 0)) > (type (org-element--get-cached-string > (match-string-no-properties 1))) > (contents-end > (progn > (goto-char (- contents-begin 1)) > (org-element--parse-paired-brackets ?\{) > (- (point) 1))) > (post-blank (skip-chars-forward " \t")) > (end (point))) > (when contents-end > (org-element-create > 'inline-language-block > (list :type type > :contents-begin contents-begin > :contents-end contents-end > :begin begin > :end end > :post-blank post-blank))))))) > > (defun org-element-inline-language-block-interpreter > (inline-language-block contents) > "Interpret INLINE LANGUAGE BLOCK object as Org syntax." > (format ":%s{%s}" > (org-element-property :type inline-language-block) > contents)) > #+end_src > > And, for now, this function for ox-latex: > > #+begin_src emacs-lisp > (defun org-latex-inline-language-block (inline-language-block contents > info) > "Transcode an INLINE LANGUAGE BLOCK element from Org to LaTeX. > CONTENTS holds the contents of the block. INFO is a plist > holding contextual information." > (let ((type (org-element-property :type inline-language-block))) > (if (string-match-p "!" type) > (format "\\foreignlanguage{%s}{%s}" > (replace-regexp-in-string "!" "" type) > contents) > (let* ((plist (cdr > (assoc type org-latex-language-alist))) > (language (plist-get plist :babel)) > (language-ini-only (plist-get plist :babel-ini-only)) > (language-ini-alt (plist-get plist :babel-ini-alt)) > (lang (or language language-ini-only language-ini-alt))) > (format "\\foreignlanguage{%s}{%s}" lang contents))))) > #+end_src > > Opinions, suggestions, feedback, ideas? > > Best regards, > > Juan Manuel > > -- > Juan Manuel Mac=C3=ADas -- Composici=C3=B3n tipogr=C3=A1fica, tratamiento= de datos, > dise=C3=B1o editorial y ortotipograf=C3=ADa > > --000000000000d88b6a0614f614ba Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable

> I have thought of a syntax that is as least intrusive as possible, so = as
> not to make reading uncomfortable. I have tried the following:
>
> :fr{some text in French} :it{some text in Italian} :la{so= me text in Latin}

Sorry for joining the discussion= a bit late. A long time ago I created a syntax to be able to mix languages= in that way, and a program to separate each version into a different file.=

Sample: https://www.dani= elclemente.com/dislines/perl.txt

It's format-a= gnostic, and unrelated to org. I have used it in plain HTML and other file = types.
Many times I tried to use it for org-mode, and while it wo= rks, the challenges are more. For instance I'd like to integrate it in= to the export process (so that a single command produces many files), I wan= t to use it to translate headers (but where do you keep the translations of= the header name? in the header itself? in a property?), I want the TOC to= use the translated headers, and I want to keep links working (and making s= ure each language only links to files in the same language). More difficult= yet: what if a particular language requires another header structure (more= headers, fewer headers, or headers arranged in another way).
I tried several approaches (inline tasks, SRC blocks, my own syntax, tags = in headers, one sub-header per language, selective export of tags, properti= es in headers, post-processing, exporting all and making language selection= in JS, one manual TOC per language, =E2=80=A6). But I didn't have time= to think or discover a good system. Multi-language hypertext systems are h= ard.

Maybe you can get some ideas from this, if yo= u're still working on mixing human languages in org paragraphs/headers/= lines/files. I see the discussion may have shifted from multilingual texts = (i.e. human languages) to multi-backend texts (e.g. export HTML/LaTeX/=E2= =80=A6 differently); multi-backend variations might be an easier goal than = dealing with multilingual texts and translations.

<= div>Thanks for implementing code for your ideas.

On Tue, 20 Feb 20= 24 at 20:36, Juan Manuel Mac=C3=ADas <maciaschain@posteo.net> wrote:
Hi,

I'm dedicating a local branch to developing this proof of concept and testing it in my workflow, so far with good results. The basic idea is
to provide Org with multilingual features and various methods for
selecting languages.

The inline-language-block is intended for small segments of text in a
language other than the main language. They can span several lines but
not more than a paragraph. They can be used for in-line textual quotes,
glosses, etc. They are constructions equivalent to, for example, LaTeX
\foreignlanguage{french}{text} or HTML <span lang=3Dfr>text</span&= gt;.

I have thought of a syntax that is as least intrusive as possible, so as not to make reading uncomfortable. I have tried the following:

:fr{some text in French} :it{some text in Italian} :la{some text in Latin}<= br>
You can also use a literal term instead of a language code:

:klingon!{some text in Klingon}

The blocks can be nested:

:klingon!{Some text in Klingon with :it{some text in Italian}}

And they may include other elements:

:el{Some text in Greek with a {{{macro}}} and a [[link]]}

To this end, I have defined the following element:

#+begin_src emacs-lisp
(defun org-element-inline-language-block-parser ()
=C2=A0 "Parse inline language block at point.

When at an inline language block, return a new syntax node of
`inline-language-block' type containing `:begin', `:end',
`:type', `:contents-begin', `:contents-end' and `:post-blank= 9; as
properties.=C2=A0 Otherwise, return nil.

Assume point is at the beginning of the block."
=C2=A0 (save-excursion
=C2=A0 =C2=A0 (when (looking-at ":\\([A-Za-z!]+\\){")
=C2=A0 =C2=A0 =C2=A0 (goto-char (match-end 0))
=C2=A0 =C2=A0 =C2=A0 (let* ((begin (match-beginning 0))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(contents-begin (match-end = 0))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(type (org-element--get-cac= hed-string
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (matc= h-string-no-properties 1)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(contents-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (progn
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (goto-char (- conte= nts-begin 1))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (org-element--parse= -paired-brackets ?\{)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (- (point) 1)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(post-blank (skip-chars-for= ward " \t"))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(end (point)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (when contents-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (org-element-create
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'inline-language-block
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(list :type type
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0:contents-beg= in contents-begin
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0:contents-end= contents-end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0:begin begin<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0:end end
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0:post-blank p= ost-blank)))))))

(defun org-element-inline-language-block-interpreter (inline-language-block= contents)
=C2=A0 "Interpret INLINE LANGUAGE BLOCK object as Org syntax." =C2=A0 (format ":%s{%s}"
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (org-element-property :type inline-langu= age-block)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 contents))
#+end_src

And, for now, this function for ox-latex:

#+begin_src emacs-lisp
(defun org-latex-inline-language-block (inline-language-block contents info= )
=C2=A0 "Transcode an INLINE LANGUAGE BLOCK element from Org to LaTeX.<= br> CONTENTS holds the contents of the block.=C2=A0 INFO is a plist
holding contextual information."
=C2=A0 (let ((type (org-element-property :type inline-language-block)))
=C2=A0 =C2=A0 (if (string-match-p "!" type)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (format "\\foreignlanguage{%s}{%s}" =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (replace-regexp-in-= string "!" "" type)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 contents)
=C2=A0 =C2=A0 =C2=A0 (let* ((plist (cdr
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(assoc type org-latex-language-alist)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(language (plist-get plist = :babel))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(language-ini-only (plist-g= et plist :babel-ini-only))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(language-ini-alt (plist-ge= t plist :babel-ini-alt))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(lang (or language language= -ini-only language-ini-alt)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (format "\\foreignlanguage{%s}{%s}" l= ang contents)))))
#+end_src

Opinions, suggestions, feedback, ideas?

Best regards,

Juan Manuel

--
Juan Manuel Mac=C3=ADas -- Composici=C3=B3n tipogr=C3=A1fica, tratamiento d= e datos, dise=C3=B1o editorial y ortotipograf=C3=ADa

--000000000000d88b6a0614f614ba--