Mondo — World's scripts, languages, places in any language (Emacs package)

Below you find the latest version of (1) the package's README and (2) its main source file.

For the git repository and issue tracker, see the project's page on sr.ht.

For more packages, see Software.


README.org

Overview

Mondo can translate into (almost) every language the names of the world's scripts, languages, territories (including countries), and countries' subdivisions.

Translations happen locallyin your machine.

Let's see some examples.

Non-interactive functions

These are pure functions to perform lookups.

Simplest examples

Here are some translations into French, using each of the four public functions:

(mondo-script 'fr 'Hrkt) => "katakana ou hiragana"
(mondo-langue 'fr 'ja)   => "japonais"
(mondo-territ 'fr 'JP)   => "Japon"
(mondo-subdiv 'fr 'jp01) => "préfecture de Hokkaidō"

Org table example

Switzerland has four official languages. What are their names in themselves?

Let's build an empty Org table with a formula:

|    | de | fr | it | rm |
|----+----+----+----+----|
| de |    |    |    |    |
| fr |    |    |    |    |
| it |    |    |    |    |
| rm |    |    |    |    |
#+tblfm: @2$2..@>$>='(mondo-langue $1 @1)

After C-c C-c'ing the #+tblfm line we get:

🇨🇭 Official languages of Switzerland

  de fr it rm
de Deutsch Französisch Italienisch Rätoromanisch
fr allemand français italien romanche
it tedesco francese italiano romancio
rm tudestg franzos talian rumantsch

What about the country's name?

|    | Language | Country |
|----+----------+---------|
| de |          |         |
| fr |          |         |
| it |          |         |
| rm |          |         |
#+tblfm: $2='(mondo-langue 'en $1)
#+tblfm: $3='(mondo-territ $1 'CH)

After C-c C-c'ing the #+tblfm lines we get:

🇨🇭 Switzerland name in its four official languages

  Language Country
de German Schweiz
fr French Suisse
it Italian Svizzera
rm Romansh Svizra

Could we name all Swiss cantons?

|    |    |    |    |
|----+----+----+----|
| ZH |    |    |    |
| BE |    |    |    |
| LU |    |    |    |
| UR |    |    |    |
| SZ |    |    |    |
| OW |    |    |    |
| NW |    |    |    |
| GL |    |    |    |
| ZG |    |    |    |
| FR |    |    |    |
| SO |    |    |    |
| BS |    |    |    |
| BL |    |    |    |
| SH |    |    |    |
| AR |    |    |    |
| AI |    |    |    |
| SG |    |    |    |
| GR |    |    |    |
| AG |    |    |    |
| TG |    |    |    |
| TI |    |    |    |
| VD |    |    |    |
| VS |    |    |    |
| NE |    |    |    |
| GE |    |    |    |
| JU |    |    |    |
|----+----+----+----|
|    | de | fr | it |
#+tblfm: @1='(mondo-langue 'en @>)
#+tblfm: @2$2..@>>$>='(mondo-subdiv @> (format "ch%s" $1))

(Subdivision data in Romansh is unavailable, so we won't add this column.)

We C-c C-c both #+tblfm lines and...

|    | German                | French                                | Italian                   |
|----+-----------------------+---------------------------------------+---------------------------|
| ZH | Zürich                | canton de Zurich                      | Canton Zurigo             |
| BE | Bern                  | canton de Berne                       | Canton Berna              |
| LU | Luzern                | canton de Lucerne                     | Canton Lucerna            |
| UR | Uri                   | canton d’Uri                          | Canton Uri                |
| SZ | Schwyz                | canton de Schwytz                     | Canton Svitto             |
| OW | Obwalden              | canton d’Obwald                       | Canton Obvaldo            |
| NW | Nidwalden             | canton de Nidwald                     | Canton Nidvaldo           |
| GL | Glarus                | canton de Glaris                      | Canton Glarona            |
| ZG | Zug                   | canton de Zoug                        | Canton Zugo               |
| FR | Freiburg              | canton de Fribourg                    | Canton Friburgo           |
| SO | Solothurn             | canton de Soleure                     | Canton Soletta            |
| BS | Basel-Stadt           | canton de Bâle-Ville                  | Canton Basilea Città      |
| BL | Basel-Landschaft      | canton de Bâle-Campagne               | Canton Basilea Campagna   |
| SH | Schaffhausen          | canton de Schaffhouse                 | Canton Sciaffusa          |
| AR | Appenzell Außerrhoden | canton d’Appenzell Rhodes-Extérieures | Canton Appenzello Esterno |
| AI | Appenzell Innerrhoden | canton d’Appenzell Rhodes-Intérieures | Canton Appenzello Interno |
| SG | St. Gallen            | canton de Saint-Gall                  | Canton San Gallo          |
| GR | Graubünden            | canton des Grisons                    | Cantone dei Grigioni      |
| AG | Aargau                | canton d’Argovie                      | Canton Argovia            |
| TG | Thurgau               | canton de Thurgovie                   | Canton Turgovia           |
| TI | Tessin                | canton du Tessin                      | Canton Ticino             |
| VD | Waadt                 | canton de Vaud                        | Canton Vaud               |
| VS | Wallis                | canton du Valais                      | Canton Vallese            |
| NE | Neuenburg             | canton de Neuchâtel                   | Canton Neuchâtel          |
| GE | Genf                  | canton de Genève                      | Canton Ginevra            |
| JU | Jura                  | canton du Jura                        | Canton Giura              |
|----+-----------------------+---------------------------------------+---------------------------|
|    | de                    | fr                                    | it                        |
#+tblfm: @1='(mondo-langue 'en @>)
#+tblfm: @2$2..@>>$>='(mondo-subdiv @> (format "ch%s" $1))

"But flandrew...", you object. "'Außerrhoden'? Swiss High German wouldn't spell that with an Eszett!"

I'm glad you noticed.

To address your orthographic concern, let's try using local standards of the languages first — by replacing this:

(mondo-subdiv @> (format "ch%s" $1)
;;   de, fr, it^  ^ZH..JU → chzh..chju

with this:

(mondo-subdiv (format "%s_CH" @>) (format "ch%s" $1))
;;             ^Swiss de, fr, it   ^ZH..JU → chzh..chju

And since we're on it, what if we wanted to remove all those "canton" that show up in French and Italian?

Let's write a temporary function to do all that:

(defun mondo//swiss-cantons (lang canton)
  (let* ((ch (format "%s_CH" lang))
         (ca (format "ch%s" canton))
         (nm (mondo-subdiv ch ca))
         (r1 (regexp-opt '("canton" "Canton" "Cantone")))
         (r2 (regexp-opt '("d’" "du" "de" "des" "dei")))
         (rc (format "^%s %s? *" r1 r2)))
    (replace-regexp-in-string rc "" nm)))

And test it:

;; We had:
(mondo-subdiv 'de "char") => "Appenzell Außerrhoden"
(mondo-subdiv 'fr "char") => "canton d’Appenzell Rhodes-Extérieures"
(mondo-subdiv 'it "char") => "Canton Appenzello Esterno"

;; Now we have:
(mondo//swiss-cantons 'de "AR") => "Appenzell Ausserrhoden"
(mondo//swiss-cantons 'fr "AR") => "Appenzell Rhodes-Extérieures"
(mondo//swiss-cantons 'it "AR") => "Appenzello Esterno"

Looks good. So we replace the second formula with this:

#+tblfm: @2$2..@>>$>='(mondo//swiss-cantons @> $1)

then C-c C-c both #+tblfm lines again, and...

🇨🇭 Swiss cantons

  German French Italian
ZH Zürich Zurich Zurigo
BE Bern Berne Berna
LU Luzern Lucerne Lucerna
UR Uri Uri Uri
SZ Schwyz Schwytz Svitto
OW Obwalden Obwald Obvaldo
NW Nidwalden Nidwald Nidvaldo
GL Glarus Glaris Glarona
ZG Zug Zoug Zugo
FR Freiburg Fribourg Friburgo
SO Solothurn Soleure Soletta
BS Basel-Stadt Bâle-Ville Basilea Città
BL Basel-Landschaft Bâle-Campagne Basilea Campagna
SH Schaffhausen Schaffhouse Sciaffusa
AR Appenzell Ausserrhoden Appenzell Rhodes-Extérieures Appenzello Esterno
AI Appenzell Innerrhoden Appenzell Rhodes-Intérieures Appenzello Interno
SG St. Gallen Saint-Gall San Gallo
GR Graubünden Grisons Grigioni
AG Aargau Argovie Argovia
TG Thurgau Thurgovie Turgovia
TI Tessin Tessin Ticino
VD Waadt Vaud Vaud
VS Wallis Valais Vallese
NE Neuenburg Neuchâtel Neuchâtel
GE Genf Genève Ginevra
JU Jura Jura Giura
  de fr it

Better!

Interactive functions

There are two kinds of interactive functions.

Translate a name

These are commands with which you can translate a query interactively.

For example, you can run this: M-x mondo-dwim

If you had a region selected, it'd use that. Otherwise it'd ask you for a query string.

Suppose you had a region selected.
Its contents were exactly gbwls, which is the subdivision code for Wales, UK.

Then it asked you for a language, and you picked French.

The string will then be replaced with Wales in French: Pays de Galles.

Display in a buffer

These are commands to show you tables with the data.

First, choose what you want to see (say, subdivisions):

M-x mondo-subdiv-buffer

Then, pick a language from the minibuffer (say, Catalan).

A new buffer pops up and...

Mondo's Subdivisions — Catalan (ca)

id default
ad02 Canillo
ad03 Encamp
ad04 La Massana
ad05 Ordino
ad06 Sant Julià de Lòria
ad07 Andorra la Vella
ad08 Escaldes-Engordany
.... ..........................
zwmn Matabeleland Septentrional
zwms Matabeleland Meridional
zwmv Província de Masvingo
zwmw Mashonaland Occidental

Still more examples

You'll find a couple hundred extra examples further below, starting at mondo-script.

Installation

For this package to work at all, you need to have translation data in your machine. Start with that.

Download the necessary translation data

You need data from Unicode Consortium's Common Locale Data Repository (CLDR).

That data is available for you to download free of charge, and is licensed under the free and open-source Unicode-3.0 license.

Option 1: Linux repositories

If you're using Linux, you may find it in repositories. Search for packages containing cldr.

In some distributions the package is called unicode-cldr-core, and installing it would put all the necessary files under /usr/share/unicode/cldr/common.

Option 2: direct download

Alternatively, you can download the most recent version directly. For that:

wget https://unicode.org/Public/cldr/latest/core.zip

or any other download method.

Then choose where to extract the file to. For example:

CLDRDIR=/usr/local/share/unicode/cldr

or any other path of your preference.

Then:

sudo mkdir -p "$CLDRDIR"
sudo unzip core.zip -d "$CLDRDIR"
ls -l "$CLDRDIR"/common  # just to check

Note that mondo (currently) only looks up data in common/main and common/subdivisions.

All of the above should equally work on other systems — such as Mac or Windows — as long as you're able to fetch and extract the data.

With that done, install mondo.

Install mondo

See my page Software for the most up-to-date instructions on how to download and install any of my Emacs packages.

Having downloaded and installed the package and its dependencies, adapt the configurations below to your init.el file.

(use-package mondo :demand t)

Alternatively, if you don't have use-package:

(require 'mondo)

Customize mondo-cldr-common-path

If you installed CLDR from a Linux repository, try this:

(require 'mondo)
(file-directory-p mondo-cldr-common-path) => t

If it returns t, you're probably done: it means that it installed to the default directory.

Otherwise, customize the variable mondo-cldr-common-path so that it points to the chosen path.

Option 1

(customize-variable 'mondo-cldr-common-path)
;; or:  M-x customize-variable RET mondo-cldr-common-path RET

The value there could be, for example, /usr/local/share/unicode/cldr/common — or wherever it is that you extracted it to.

If you edited the value, save it.

Option 2

You could instead do it here:

(use-package mondo
  :demand t
  :custom
  (mondo-cldr-common-path "/usr/local/share/unicode/cldr/common")) ;<-- C-x C-e

Test it

It should now work. Test it:

(mondo-script 'es 'Maya)  => "jeroglíficos mayas"
(mondo-langue 'es 'en_US) => "inglés estadounidense"
(mondo-territ 'es 'JP)    => "Japón"
(mondo-subdiv 'es 'CA-NS) => "Nueva Escocia"
;;                       ^C-x C-e

and enjoy.

Disclaimer

This package doesn't take or endorse any position on territorial claims, nor on language names, nor on which should be some place's official script, nor on any such disputes.

All this package does is:

  • take your input query;
  • look it up on some of the CLDR's XML files that you fetched from upstream sources;
  • and output what it finds.

If the output doesn't match the source data, that might be due to a bug in this package. Please report.

If, however, you believe something in the source data itself is wrong, consider sending a bug report to the Unicode Consortium.

Summary of callables

Here's an overview of this package's callables:

Function Summary
mondo-script Name of the script QUERY in language LANG.
mondo-langue Name of the language QUERY in language LANG.
mondo-territ Name of the territory QUERY in language LANG.
mondo-subdiv Name of the country subdivision QUERY in language LANG.
mondo-script-ht Output hash table of all scripts in language LANG.
mondo-langue-ht Output hash table of all languages in language LANG.
mondo-territ-ht Output hash table of all territories in language LANG.
mondo-subdiv-ht Output hash table of all subdivisions in language LANG.
mondo-dwim Ask for query or use selection. Ask language. Return name of query in it.
mondo-region Replace selection (a query) with its name in a language of your choice.
mondo-ask Ask for query and a language. Return name of query in that language.
mondo-script-buffer Display, in a language of your choice, the names of all scripts.
mondo-langue-buffer Display, in a language of your choice, the names of all languages.
mondo-territ-buffer Display, in a language of your choice, the names of all territories.
mondo-subdiv-buffer Display, in a language of your choice, the names of all subdivisions.
mondo-see-readme Open mondo's README.org file.
mondo-see-news See the News in mondo's README.org file.

They're described in more detail below.

Functions

Translate a name

Functions to translate to any language the name of a script, language,
territory, or subdivision. Territories, here, are not only countries,
but also regions such as North America or Middle Africa. Subdivisions
apply to each country.

Function arguments can be strings or symbols — the result is the same.

mondo-script (lang query &optional alt)

Name of the script QUERY in language LANG.

LANG is a language code. QUERY is a script code.
Optional arg ALT looks for an alternative name, if available.

;;;; Arguments can be passed as either symbol or string (same result)
(mondo-script 'en  'Hang)  => "Hangul"
(mondo-script "en" 'Hang)  => "Hangul"
(mondo-script 'en  "Hang") => "Hangul"
(mondo-script "en" "Hang") => "Hangul"

;;;; Some scripts have alternative names, available as optional 3rd arg
(mondo-script 'en  'Hans)                => "Simplified"
(mondo-script 'en  'Hans  'stand-alone)  => "Simplified Han"
(mondo-script "en" "Hant" "stand-alone") => "Traditional Han"

;;;; Default names
(mondo-script 'ar 'Egyp) => "الهيروغليفية"
(mondo-script 'ar 'Phnx) => "الفينيقية"
(mondo-script 'bn 'Beng) => "বাংলা"
(mondo-script 'bo 'Tibt) => "བོད་ཡིག་"
(mondo-script 'da 'Hrkt) => "japanske skrifttegn"
(mondo-script 'de 'Hrkt) => "Japanische Silbenschrift"
(mondo-script 'el 'Grek) => "Ελληνικό"
(mondo-script 'el 'Latn) => "Λατινικό"
(mondo-script 'es 'Hrkt) => "silabarios japoneses"
(mondo-script 'es 'Cyrs) => "cirílico del antiguo eslavo eclesiástico"
(mondo-script 'en 'Armi) => "Imperial Aramaic"
(mondo-script 'en 'Bali) => "Balinese"
(mondo-script 'en 'Bopo) => "Bopomofo"
(mondo-script 'en 'Brai) => "Braille"
(mondo-script 'en 'Cans) => "Unified Canadian Aboriginal Syllabics"
(mondo-script 'en 'Cyrl) => "Cyrillic"
(mondo-script 'en 'Cyrs) => "Old Church Slavonic Cyrillic"
(mondo-script 'en 'Deva) => "Devanagari"
(mondo-script 'en 'Egyp) => "Egyptian hieroglyphs"
(mondo-script 'en 'Hanb) => "Han with Bopomofo"
(mondo-script 'en 'Hrkt) => "Japanese syllabaries"
(mondo-script 'en 'Java) => "Javanese"  ; ꦗꦮ <-- real Java script!
(mondo-script 'en 'Latn) => "Latin"
(mondo-script 'en 'Linb) => "Linear B"
(mondo-script 'en 'Maya) => "Mayan hieroglyphs"
(mondo-script 'en 'Phnx) => "Phoenician"
(mondo-script 'en 'Tfng) => "Tifinagh"
(mondo-script 'en 'Tibt) => "Tibetan"
(mondo-script 'en 'Zmth) => "Mathematical Notation"
(mondo-script 'en 'Zsye) => "Emoji"
(mondo-script 'fr 'Cans) => "syllabaire autochtone canadien unifié"
(mondo-script 'fr 'Hrkt) => "katakana ou hiragana"
(mondo-script 'fr 'Latn) => "latin"
(mondo-script 'fr 'Xsux) => "cunéiforme suméro-akkadien"
(mondo-script 'he 'Hebr) => "עברי"
(mondo-script 'he 'Hrkt) => "הברתי יפני"
(mondo-script 'he 'Latn) => "לטיני"
(mondo-script 'hy 'Armn) => "հայկական"
(mondo-script 'ii 'Yiii) => "ꆈꌠꁱꂷ"
(mondo-script 'ka 'Geok) => "ხუცური"
(mondo-script 'ka 'Geor) => "ქართული"
(mondo-script 'hi 'Deva) => "देवनागरी"
(mondo-script 'hi 'Latn) => "लैटिन"
(mondo-script 'pa 'Deva) => "ਦੇਵਨਾਗਰੀ"
(mondo-script 'pa 'Guru) => "ਗੁਰਮੁਖੀ"
(mondo-script 'ko 'Hang) => "한글"   ; Hangul
(mondo-script 'ko 'Hani) => "한자"   ; Hanja
(mondo-script 'ja 'Hani) => "漢字"   ; Kanji
(mondo-script 'ja 'Hrkt) => "仮名"
(mondo-script 'ja 'Hira) => "ひらがな"
(mondo-script 'ja 'Kana) => "カタカナ"
(mondo-script 'ja 'Jpan) => "日本語の文字"
(mondo-script 'ja 'Hans) => "簡体字"
(mondo-script 'ja 'Hant) => "繁体字"
(mondo-script 'ja 'Hanb) => "漢語注音字母"
(mondo-script 'ja 'Bopo) => "注音字母"
(mondo-script 'ja 'Hang) => "ハングル"
(mondo-script 'ja 'Latn) => "ラテン文字"
(mondo-script 'ru 'Cyrl) => "кириллица"
(mondo-script 'ru 'Latn) => "латиница"
(mondo-script 'uk 'Cyrl) => "кирилиця"
(mondo-script 'uk 'Latn) => "латиниця"
(mondo-script 'vi 'Latn) => "Chữ La tinh"
(mondo-script 'vi 'Hrkt) => "Bảng ký hiệu âm tiết Tiếng Nhật"
(mondo-script 'zh 'Hani) => "汉字"
(mondo-script 'zh 'Hans) => "简体"
(mondo-script 'zh 'Hant) => "繁体"

;;;; When not found, either fall back or fail gracefully
(mondo-script 'en 'Hrkt 'foo) => "Japanese syllabaries"  ; ignore 'foo
(mondo-script 'en 'Fooo)      => ""
(mondo-script 'fu 'Kana)      => ""
(mondo-script 'fu 'Fooo)      => ""
(mondo-script 'fu 'Fooo 'foo) => ""

;;;; Language varieties fall back to parents when not found
(mondo-script 'en_AU 'Beng) => "Bengali"
(mondo-script 'en_GB 'Beng) => "Bangla"  ;| fall back to primary ('en)
(mondo-script 'en_KR 'Beng) => "Bangla"  ;|
(mondo-script 'en    'Beng) => "Bangla"

mondo-langue (lang query &optional alt)

Name of the language QUERY in language LANG.

LANG and QUERY are both language codes.
Optional arg ALT looks for an alternative name, if available.

;;;; Arguments can be passed as either symbol or string (same result)
(mondo-langue 'en  'zu)  => "Zulu"
(mondo-langue "en" 'zu)  => "Zulu"
(mondo-langue 'en  "zu") => "Zulu"
(mondo-langue "en" "zu") => "Zulu"

;;;; Some languages have alternative names, available as optional 3rd arg
(mondo-langue 'en  'en_GB)          => "British English"
(mondo-langue 'en  'en_GB   'short) => "UK English"
(mondo-langue 'en  'se)             => "Northern Sami"
(mondo-langue 'en  'sma)            => "Southern Sami"
(mondo-langue 'en  'se       'menu) => "Sami, Northern"
(mondo-langue 'en  'sma      'menu) => "Sami, Southern"
(mondo-langue 'en  'yue)            => "Cantonese"
(mondo-langue 'en  'yue      'menu) => "Chinese, Cantonese"
(mondo-langue 'en  'zh)             => "Chinese"
(mondo-langue 'en  'zh       'long) => "Mandarin Chinese"
(mondo-langue 'en  'zh       'menu) => "Chinese, Mandarin"
(mondo-langue 'en  'zh_Hans)        => "Simplified Chinese"
(mondo-langue 'en  'zh_Hans  'long) => "Simplified Mandarin Chinese"
(mondo-langue 'zh  'zh)             => "中文"
(mondo-langue 'zh  'zh       'menu) => "普通话"
(mondo-langue 'zh  'zh_Hans)        => "简体中文"

;;;; Default names
(mondo-langue 'de  'de)     => "Deutsch"
(mondo-langue 'de  'de_CH)  => "Schweizer Hochdeutsch"
(mondo-langue 'de  'bar)    => "Bairisch"
(mondo-langue 'en  'chr)    => "Cherokee"
(mondo-langue 'en  'csw)    => "Swampy Cree"
(mondo-langue 'en  'ikt)    => "Western Canadian Inuktitut"
(mondo-langue 'en  'cy)     => "Welsh"
(mondo-langue 'en  'de)     => "German"
(mondo-langue 'en  'de_CH)  => "Swiss High German"
(mondo-langue 'en  'en_AU)  => "Australian English"
(mondo-langue 'en  'es_MX)  => "Mexican Spanish"
(mondo-langue 'en  'nl)     => "Dutch"
(mondo-langue 'en  'nl_BE)  => "Flemish"
(mondo-langue 'en  'eo)     => "Esperanto"
(mondo-langue 'en  'ff)     => "Fulah"
(mondo-langue 'en  'ii)     => "Sichuan Yi"
(mondo-langue 'en  'iu)     => "Inuktitut"
(mondo-langue 'en  'ja)     => "Japanese"
(mondo-langue 'en  'jam)    => "Jamaican Creole English"
(mondo-langue 'en  'pcm)    => "Nigerian Pidgin"
(mondo-langue 'pcm 'pcm)    => "Naijíriá Píjin"
(mondo-langue 'en  'lou)    => "Louisiana Creole"
(mondo-langue 'en  'lzh)    => "Literary Chinese"
(mondo-langue 'en  'yue)    => "Cantonese"
(mondo-langue 'en  'hr)     => "Croatian"
(mondo-langue 'en  'sh)     => "Serbo-Croatian"
(mondo-langue 'en  'sr)     => "Serbian"
(mondo-langue 'en  'sr_ME)  => "Montenegrin"
(mondo-langue 'en  'st)     => "Southern Sotho"
(mondo-langue 'en  'sw)     => "Swahili"
(mondo-langue 'en  'sw_CD)  => "Congo Swahili"
(mondo-langue 'en  'eu)     => "Basque"
(mondo-langue 'eu  'eu)     => "euskara"
(mondo-langue 'en  'es_419) => "Latin American Spanish"     ;| Wikipedia:
(mondo-langue 'es  'es_419) => "español latinoamericano"    ;| "UN M49"
(mondo-langue 'fr  'es_419) => "espagnol d’Amérique latine" ;|
(mondo-langue 'fr  'ase)    => "langue des signes américaine"
(mondo-langue 'fr  'fr)     => "français"
(mondo-langue 'fr  'fr_CA)  => "français canadien"
(mondo-langue 'fr  'fr_CH)  => "français suisse"
(mondo-langue 'fr  'jam)    => "créole jamaïcain"
(mondo-langue 'fr  'lzh)    => "chinois littéraire"
(mondo-langue 'fr  'frp)    => "francoprovençal"
(mondo-langue 'fr  'lij)    => "ligure"
(mondo-langue 'fr  'oc)     => "occitan"
(mondo-langue 'fr  'br)     => "breton"
(mondo-langue 'br  'br)     => "brezhoneg"
(mondo-langue 'hi  'en)     => "अंग्रेज़ी"
(mondo-langue 'hi  'hi)     => "हिन्दी"
(mondo-langue 'hi  'sa)     => "संस्कृत"
(mondo-langue 'ii  'ii)     => "ꆈꌠꉙ"
(mondo-langue 'id  'id)     => "Indonesia"
(mondo-langue 'id  'fil)    => "Filipino"
(mondo-langue 'id  'tl)     => "Tagalog"
(mondo-langue 'id  'ms)     => "Melayu"
(mondo-langue 'id  'de)     => "Jerman"
(mondo-langue 'id  'fr)     => "Prancis"
(mondo-langue 'id  'en)     => "Inggris"
(mondo-langue 'it  'en)     => "inglese"
(mondo-langue 'it  'it)     => "italiano"
(mondo-langue 'it  'egl)    => "emiliano"
(mondo-langue 'it  'fur)    => "friulano"
(mondo-langue 'it  'nap)    => "napoletano"
(mondo-langue 'it  'rgn)    => "romagnolo"
(mondo-langue 'it  'scn)    => "siciliano"
(mondo-langue 'it  'lzh)    => "cinese classico"
(mondo-langue 'ja  'lzh)    => "漢文"
(mondo-langue 'ja  'zh)     => "中国語"
(mondo-langue 'ja  'en)     => "英語"
(mondo-langue 'lb  'lb)     => "Lëtzebuergesch"
(mondo-langue 'nl  'nl)     => "Nederlands"
(mondo-langue 'nl  'nl_BE)  => "Vlaams"
(mondo-langue 'no  'nb)     => "norsk bokmål"
(mondo-langue 'no  'nn)     => "norsk nynorsk"
(mondo-langue 'sw  'en)     => "Kiingereza"
(mondo-langue 'tr  'en)     => "İngilizce"
(mondo-langue 'tr  'sw)     => "Svahili dili"
(mondo-langue 'tr  'tr)     => "Türkçe"
(mondo-langue 'zh  'en)     => "英语"
(mondo-langue 'zh_Hant 'en) => "英文"

;;;; When not found, either fall back or fail gracefully
(mondo-langue 'en 'lzh 'foo) => "Literary Chinese"  ; ignore 'foo
(mondo-langue 'en 'fu)       => ""
(mondo-langue 'fu 'en)       => ""
(mondo-langue 'fu 'fu)       => ""

;;;; Language varieties fall back to parents when not found
(mondo-langue 'en_AU 'en_US)        => "United States English"
(mondo-langue 'en_KR 'en_US)        => "American English" ; fall back to 'en
(mondo-langue 'en    'en_US)        => "American English"
(mondo-langue 'en_KR 'en_US 'short) => "US English"       ; fall back to 'en
(mondo-langue 'en    'en_US 'short) => "US English"

mondo-territ (lang query &optional alt)

Name of the territory QUERY in language LANG.

LANG is a language code. QUERY is a territory code.
Optional arg ALT looks for an alternative name, if available.

;;;; Arguments can be passed as either symbol or string (same result)
(mondo-territ 'es  'NL)  => "Países Bajos"
(mondo-territ "es" 'NL)  => "Países Bajos"
(mondo-territ 'es  "NL") => "Países Bajos"
(mondo-territ "es" "NL") => "Países Bajos"

;;;; World regions have numeric codes
(mondo-territ 'en   001)  => "" ; Nope — not as a number. Symbol or string:
(mondo-territ 'en '\001)  => "world"
(mondo-territ 'en  "001") => "world"
(mondo-territ 'fr  "001") => "Monde"
(mondo-territ 'it  "001") => "Mondo"
(mondo-territ 'eo  "001") => "Mondo"
(mondo-territ 'ja  "001") => "世界"
(mondo-territ 'en  "017") => "Middle Africa"
(mondo-territ 'fr  "017") => "Afrique centrale"
(mondo-territ 'ja  "017") => "中部アフリカ"
(mondo-territ 'en  "019") => "Americas"
(mondo-territ 'fr  "019") => "Amériques"
(mondo-territ 'ja  "019") => "アメリカ大陸"

;;;; Some places have alternative names, available as optional 3rd arg
(mondo-territ 'de  'CI)          => "Côte d’Ivoire" ; official name
(mondo-territ 'de  'CI 'variant) => "Elfenbeinküste"
(mondo-territ 'de  'US)          => "Vereinigte Staaten"
(mondo-territ 'de  'US 'short)   => "USA"
(mondo-territ "de" "US" "short") => "USA"

;;;; Default names
(mondo-territ 'agq 'CD) => "Dɛ̀mùkàlatì Lèkpubèlè è Kuŋgù"
(mondo-territ 'am  'CF) => "ማዕከላዊ አፍሪካ ሪፑብሊክ"
(mondo-territ 'bg  'AS) => "Американска Самоа"
(mondo-territ 'br  'DE) => "Alamagn"
(mondo-territ 'chr 'US) => "ᏌᏊ ᎢᏳᎾᎵᏍᏔᏅ ᏍᎦᏚᎩ"
(mondo-territ 'cs  'CF) => "Středoafrická republika"
(mondo-territ 'da  'EU) => "Den Europæiske Union"
(mondo-territ 'de  'CX) => "Weihnachtsinsel"
(mondo-territ 'dz  'EU) => "ཡུ་རོབ་གཅིག་བསྡོམས་ཚོགས་པ"
(mondo-territ 'el  'GE) => "Γεωργία"
(mondo-territ 'en  'CF) => "Central African Republic"
(mondo-territ 'eo  'VG) => "Britaj Virgulininsuloj"
(mondo-territ 'et  'DE) => "Saksamaa"
(mondo-territ 'eu  'DO) => "Dominikar Errepublika"
(mondo-territ 'eu  'EU) => "Europar Batasuna"
(mondo-territ 'fi  'CF) => "Keski-Afrikan tasavalta"
(mondo-territ 'fil 'KR) => "Timog Korea"
(mondo-territ 'fo  'FO) => "Føroyar"
(mondo-territ 'fo  'VI) => "Sambandsríki Amerikas Jomfrúoyggjar"
(mondo-territ 'fr  'TF) => "Terres australes françaises"
(mondo-territ 'ga  'IM) => "Oileán Mhanann"
(mondo-territ 'gd  'IM) => "Eilean Mhanainn"
(mondo-territ 'gv  'IM) => "Ellan Vannin"
(mondo-territ 'he  'SK) => "סלובקיה"
(mondo-territ 'hi  'AU) => "ऑस्ट्रेलिया"
(mondo-territ 'hu  'TF) => "Francia Déli Területek"
(mondo-territ 'hy  'US) => "Միացյալ Նահանգներ"
(mondo-territ 'id  'VG) => "Kepulauan Virgin Britania Raya"
(mondo-territ 'ii  'CN) => "ꍏꇩ"
(mondo-territ 'is  'CF) => "Mið-Afríkulýðveldið"
(mondo-territ 'ja  'MG) => "マダガスカル"
(mondo-territ 'ka  'GR) => "საბერძნეთი"
(mondo-territ 'kl  'GL) => "Kalaallit Nunaat"
(mondo-territ 'ko  'VN) => "베트남"
(mondo-territ 'lo  'GS) => "ໝູ່ເກາະ ຈໍເຈຍຕອນໃຕ້ ແລະ ແຊນວິດຕອນໃຕ້"
(mondo-territ 'mg  'CF) => "Repoblika Ivon’Afrika"
(mondo-territ 'mk  'MK) => "Северна Македонија"
(mondo-territ 'ml  'IN) => "ഇന്ത്യ"
(mondo-territ 'mn  'CF) => "Төв Африкийн Бүгд Найрамдах Улс"
(mondo-territ 'mt  'VG) => "il-Gżejjer Verġni Brittaniċi"
(mondo-territ 'my  'VI) => "ယူအက်စ် ဗာဂျင်း ကျွန်းစု"
(mondo-territ 'nl  'CF) => "Centraal-Afrikaanse Republiek"
(mondo-territ 'nn  'VI) => "Dei amerikanske Jomfruøyane"
(mondo-territ 'or  'IE) => "ଆୟରଲ୍ୟାଣ୍ଡ"
(mondo-territ 'pl  'UM) => "Dalekie Wyspy Mniejsze Stanów Zjednoczonych"
(mondo-territ 'pt  'CF) => "República Centro-Africana"
(mondo-territ 'ro  'TF) => "Teritoriile Australe și Antarctice Franceze"
(mondo-territ 'ru  'FM) => "Федеративные Штаты Микронезии"
(mondo-territ 'si  'LK) => "ශ්‍රී ලංකාව"
(mondo-territ 'sq  'GS) => "Xhorxha Jugore dhe Ishujt Senduiçë të Jugut"
(mondo-territ 'sr  'RS) => "Србија"
(mondo-territ 'sv  'VC) => "S:t Vincent och Grenadinerna"
(mondo-territ 'sw  'GB) => "Ufalme wa Muungano"
(mondo-territ 'ta  'VC) => "செயின்ட் வின்சென்ட் & கிரெனடைன்ஸ்"
(mondo-territ 'th  'GB) => "สหราชอาณาจักร"
(mondo-territ 'tr  'US) => "Amerika Birleşik Devletleri"
(mondo-territ 'uk  'SH) => "Острів Святої Єлени"
(mondo-territ 'vai 'LR) => "ꕞꔤꔫꕩ"
(mondo-territ 'vai 'ZA) => "ꕉꔱꔸꕪ ꗛꔤ ꔒꘋꗣ ꗏ ꕸꖃꔀ"
(mondo-territ 'vi  'US) => "Hoa Kỳ"
(mondo-territ 'wo  'ZA) => "Afrik di Sid"
(mondo-territ 'yo  'ZA) => "Gúúṣù Áfíríkà"
(mondo-territ 'zh  'GB) => "英国"
(mondo-territ 'ff_Adlm 'BF) => "𞤄𞤵𞤪𞤳𞤭𞤲𞤢 𞤊𞤢𞤧𞤮𞥅"
(mondo-territ 'ff_Adlm 'ML) => "𞤃𞤢𞥄𞤤𞤭"

;;;; When not found, either fall back or fail gracefully
(mondo-territ 'en 'FR 'foo) => "France"  ; ignore 'foo
(mondo-territ 'en 'FU)      => ""
(mondo-territ 'fu 'GB)      => ""
(mondo-territ 'fu 'FU)      => ""

;;;; Language varieties fall back to parents when not found
(mondo-territ 'fr_CA 'MP) => "Mariannes du Nord"
(mondo-territ 'fr_FR 'MP) => "Îles Mariannes du Nord"  ; fall back to 'fr
(mondo-territ 'fr    'MP) => "Îles Mariannes du Nord"
(mondo-territ 'zh_Hant_HK 'JP) => "日本" ;| the 3 fall back to 'zh
(mondo-territ 'zh_Hant_TW 'JP) => "日本" ;|
(mondo-territ 'zh_Hant    'JP) => "日本" ;|
(mondo-territ 'zh         'JP) => "日本" ;
(mondo-territ 'zh_Hant_HK 'EC) => "厄瓜多爾"
(mondo-territ 'zh_Hant_TW 'EC) => "厄瓜多"  ; fall back to 'zh_Hant
(mondo-territ 'zh_Hant    'EC) => "厄瓜多"
(mondo-territ 'zh         'EC) => "厄瓜多尔"
(mondo-territ 'zh_Hant_HK 'CI) => "科特迪瓦"
(mondo-territ 'zh_Hant_TW 'CI) => "象牙海岸" ; fall back to 'zh_Hant
(mondo-territ 'zh_Hant    'CI) => "象牙海岸"
(mondo-territ 'zh         'CI) => "科特迪瓦"

mondo-subdiv (lang query &optional alt)

Name of the country subdivision QUERY in language LANG.

LANG is a language code. QUERY is a country subdivision code.
Optional arg ALT looks for an alternative name, if available.

Source data's keys are dehyphenized and downcased ISO 3166-2 codes.
Yet you may also pass QUERY in standard format. So either of these:

  • 'CA-QC (standard ISO 3166-2 code)
  • 'caqc (as stored in source data)

will give you "Quebec".

;;;; Arguments can be passed as either symbol or string (same result)
(mondo-subdiv 'sr_Latn  'usnc)  => "Severna Karolina"
(mondo-subdiv "sr_Latn" 'usnc)  => "Severna Karolina"
(mondo-subdiv 'sr_Latn  "usnc") => "Severna Karolina"
(mondo-subdiv "sr_Latn" "usnc") => "Severna Karolina"

;;;; Source data's keys are dehyphenized and downcased ISO 3166-2 codes
;;;; Yet you may use standard ISO 3166-2 format instead (same result)
(mondo-subdiv 'sr 'usnc)   => "Северна Каролина"
(mondo-subdiv 'sr 'US-NC)  => "Северна Каролина"
(mondo-subdiv 'bg 'bevov)  => "Източна Фландрия"
(mondo-subdiv 'bg 'BE-VOV) => "Източна Фландрия"
(mondo-subdiv 'ca 'ad07)   => "Andorra la Vella"
(mondo-subdiv 'ca 'AD-07)  => "Andorra la Vella"
(format "Toto, I've a feeling we're not in %s anymore."
        (mondo-subdiv 'en "US-KS"))
=> "Toto, I've a feeling we're not in Kansas anymore."

;;;; Default names
;;;;; 🇺🇸 South Carolina (🏴󠁵󠁳󠁳󠁣󠁿) in several languages
(mondo-subdiv 'af  'ussc) => "Suid-Carolina"
(mondo-subdiv 'am  'ussc) => "ሳውዝ ካሮላይና"
(mondo-subdiv 'ar  'ussc) => "كارولاينا الجنوبية"
(mondo-subdiv 'az  'ussc) => "Cənubi Karolina"
(mondo-subdiv 'be  'ussc) => "Штат Паўднёвая Караліна"
(mondo-subdiv 'bg  'ussc) => "Южна Каролина"
(mondo-subdiv 'bn  'ussc) => "সাউথ ক্যারোলাইনা"
(mondo-subdiv 'bs  'ussc) => "Južna Karolina"
(mondo-subdiv 'ca  'ussc) => "Carolina del Sud"
(mondo-subdiv 'cs  'ussc) => "Jižní Karolína"
(mondo-subdiv 'cy  'ussc) => "De Carolina"
(mondo-subdiv 'el  'ussc) => "Νότια Καρολίνα"
(mondo-subdiv 'en  'ussc) => "South Carolina"
(mondo-subdiv 'es  'ussc) => "Carolina del Sur"
(mondo-subdiv 'et  'ussc) => "Lõuna-Carolina"
(mondo-subdiv 'eu  'ussc) => "Hego Carolina"
(mondo-subdiv 'fa  'ussc) => "کارولینای جنوبی"
(mondo-subdiv 'fi  'ussc) => "Etelä-Carolina"
(mondo-subdiv 'fr  'ussc) => "Caroline du Sud"
(mondo-subdiv 'ga  'ussc) => "Carolina Theas"
(mondo-subdiv 'gl  'ussc) => "Carolina do Sur"
(mondo-subdiv 'gu  'ussc) => "સાઉથ કેરોલિના"
(mondo-subdiv 'he  'ussc) => "קרוליינה הדרומית"
(mondo-subdiv 'hi  'ussc) => "दक्षिणी केरोलाइना"
(mondo-subdiv 'hr  'ussc) => "Južna Karolina"
(mondo-subdiv 'hu  'ussc) => "Dél-Karolina"
(mondo-subdiv 'hy  'ussc) => "Հարավային Կարոլինա"
(mondo-subdiv 'id  'ussc) => "Carolina Selatan"
(mondo-subdiv 'ig  'ussc) => "Nleda anyanwu Kàròlina"
(mondo-subdiv 'is  'ussc) => "Suður-Karólína"
(mondo-subdiv 'it  'ussc) => "Carolina del Sud"
(mondo-subdiv 'ja  'ussc) => "サウスカロライナ州"
(mondo-subdiv 'ka  'ussc) => "სამხრეთი კაროლინა"
(mondo-subdiv 'kk  'ussc) => "Оңтүстік Каролина"
(mondo-subdiv 'kn  'ussc) => "ದಕ್ಷಿಣ ಕೆರೊಲಿನಾ"
(mondo-subdiv 'ko  'ussc) => "사우스캐롤라이나 주"
(mondo-subdiv 'lt  'ussc) => "Pietų Karolina"
(mondo-subdiv 'lv  'ussc) => "Dienvidkarolīna"
(mondo-subdiv 'mk  'ussc) => "Јужна Каролина"
(mondo-subdiv 'ml  'ussc) => "തെക്കൻ കരൊലൈന"
(mondo-subdiv 'mr  'ussc) => "साउथ कॅरोलिना"
(mondo-subdiv 'ms  'ussc) => "Carolina Selatan"
(mondo-subdiv 'my  'ussc) => "တောင်ကယ်ရိုလိုင်းနားပြည်နယ်"
(mondo-subdiv 'ne  'ussc) => "साउथ क्यारोलाइना"
(mondo-subdiv 'no  'ussc) => "Sør-Carolina"
(mondo-subdiv 'pa  'ussc) => "ਦੱਖਣੀ ਕੈਰੋਲੀਨਾ"
(mondo-subdiv 'pl  'ussc) => "Karolina Południowa"
(mondo-subdiv 'pt  'ussc) => "Carolina do Sul"
(mondo-subdiv 'ro  'ussc) => "Carolina de Sud"
(mondo-subdiv 'ru  'ussc) => "Южная Каролина"
(mondo-subdiv 'si  'ussc) => "දකුණු කැරොලිනා"
(mondo-subdiv 'sk  'ussc) => "Južná Karolína"
(mondo-subdiv 'sl  'ussc) => "Južna Karolina"
(mondo-subdiv 'sr  'ussc) => "Јужна Каролина"
(mondo-subdiv 'ta  'ussc) => "தென் கரொலைனா"
(mondo-subdiv 'te  'ussc) => "దక్షిణ కరొలినా"
(mondo-subdiv 'th  'ussc) => "รัฐเซาท์แคโรไลนา"
(mondo-subdiv 'tr  'ussc) => "Güney Karolina"
(mondo-subdiv 'ru  'ussc) => "Южная Каролина"
(mondo-subdiv 'uk  'ussc) => "Південна Кароліна"
(mondo-subdiv 'yue 'ussc) => "南卡羅萊納州"
(mondo-subdiv 'ur  'ussc) => "جنوبی کیرولینا"
(mondo-subdiv 'uz  'ussc) => "Janubiy Karolina"
(mondo-subdiv 'vi  'ussc) => "Nam Carolina"
(mondo-subdiv 'yo  'ussc) => "Gúúsù Carolina"
(mondo-subdiv 'zh  'ussc) => "南卡罗来纳州"

;;;;; 🇫🇷 Provence-Alpes-Côte d’Azur in several languages
(mondo-subdiv 'ar  'frpac) => "بروفنس ألب كوت دازور"
(mondo-subdiv 'az  'frpac) => "Provans-Alp-Kot d’Azur"
(mondo-subdiv 'be  'frpac) => "Праванс-Альпы-Лазурны бераг"
(mondo-subdiv 'bg  'frpac) => "Прованс-Алпи-Лазурен бряг"
(mondo-subdiv 'bn  'frpac) => "প্রোভঁস-আল্প-কোত দাজ্যুর"
(mondo-subdiv 'ca  'frpac) => "Provença – Alps – Costa Blava"
(mondo-subdiv 'el  'frpac) => "Προβηγκία-Άλπεις-Κυανή Ακτή"
(mondo-subdiv 'en  'frpac) => "Provence-Alpes-Côte-d’Azur"
(mondo-subdiv 'es  'frpac) => "Provenza-Alpes-Costa Azul"
(mondo-subdiv 'fa  'frpac) => "پروانس آلپ‌-کوت دازور"
(mondo-subdiv 'fr  'frpac) => "Provence-Alpes-Côte d’Azur"
(mondo-subdiv 'he  'frpac) => "פרובאנס-אלפ-קוט ד׳אזור"
(mondo-subdiv 'hi  'frpac) => "प्रोवेंस-एल्पस-कोट डी‘आज़ुर"
(mondo-subdiv 'hr  'frpac) => "Provansa-Alpe-Azurna obala"
(mondo-subdiv 'hy  'frpac) => "Պրովանս-Ալպեր-Լազուր ափ"
(mondo-subdiv 'it  'frpac) => "Provenza-Alpi-Costa Azzurra"
(mondo-subdiv 'ka  'frpac) => "პროვანსი-ალპები-ლაჟვარდოვანი ნაპირი"
(mondo-subdiv 'ko  'frpac) => "프로방스알프코트다쥐르"
(mondo-subdiv 'lt  'frpac) => "Provansas-Alpės-Žydrasis Krantas"
(mondo-subdiv 'lv  'frpac) => "Provansa-Alpi-Azūra Krasts"
(mondo-subdiv 'mk  'frpac) => "Прованса-Алпи-Азурен Брег"
(mondo-subdiv 'mr  'frpac) => "प्रोव्हाँस-आल्प-कोत देझ्युर"
(mondo-subdiv 'pa  'frpac) => "ਪ੍ਰੋਵਾਂਸ-ਆਲਪ-ਅਸਮਾਨੀ ਤਟ"
(mondo-subdiv 'pl  'frpac) => "Prowansja-Alpy-Lazurowe Wybrzeże"
(mondo-subdiv 'pt  'frpac) => "Provença-Alpes-Costa Azul"
(mondo-subdiv 'ro  'frpac) => "Provența-Alpi-Coasta de Azur"
(mondo-subdiv 'ru  'frpac) => "Прованс — Альпы — Лазурный Берег"
(mondo-subdiv 'sl  'frpac) => "Provansa-Alpe-Azurna obala"
(mondo-subdiv 'sr  'frpac) => "Прованса-Алпи-Азурна обала"
(mondo-subdiv 'th  'frpac) => "แคว้นพรอว็องซาลป์-โกตดาซูร์"
(mondo-subdiv 'uk  'frpac) => "Прованс-Альпи-Лазурний берег"
(mondo-subdiv 'ur  'frpac) => "پروانس-آلپ-کوت دازور"
(mondo-subdiv 'yue 'frpac) => "普羅旺斯-阿爾卑斯-蔚藍海岸"
(mondo-subdiv 'zh  'frpac) => "普罗旺斯-阿尔卑斯-蓝色海岸"

;;;; When not found, either fall back or fail gracefully
(mondo-subdiv 'en 'ussc 'foo) => "South Carolina"  ; ignore 'foo
(mondo-subdiv 'en 'usfu)      => ""
(mondo-subdiv 'fu 'ussc)      => ""
(mondo-subdiv 'fu 'usfu)      => ""

;;;; Language varieties fall back to parents when not found
(mondo-subdiv 'de_CH 'char) => "Appenzell Ausserrhoden"
(mondo-subdiv 'de_JP 'char) => "Appenzell Außerrhoden"  ; fall back to 'de
(mondo-subdiv 'de    'char) => "Appenzell Außerrhoden"
(mondo-subdiv 'de_CH 'chzh) => "Zürich"  ;| fall back to 'de
(mondo-subdiv 'de_JP 'chzh) => "Zürich"  ;|
(mondo-subdiv 'de    'chzh) => "Zürich"

Output a hash table

Functions to output a hash table of all scripts, languages, territories, or
subdivisions. Any such hash table is structured as follows:

(h* KEY1 (h* "default" VALUE1)
    KEY2 (h* "default" VALUE2)
    KEY3 (h* "default" VALUE3a
             "variant" VALUE3b)
    ....     ......... ......
    KEYn (h* "default" VALUEn))

where keys and values are strings.

mondo-script-ht (lang)

Output hash table of all scripts in language LANG.

;;;; Yep, it's a hash table
(h? (mondo-script-ht 'en)) => t

;;;; We can use xht's h-let to bind its values
(h-let (mondo-script-ht 'fr)
  (format "J'ai trouvé des %s." .Maya.default))
=> "J'ai trouvé des hiéroglyphes mayas."

(h-let (mondo-script-ht 'en)
  (format "%s is the code for %s (%s)."
          'Cans  .Cans.default  .Cans.short))
=> "Cans is the code for Unified Canadian Aboriginal Syllabics (UCAS)."

mondo-langue-ht (lang)

Output hash table of all languages in language LANG.

;;;; Yep, it's a hash table
(h? (mondo-langue-ht 'en)) => t

;;;; We can use xht's h-let to bind its values
(h-let (mondo-langue-ht 'en)
  (format "They speak fluent %s, %s, and %s."
          .tr.default   .zh.long   .az.short))
=> "They speak fluent Turkish, Mandarin Chinese, and Azeri."

;;;; When using only default values, we can also do this
(h-let (h--hmap key (h-get value "default") (mondo-langue-ht 'fr))
  (format "Elle parle %s, %s et %s." .ja .jv .qug))
=> "Elle parle japonais, javanais et quichua du Haut-Chimborazo."

mondo-territ-ht (lang)

Output hash table of all territories in language LANG.

;;;; Yep, it's a hash table
(h? (mondo-territ-ht 'en)) => t

;;;; We can use xht's h-let to bind its values
(h-let (mondo-territ-ht 'en)
  (format "She's just been to %s, %s, and %s."
          .BA.short  .CI.variant  .SB.default))
=> "She's just been to Bosnia, Ivory Coast, and Solomon Islands."

;;;; When only using default values, we can also do this
(h-let (h--hmap key (h-get value "default") (mondo-territ-ht 'de))
  (format "%s, %s und %s gehören zum %s." .BA .HR .RS \.039))
=> "Bosnien und Herzegowina, Kroatien und Serbien gehören zum Südeuropa."

mondo-subdiv-ht (lang)

Output hash table of all subdivisions in language LANG.

  ;;;; Yep, it's a hash table
  (h? (mondo-subdiv-ht 'en)) => t

  ;;;; We can use xht's h-let to bind its values
  (h-let (mondo-subdiv-ht 'en)
    (format "%s consumes a lot of %s sauce."
            .svss.default   .gbwor.default))
  => "San Salvador consumes a lot of Worcestershire sauce."

  ;;;; Subdivisions seem to have only "default" values.
  ;;;; We can then always flatten the hash table to shorten the arguments.
  (h-let (h--hmap key (h-get value "default") (mondo-subdiv-ht 'en))
    (format "%s, %s, %s, and %s\nare the four constituent countries of the %s."
            .gbeng  .gbnir  .gbsct  .gbwls  (mondo-territ 'en 'GB)))
  => "\
England, Northern Ireland, Scotland, and Wales
are the four constituent countries of the United Kingdom."

  ;;;; Naturally, we can filter these hash tables as we wish
  ;;;;; For our example, let's pick a place with very few subdivisions
  (->> (mondo-subdiv-ht 'en)
       (h--hmap (when (string= "sg" (substring key 0 2)) key)
                (h-get value "default")))
  H=> (h* "sg01" "Central Singapore"
          "sg02" "North East"
          "sg03" "North West"
          "sg04" "South East"
          "sg05" "South West")

  ;;;;; Now let's have these keys as symbols and add a second language
  (->> (mondo-subdiv-ht 'en)
       (h--hmap
        (when (string= "sg" (substring key 0 2)) (h-as-symbol key))
        (format "%s — %s" (mondo-subdiv 'zh key) (h-get value "default"))))
  H=> (h* 'sg01 "中区社区发展理事会 — Central Singapore"
          'sg02 "东北社区发展理事会 — North East"
          'sg03 "西北社区发展理事会 — North West"
          'sg04 "东南社区发展理事会 — South East"
          'sg05 "西南社区发展理事会 — South West")

  ;;;;; We could also do the above like this
  (->> (mondo-subdiv-ht 'en)
       (h--sel  (string= "sg" (substring key 0 2)))
       (h--hmap (h-as-symbol key)
                (format "%s — %s"
                        (mondo-subdiv 'zh key)
                        (mondo-subdiv 'en key))))
  H=> (h* 'sg01 "中区社区发展理事会 — Central Singapore"
          'sg02 "东北社区发展理事会 — North East"
          'sg03 "西北社区发展理事会 — North West"
          'sg04 "东南社区发展理事会 — South East"
          'sg05 "西南社区发展理事会 — South West")

  ;;;;; Yet another way — and let's now add a third language
  (->> (mondo-subdiv-ht 'en)
       (h--sel  (string= "sg" (substring key 0 2)))
       (h--hmap (h-as-symbol key)
                (--tree-mapreduce (mondo-subdiv it key)
                                  (concat it " · " acc) '(zh en fr))))
  H=> (h* 'sg01 "中区社区发展理事会 · Central Singapore · Singapour central"
          'sg02 "东北社区发展理事会 · North East · District du Nord-Est"
          'sg03 "西北社区发展理事会 · North West · District du Nord-Ouest"
          'sg04 "东南社区发展理事会 · South East · District du Sud-Est"
          'sg05 "西南社区发展理事会 · South West · District du Sud-Ouest")

  ;;;;; But what if you wanted to output (only) those values, as a string?
  (-as-> (mondo-subdiv-ht 'en) htbl
         (h--sel (string= "sg" (substring key 0 2)) htbl)
         (with-output-to-string
           (h--each htbl
             (princ (--tree-mapreduce (mondo-subdiv it key)
                                      (concat it " · " acc) '(zh en fr)))
             (terpri))))
  => "\
中区社区发展理事会 · Central Singapore · Singapour central
东北社区发展理事会 · North East · District du Nord-Est
西北社区发展理事会 · North West · District du Nord-Ouest
东南社区发展理事会 · South East · District du Sud-Est
西南社区发展理事会 · South West · District du Sud-Ouest
"

Commands

Translate a name

Interactively translate a query.

Autodetect query type: script, language, territory, or subdivision code.

Ask for language to translate that query into.

Query will be either the contents of a selected region, or whatever you
entered in the minibuffer. In the former case, the region will be replaced.

mondo-dwim ()

Ask for query or use selection. Ask language. Return name of query in it.

mondo-region ()

Replace selection (a query) with its name in a language of your choice.

mondo-ask ()

Ask for query and a language. Return name of query in that language.

Display in a buffer

Commands to show in a separate buffer, in a language of your choice, a
table with the names of scripts, languages, territories, or subdivisions.

mondo-script-buffer ()

Display, in a language of your choice, the names of all scripts.

mondo-langue-buffer ()

Display, in a language of your choice, the names of all languages.

mondo-territ-buffer ()

Display, in a language of your choice, the names of all territories.

mondo-subdiv-buffer ()

Display, in a language of your choice, the names of all subdivisions.

See README

Commands to open mondo's README.org. Optionally, find things in it.

mondo-see-readme (&optional heading narrow)

Open mondo's README.org file.

Search for the file in mondo.el's directory.

If found, open it read-only.

If optional argument HEADING is passed, try to navigate to the
heading after opening it. HEADING should be a string.

If optional argument NARROW is non-nil, narrow to that heading.
This argument has no effect if HEADING is nil or not found.

mondo-see-news ()

See the News in mondo's README.org file.

Contributing

See my page Software for information about how to contribute to any of my Emacs packages.

News

0.2.0

Release

See also

Other packages

You can integrate mondo's examples into Help or Helpful buffers with Democratize, which also offers examples from xht, dash, s, f, and native Emacs libraries.

You may also be interested in Vexil, with which you can produce flags of countries and subdivisions.

License

This project follows the REUSE Specification (FAQ), which in turn is built upon SPDX.

Therefore, license and copyright information can be found in:

  • each file's comment header, or
  • an adjacent file of the same name with the additional extension .license, or
  • the .reuse/dep5 file

The full text of the licenses can be found in the LICENSES subdirectory.


mondo.el

Structure

;;; mondo.el --- World's scripts, languages, places in any language  -*- lexical-binding: t -*-
;;; Commentary:
;;;; For all the details, please do see the README
;;; Code:
;;;; Libraries
;;;; Backports
;;;; Package metadata
;;;; Customizable variables
;;;; Other variables
;;;;; Memoized lookups
;;;;; Regular expressions
;;;; Functions
;;;;; Description macro
;;;;; Translate a name
;;;;; Output a hash table
;;;;; Internal
;;;;;; Translate a name
;;;;;;; Lookups proper
;;;;;;; Find parent locales
;;;;;;; String modifications
;;;;;; Data maps
;;;;;;; Hash tables
;;;;;;;; Normalization
;;;;;;; Alists
;;;;;;;; Read XML file and convert to alist
;;;; Commands
;;;;; Translate a name
;;;;; Display in a buffer
;;;;; Internal
;;;;;; Translate a name
;;;;;; Display in a buffer
;;;;;; Choose a language using minibuffer
;;;;; See README
;;;;;; Internal
;;;; Wrapping up
;;; mondo.el ends here

Contents

;;; mondo.el --- World's scripts, languages, places in any language  -*- lexical-binding: t -*-

;; SPDX-FileCopyrightText: © flandrew <https://flandrew.srht.site/listful>

;;---------------------------------------------------------------------------
;; Author:    flandrew
;; Created:   2025-05-25
;; Updated:   2025-09-08
;; Keywords:  extensions, lisp
;; Homepage:  <https://flandrew.srht.site/listful/software.html>
;;---------------------------------------------------------------------------
;; Package-Version:  0.2.0
;; Package-Requires: ((emacs "25.1") (xht "2.0") (dash "2.14"))
;;---------------------------------------------------------------------------

;; SPDX-License-Identifier: GPL-3.0-or-later

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation, either version 3 of the License,
;; or (at your option) any later version.
;;
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this file. If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Mondo can translate into (almost) every language the names of the world's
;; scripts, languages, territories (including countries), and countries'
;; subdivisions.
;;
;; Translation happens locally, in your machine.
;;
;; For that, you'll need data from Unicode Consortium's CLDR:
;;   https://en.wikipedia.org/wiki/Common_Locale_Data_Repository
;;
;; That data is available for you to download free of charge, and is licensed
;; under the free and open-source Unicode-3.0 license.
;;
;;;; For all the details, please do see the README
;;
;; Open it easily with:
;;   (find-file-read-only "README.org")   <--- C-x C-e here¹
;;
;; or from any buffer:
;;   M-x mondo-see-readme
;;
;; or read it online:
;;   <https://flandrew.srht.site/listful/sw-emacs-mondo.html>
;;
;; ¹ or the key that ‘eval-last-sexp’ is bound to, if not C-x C-e.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;;; Code:
;;;; Libraries

(require 'xml)
(require 'xht)       ; <--also by the author of this package
(require 'dash)
(require 'pcase)
(require 'lisp-mnt)  ; lm-summary’, ‘lm-homepage’, ‘lm-version’, ‘lm-header


;;;; Backports

(eval-and-compile
  (defalias 'mondo--with-memoization
    (if (fboundp 'with-memoization)
        'with-memoization
      (cons 'macro
            (lambda (place &rest code)
              (gv-letplace (getter setter) place
                `(or ,getter
                     ,(macroexp-let2 nil val (macroexp-progn code)
                        `(progn
                           ,(funcall setter val)
                           ,val)))))))))


;;;; Package metadata

(defvar mondo--name "Mondo")

(defvar mondo--dot-el
  (format "%s.el" (file-name-sans-extension (eval-and-compile
                                              (or load-file-name
                                                  buffer-file-name)))))
(defvar mondo--readme-org
  (expand-file-name "README.org" (file-name-directory mondo--dot-el)))

(defvar mondo--summary  (lm-summary   mondo--dot-el))
(defvar mondo--homepage (lm-homepage  mondo--dot-el))
(defvar mondo--version  (lm-with-file mondo--dot-el
                          (or (lm-header "package-version")
                              (lm-version))))


;;;; Customizable variables

(defgroup mondo nil
  (format "%s." mondo--summary)
  :group 'extensions
  :group 'lisp
  :link  '(emacs-library-link :tag "Lisp file" "mondo.el")
  :link  `(file-link :tag "README.org" ,mondo--readme-org)
  :link  `(url-link  :tag "Homepage"   ,mondo--homepage))

(defun mondo--cldr-common-path-guess ()
  "Search some paths where CLDR's common subdir could be."
  (pcase system-type
    ('gnu/linux (-first #'file-directory-p
                        '("/usr/local/share/unicode/cldr/common"
                          "/usr/share/unicode/cldr/common")))))

(defcustom mondo-cldr-common-path
  (or (mondo--cldr-common-path-guess)
      "Unknown: add path once you have CLDR data")
  "Path to CLDR's common subdir."
  :package-version '(mondo "0.2.0")
  :type 'string)

(defcustom mondo-title-propertize t
  "Whether to propertize the title of display buffers."
  :package-version '(mondo "0.2.0")
  :type 'boolean)

(defcustom mondo-title-color "dark turquoise"
  "The color with which to propertize display buffers' title.
Has no effect if ‘mondo-title-propertize’ is nil."
  :package-version '(mondo "0.2.0")
  :type 'string)


;;;; Other variables
;;;;; Memoized lookups

(defvar mondo--lang-parents-ht
  (h* 'parents nil)
  "A hash table to memoize parent locales.")

(defvar mondo--keys-ht
  (h* 'scripts (h*) 'languages (h*) 'territories (h*) 'subdivisions (h*))
  "A hash table to memoize key lookups.")


;;;;; Regular expressions

(defvar mondo--lansub-code-re
  (rx bos (= 3 (in "a-z")) eos)
  "Regular expression matching ambiguous code: language or subdivision.")

(defvar mondo--script-code-re
  (rx bos (= 1 (in "A-Z")) (= 3 (in "a-z")) eos)
  "Regular expression matching script code.")

(defvar mondo--langue-code-re
  (rx bos (** 2 3 (in "a-z")) (? (: "_" (* graph))) eos)
  "Regular expression matching language code.")

(defvar mondo--territ-code-re
  (rx bos (or (= 2 (in "A-Z")) (= 3 (in "0-9"))) eos)
  "Regular expression matching territory code.")

(defvar mondo--subdiv-code-re
  (rx bos (or (: (= 2 (in "a-z"))     (** 1 3 (in "0-9a-z")))
              (: (= 2 (in "A-Z")) "-" (** 1 3 (in "0-9A-Z"))))
      eos)
  "Regular expression matching subdivision code.")


;;;; Functions
;;;;; Description macro

(defmacro mondo--describe (str)
  "Describe with string STR the contents under this heading.
Think of it as a docstring for the headings of your elisp files.
For the full docstring, look for ‘orgreadme-fy-describe’ in the
package ‘orgreadme-fy’."
  (declare (indent 0))
  (unless (stringp str)
    (user-error "‘mondo--describe’ must receive a string")))


;;;;; Translate a name

(mondo--describe
  "Functions to translate to any language the name of a script, language,
territory, or subdivision. Territories, here, are not only countries,
but also regions such as North America or Middle Africa. Subdivisions
apply to each country.

Function arguments can be strings or symbols — the result is the same.")

(defun mondo-script (lang query &optional alt)
  "Name of the script QUERY in language LANG.
LANG is a language code. QUERY is a script code.
Optional arg ALT looks for an alternative name, if available."
  (mondo--ldn-key 'scripts lang query alt))

(defun mondo-langue (lang query &optional alt)
  "Name of the language QUERY in language LANG.
LANG and QUERY are both language codes.
Optional arg ALT looks for an alternative name, if available."
  (mondo--ldn-key 'languages lang query alt))

(defun mondo-territ (lang query &optional alt)
  "Name of the territory QUERY in language LANG.
LANG is a language code. QUERY is a territory code.
Optional arg ALT looks for an alternative name, if available."
  (mondo--ldn-key 'territories lang query alt))

(defun mondo-subdiv (lang query &optional alt)
  "Name of the country subdivision QUERY in language LANG.
LANG is a language code. QUERY is a country subdivision code.
Optional arg ALT looks for an alternative name, if available.

Source data's keys are dehyphenized and downcased ISO 3166-2 codes.
Yet you may also pass QUERY in standard format. So either of these:
- \\='CA-QC (standard ISO 3166-2 code)
- \\='caqc  (as stored in source data)
will give you \"Quebec\"."
  (--> (mondo--strip-hyphen-downcase query)
       (mondo--ldn-key 'subdivisions lang it alt)))


;;;;; Output a hash table

(mondo--describe
  "Functions to output a hash table of all scripts, languages, territories, or
subdivisions. Any such hash table is structured as follows:

#+begin_src emacs-lisp
  (h* KEY1 (h* \"default\" VALUE1)
      KEY2 (h* \"default\" VALUE2)
      KEY3 (h* \"default\" VALUE3a
               \"variant\" VALUE3b)
      ....     ......... ......
      KEYn (h* \"default\" VALUEn))
#+end_src

where keys and values are strings.")

(defun mondo-script-ht (lang)
  "Output hash table of all scripts in language LANG."
  (mondo--ldn-key-ht 'scripts lang))

(defun mondo-langue-ht (lang)
  "Output hash table of all languages in language LANG."
  (mondo--ldn-key-ht 'languages lang))

(defun mondo-territ-ht (lang)
  "Output hash table of all territories in language LANG."
  (mondo--ldn-key-ht 'territories lang))

(defun mondo-subdiv-ht (lang)
  "Output hash table of all subdivisions in language LANG."
  (mondo--ldn-key-ht 'subdivisions lang))


;;;;; Internal
;;;;;; Translate a name
;;;;;;; Lookups proper

;; KEY, found below frequently in functions' arguments, is a choice between
;; one of these symbols: '(scripts languages territories subdivisions).

(defun mondo--ldn-key (key lang query &optional alt)
  "Given SDIR, return name of QUERY (a KEY) in LANG.
If ALT is non-nil, try alternative name.
If not found and LANG has a parent, recursively try parents.
If still not found, return empty string."
  (let (res)
    (while (and (not res) lang)
      (setq res  (mondo--ldn-key-1 key lang query alt)
            lang (mondo--lang-parent lang)))
    (or res "")))

(defun mondo--ldn-key-1 (key lang query &optional alt)
  "Given SDIR, return name of QUERY (a KEY) in LANG.
If ALT is non-nil, try alternative name.
If not found, return nil."
  (let* ((qrys (format "%s" query))
         (htbl (mondo--ldn-key-ht key lang))
         (ghqh (h-get htbl qrys)))
    (when (h? ghqh)
      (or (when alt (h-get ghqh (format "%s" alt)))
          (h-get ghqh "default")))))


;;;;;;; Find parent locales

(defun mondo--lang-all-parents (lang)
  "List LANG and its chain of parents."
  (let ((l (format "%s" lang)))
    (unless (string= "" l)
      (--unfold (when it (cons it (mondo--lang-parent it)))
                l))))

(defun mondo--lang-parent (lang)
  "LANG's parent."
  (let* ((l (format "%s" lang))
         (p (h-get (mondo--lang-parents-ht) l)))
    (unless (equal p "root")
      (or p (when (string-match-p "_" l)
              (mondo--strip-trailing-subtag l))))))

(defun mondo--lang-parents-ht ()
  "Make hash table that maps languages to their parents.
These are the exceptional cases only."
  (mondo--with-memoization
      (h-get mondo--lang-parents-ht 'parents)
    (let ((htbl (h-new)))
      (--each (mondo--lang-parents-alist)
        (-let [(&alist 'parent 'locales) (cadr it)]
          (dolist (locale (split-string locales))
            (h-put! htbl locale parent))))
      htbl)))

(defun mondo--lang-parents-alist ()
  "Make alist of parent locales."
  (mondo--file-alist "supplemental" "supplementalData"
                     'supplementalData 'parentLocales))


;;;;;;; String modifications

(defun mondo--strip-hyphen-downcase (obj)
  "Stringify, downcase, and dehyphenize OBJ."
  (->> obj   (format "%s")   downcase
       (replace-regexp-in-string "-" "")))

(defun mondo--strip-trailing-subtag (string)
  "Remove trailing subtag, if any, from STRING.
STRING is presumed to be a _-separated language tag."
  (replace-regexp-in-string "_[^_]+\\'" "" string))

(defun mondo--strip-additional-subtags (string)
  "Remove all additional subtags, if any, from STRING.
STRING is presumed to be a _-separated language tag."
  (replace-regexp-in-string "_.*\\'" "" string))


;;;;;; Data maps
;;;;;;; Hash tables

(defun mondo--ldn-key-ht (key lang &optional complete normalized)
  "Given LANG code, return hash table of KEY, maybe COMPLETE.
If COMPLETE is nil use ‘mondo--ldn-key-ht-1
Otherwise, use instead ‘mondo--ldn-key-ht-complete’.
If NORMALIZED is non-nil, pass the result to ‘mondo--ht-normalize’.
Normalization:
  1. makes it a proper 2D hash table, by adding an ID field
  2. fills with empty string the values of missing fields
The hash table is then ready to be converted to strictly 2D formats,
such as org table; tab- or space-separated values; or list of lists."
  (--> (if complete
           (mondo--ldn-key-ht-complete key lang)
         (mondo--ldn-key-ht-1 key lang))
       (if normalized (mondo--ht-normalize it) it)))

(defun mondo--ldn-key-ht-complete (key lang)
  "Given LANG code, return complete hash table of KEY.
Like ‘mondo--ldn-key-ht-1’, but recursively look up missing keys in all
parent locales to complete the hash table. This is done by merging
nested hash tables from the top (parentless) parent language down
its descendants, ending in LANG, thereby sequentially updating it."
  (let* ((langs (mondo--lang-all-parents lang))
         (htbls (--map (mondo--ldn-key-ht-1 key it) langs)))
    (apply #'h-mix* (nreverse htbls))))

(defun mondo--ldn-key-ht-1 (key lang)
  "Given LANG code, return hash table of KEY."
  (mondo--with-memoization
      (h-get (h-get mondo--keys-ht key) (format "%s" lang))
    (let ((htbl (h-new)))
      (--each (mondo--ldn-key-alist key lang)
        (-let* ((((&alist 'type 'alt) name) (cdr it))
                (tag (if alt (format "%s" alt) "default")))
          (h-put*! htbl type tag name)))
      htbl)))


;;;;;;;; Normalization

(defun mondo--ht-normalize (htbl &optional id dv)
  "Normalize 2D hash table HTBL using ID.
Whenever a field is missing, add it, and make the value DV.
If nil, DV defaults to the empty string.
If nil, ID defaults to \"id\"."
  (or id (setq id "id"))
  (or dv (setq dv ""))
  (let* ((fields (mondo--ht-fields htbl))
         (values (make-list (length fields) dv))
         (nvalue (h-zip-lists fields values)))
    (h--hmap key (h-mix* (h* id key) nvalue value) htbl)))

(defun mondo--ht-fields (htbl)
  "Return a list of fields found in 2D hash table HTBL.
That's the union of the keys of all values."
  (let ((vals-ht (h-new)))
    (h--each htbl (--each (h-keys value) (h-put! vals-ht it t)))
    (h-keys vals-ht)))


;;;;;;; Alists

(defun mondo--ldn-key-alist (key lang)
  "Given LANG code and KEY, walk XMLPATH and return alist.
Open corresponding lang file and return alist with the value at the
end of the path \\='localeDisplayNames \\='KEY."
  (mondo--file-alist (mondo--ldn-key-to-sdir key)
                     lang 'ldml 'localeDisplayNames key))

(defun mondo--ldn-key-to-sdir (key)
  "Given KEY, select subdirectory SDIR."
  (pcase key ('subdivisions "subdivisions") (_ "main")))


;;;;;;;; Read XML file and convert to alist

(defun mondo--file-alist (sdir fname &rest xmlpath)
  "Given SDIR and FNAME code, walk XMLPATH and return alist.
SDIR is a subdir of common. FNAME is the name of a file there without
the xml extension. Open that file and return alist with the value at the
end of XMLPATH. If file cannot be found or read, return nil."
  (mondo--check-for-common)
  (let ((file (->> mondo-cldr-common-path
                   (expand-file-name (format "%s" sdir))
                   (expand-file-name (format "%s.xml" fname))))
        (next) (res))
    (when (file-readable-p file)
      (with-temp-buffer
        (insert-file-contents file)
        (->> (delete-and-extract-region 1 (point-max))
             ;; Preliminary cleanup for easier xml parsing
             (replace-regexp-in-string "\n[\t\s]*" "")
             ;; Remove trailing spaces before <!-- deprecated -->
             (replace-regexp-in-string ">[\t\s]+<!--" "><!--")
             (insert))
        (setq res (xml-parse-region nil nil nil 'parse-dtd))
        (while xmlpath
          (setq next (--> (pop xmlpath)
                          (if (symbolp it) it (intern it)))
                res  (alist-get next res)))
        (cdr res)))))

(defun mondo--check-for-common (&optional sdir)
  "Check that ‘mondo-cldr-common-path’ is a valid path.
Alternatively, do that instead for its subdirectory SDIR."
  (let* ((mccp mondo-cldr-common-path)
         (path (if sdir (expand-file-name sdir mccp) mccp)))
    (unless (file-directory-p path)
      (error "%s: invalid CLDR path" path))))


;;;; Commands
;;;;; Translate a name

(mondo--describe
  "Interactively translate a query.

Autodetect query type: script, language, territory, or subdivision code.

Ask for language to translate that query into.

Query will be either the contents of a selected region, or whatever you
entered in the minibuffer. In the former case, the region will be replaced.")

;;;###autoload
(defun mondo-dwim ()
  "Ask for query or use selection. Ask language. Return name of query in it."
  (interactive)  (mondo--convert-dwim))

;;;###autoload
(defun mondo-region ()
  "Replace selection (a query) with its name in a language of your choice."
  (interactive)  (mondo--convert-region))

;;;###autoload
(defun mondo-ask ()
  "Ask for query and a language. Return name of query in that language."
  (interactive)  (mondo--convert-ask))


;;;;; Display in a buffer

(mondo--describe
  "Commands to show in a separate buffer, in a language of your choice, a
table with the names of scripts, languages, territories, or  subdivisions.")

;;;###autoload
(defun mondo-script-buffer ()
  "Display, in a language of your choice, the names of all scripts."
  (interactive)  (mondo--key-buffer 'scripts))

;;;###autoload
(defun mondo-langue-buffer ()
  "Display, in a language of your choice, the names of all languages."
  (interactive)  (mondo--key-buffer 'languages))

;;;###autoload
(defun mondo-territ-buffer ()
  "Display, in a language of your choice, the names of all territories."
  (interactive)  (mondo--key-buffer 'territories))

;;;###autoload
(defun mondo-subdiv-buffer ()
  "Display, in a language of your choice, the names of all subdivisions."
  (interactive)  (mondo--key-buffer 'subdivisions))


;;;;; Internal
;;;;;; Translate a name

(defun mondo--convert-dwim ()
  "Convert selected region. If none, ask."
  (if mark-active (mondo--convert-region) (mondo--convert-ask)))

(defun mondo--convert-region ()
  "Convert selected region."
  (unless mark-active (error "You must first select a region"))
  (save-excursion
    (let* ((p (point)) (m (mark))
           (obj (buffer-substring-no-properties p m))
           (mod (mondo--convert-ask obj)))
      (delete-region p m)
      (insert mod))))

(defun mondo--convert-ask (&optional query)
  "Ask what to convert and show the result."
  (unless query
    (setq query (read-string "Enter code to be translated: ")))
  (let* ((key (mondo--query->key query))
         (fun (mondo--key->fun     key))
         (lan (mondo--choose-lang  key))
         (res (funcall fun lan query)))
    (message "%s" res)))

(defun mondo--key->fun (key)
  "From KEY to conversion function."
  (pcase-exhaustive (h-as-symbol key)
    ('scripts      #'mondo-script)
    ('languages    #'mondo-langue)
    ('territories  #'mondo-territ)
    ('subdivisions #'mondo-subdiv)))

(defun mondo--query->key (query)
  "Guess QUERY type; return key symbol. Interactively disambiguate."
  (-as-> (mondo--query->key-1 query) key
         (if (eq key 'lan-or-sub)
             (mondo--lan-or-sub-p)
           key)))

(defun mondo--query->key-1 (query)
  "Guess QUERY type; return key symbol."
  (let ((case-fold-search nil)
        (q (h-as-string query)))
    (cond ((string-match-p mondo--lansub-code-re q) 'lan-or-sub)
          ((string-match-p mondo--script-code-re q) 'scripts)
          ((string-match-p mondo--langue-code-re q) 'languages)
          ((string-match-p mondo--territ-code-re q) 'territories)
          ((string-match-p mondo--subdiv-code-re q) 'subdivisions)
          (:otherwise (error "Invalid query type: %s" q)))))

(defun mondo--lan-or-sub-p ()
  "Was that a language? Or was it a subdivision?
Only needed when query has exactly three lowercase letters.

For example, the query \\='mga could be intended as either a language
lookup, in which case it'd return Middle Irish; or as a subdivision
lookup, in which case it'd return Toamasina (in Madagascar)."
  (let* ((lis1 '("Language" "Subdivision"))
         (lis2 '(languages subdivisions))
         (alis (-zip-pair lis1 lis2))
         (prmp "Ambiguous query type. Which was intended? ")
         (from  (completing-read prmp lis1)))
    (cdr (assoc from alis))))


;;;;;; Display in a buffer

(defun mondo--key-buffer (key)
  "Display names of every KEY item in a language of your choice."
  (mondo--ldn-key-buffer key (mondo--choose-lang key) 'complete))

(defun mondo--ldn-key-buffer (key lang &optional complete)
  "Return buffer with the names of every KEY item in LANG.
If COMPLETE is non-nil, recursively complete with parent locales."
  (let* ((keystring (capitalize (format "%s" key)))
         (lang-name (mondo--title-lang-name lang))
         (buff-name (format "*World's %s (%s)*" keystring lang))
         (title-str (format  "World's %s — %s (%s)\n\n"
                             keystring lang-name lang)))
    (with-help-window buff-name
      (setq-local truncate-lines t)
      (when (fboundp 'olivetti-mode) (olivetti-mode -1))
      (princ title-str)
      (princ (h->orgtbl (mondo--ldn-key-ht key lang complete 'normalized)))
      (when mondo-title-propertize
        (mondo--title-propertize title-str mondo-title-color)))
    (prog1 nil (pop-to-buffer buff-name) (forward-line))))

(defun mondo--title-propertize (str color)
  "Propertize buffer title."
  (add-face-text-property 1 (length str) `(:foreground ,color :weight bold)))

(defun mondo--title-lang-name (lang)
  "Given LANG, output suitable language name for display buffer title.
If LANG code has a name, use it; otherwise fall back to the topmost
named ascendant."
  (let* ((all (--map (mondo-langue 'en it 'long)
                     (mondo--lang-all-parents lang)))
         (1st (car all)))
    (if (equal 1st "")
        (or (--last (not (equal it "")) all)
            ;; Get name of primary language subtag in irreducible (parentless)
            ;; tags such as "bs_Cyrl" and "sr_Latn"
            (--> (mondo--strip-additional-subtags (format "%s" lang))
                 (mondo-langue 'en it 'long)))
      1st)))


;;;;;; Choose a language using minibuffer

(defun mondo--choose-lang (key)
  "Given KEY, choose language using minibuffer."
  (mondo--choose-lang-from-list
   (mondo--langs-in-sdir
    (mondo--ldn-key-to-sdir key))))

(defun mondo--choose-lang-from-list (list)
  "Choose language from LIST, using minibuffer."
  (let* ((name (--map (mondo-langue 'en it) list))
         (both (--zip-with (format "%-7s  %s" it other) list name))
         (lang (completing-read "Language (you can also type names): " both)))
    (replace-regexp-in-string " .*" "" lang)))

(defun mondo--langs-in-sdir (sdir)
  "List all language codes in subdirectory SDIR.
SDIR is under ‘mondo-cldr-common-path’, and expected to have files
named langcode.xml."
  (mondo--check-for-common sdir)
  (--> mondo-cldr-common-path
       (expand-file-name sdir it)
       (directory-files it nil (rx ".xml" eos))
       (remove "root.xml" it) ; not a language
       (-map #'file-name-sans-extension it)))


;;;;; See README

(mondo--describe
  "Commands to open mondo's README.org. Optionally, find things in it.")

;;;###autoload
(defun mondo-see-readme (&optional heading narrow)
  "Open mondo's README.org file.
Search for the file in mondo.el's directory.

If found, open it read-only.

If optional argument HEADING is passed, try to navigate to the
heading after opening it. HEADING should be a string.

If optional argument NARROW is non-nil, narrow to that heading.
This argument has no effect if HEADING is nil or not found."
  (interactive)
  (let ((readme mondo--readme-org))
    (if (file-exists-p readme)
        (let ((pr (make-progress-reporter
                   (format "Opening %s ... "
                           (abbreviate-file-name readme)))))
          (find-file-read-only readme)
          (when heading
            (mondo--goto-org-heading heading narrow))
          (progress-reporter-done pr))
      (message "Couldn't find %s's README.org" mondo--name))))

;;;###autoload
(defun mondo-see-news ()
  "See the News in mondo's README.org file."
  (interactive)
  (mondo-see-readme "News" 'narrow)
  (mondo--display-org-subtree))


;;;;;; Internal

(defun mondo--display-org-subtree ()
  "Selectively display org subtree."
  (let ((cmds '( outline-hide-subtree outline-show-children
                 outline-next-heading outline-show-branches)))
    (and (equal (mapcar #'fboundp cmds) '(t t t t))
         (mapc #'funcall cmds))))

(defun mondo--goto-org-heading (heading &optional narrow)
  "Navigate to org HEADING and optionally NARROW to it."
  (let* ((hrx (format "^[*]+ %s" heading))
         (pos (save-match-data
                (save-excursion
                  (save-restriction
                    (widen)  (goto-char (point-max))
                    (re-search-backward hrx nil t 1))))))
    (when pos
      (widen)  (goto-char pos)
      (if (and narrow (fboundp 'org-narrow-to-subtree))
          (org-narrow-to-subtree)
        (recenter-top-bottom 1))
      (when (fboundp 'outline-show-subtree)
        (outline-show-subtree))
      (when (fboundp 'org-flag-drawer)
        (save-excursion
          (forward-line 1)
          (org-flag-drawer t))))))


;;;; Wrapping up

(provide 'mondo)

;; Local Variables:
;; coding:                     utf-8
;; indent-tabs-mode:           nil
;; sentence-end-double-space:  nil
;; outline-regexp:             ";;;;* "
;; End:

;;; mondo.el ends here
📆 2025-09-08