Modulo:mtagg

El Vikivortaro
 MODULO
Memtesto disponeblas sur la paĝo Ŝablono:k.
Ĉi tiu modulo estas multfoje bindita.
Se vi konas la eblajn sekvojn, tiam vi povas zorgeme ekredakti.
Se vi ne kuraĝas redakti tiam vi povas proponi la deziratan ŝanĝon en la diskutejo.

--[===[

MODULE "MTAGG" (tag)

"eo.wiktionary.org/wiki/Modulo:mtagg" <!--2023-Aug-xx-->
"id.wiktionary.org/wiki/Modul:mtagg"

Purpose: adds extra information ("tag") to a single meaning of a word
         or the complete lemma (covering all meanings), showed as
         an inline list of coloured hints (that can contain links
         and images), and generates category inserts

Utilo: aldonas na kroma informaro ("etikedo") al signifo de vorto
       aux la tuta kapvorto (kovrante cxiujn signifojn), montrite kiel
       enlinia listo kun koloraj konsiloj (kiuj povas enhavi ligilojn
       kaj bildojn), kaj generas kategoriajn enmetojn

Manfaat: menambahkan informasi ekstra ("tag") ke arti kata atau lema ...

Syfte: tillaegger extra information ...

Used by templates / Uzata far sxablonoj / Digunakan oleh templat:
- k (EO) , k (ID)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
* "loaddata-tbllingvoj" T76 in turn requiring template "tbllingvoj" (EO)
* "loaddata-tbllingvoj" T76 in turn requiring template "tblbahasa" (ID)
* "loaddata-tbldialektoj" T78 in turn requiring template "tbldialektoj" (EO)

This module can accept parameters whether sent to itself (own frame) or
to the caller (caller's frame). If there is a parameter "caller=true"
on the own frame then that own frame is discarded in favor of the
caller's one.

Incoming: * 2...25 anonymous obligatory parameters
            * language code (2 or 3 lowercase letters or more if enabled)
            * 1...24 further parameters elements of the main control
              chain, each 2...50 octet:s (see below)
          * 3 hidden parameters
            * "pagenameoverridetestonly=" can cause #E01
            * "nocat=" no error possible
            * "detrc=" no error possible

Returned: * one string intended to be showed after the numbered "#" list sign
            if relating to a meaning, or after the bulletized "*" list sign
            if relating to the complete lemma

This module is unbreakable (when called with correct module name
and function name). Every imaginable input from the caller and
from the imported module "xxx" will output either a useful
result or at least an error string.

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio). Cxiu imagebla enigo de la vokanto kaj
de la importata modulo "xxx" eldonos aux utilan
rezulton aux almenaux eraranoncan signocxenon.

Structure of the control chain:
- Groups and elements:
  - The chain may contain 1...24 elements belonging to following
    groups (see "contablimahuruf"):
    - "T" translingual -- "FFC8C8" pink
    - "D" dialect      -- "FFF0C0" orange
    - "G" grammar      -- "FFFFB0" yellow or "D0FFD0" green for "eo"
    - "F" field        -- "D0F0FF" bluish cyan
    - "S" style        -- "FFD0FF" violet
    - "C" other        -- "D8D8D8" grey
    There can be several elements belonging to same group, and groups can be
    omitted, but the order of groups is crucial, at least one element and one
    group is required, and same type of group must be introduced exactly one
    time. The elements are separated by wall "|", and groups are introduced by
    an UPPERCase letter followed by a colon and space ie ": " at the beginning
    of an element (NOT by a separate element).
- Special char:s:
  - Absolutely reserved:
    - wall "|" (wikisyntax) used to separate elements
      as well as params of the call
    - curly brackets "{" and "}" in double or higher multiplicity
      (wikisyntax) used to begin and end the call
    - rectangular brackets "[" and "]" in double or higher
      multiplicity (wikisyntax) absolutely prohibited
    - TAB and LF absolutely prohibited
  - In some positions reserved:
    - space " "
    - colon ":" (reserved after whitelisted name only)
    - dot "." (reserved at end of element only)
    - dash "-" (reserved at end of element only)
    - percent-sign "%" and and-sign "&"
- Examples:
  - <<D: % predominantly-|qeb.|G: VRF:|% only in negative contexts|S: sla.|C: % about abstract stuff>>
  - <<G: VIT.-|% kun prepozicio "pri"|% ecx-|VTR.|C: % celante personon>>
  - <<MS: ark.>>
  - <<RS: ark.>>
- Global reduction prefixes (recognized at the very beginning of the
  chain only):                                                                  !!!FIXME!!! imple incomplete
  - The prefix "M" should be used if the template is called from
    other places than a definition or overview above the definitions,
    it reduces the generosity of the output in following ways:
    - no categories
    - no colours
    - no links
    - no tooltips
    - no images
  - The prefix "R" removes the categories only. As opposed to "nocat=true"      !!!FIXME!!! nocat is DEPRECATED
    it does not suppress tracking categories. Furthermore "nocat=true" must
    not be used on lemma pages.
- Types of elements:
  - An element may be of one of two types:
    * plain text element, must NOT end with a dot, categorized by
      default, cannot provide a translated category, cannot suppress
      the language-dependent category, cannot provide a link, tooltip
      or image, as a base rule any text of sane length without reserved
      char:s is permitted, but some texts may                                   !!!FIXME!!! not checked yet
      be either simply disallowed, or disallowed in favor of an
      abbreviation (see below), or have one of the element prefixes (see
      below) obligatory or prohibited, or be reserved for a certain group
    * abbreviation element (2...12 char:s of a limited set (ASCII UPPERCase,    !!!FIXME!!! not checked yet
      ASCII lowercase, EO UPPERCase, EO lowercase, dash "-") will be
      peeked from a list, specific to a group (thus same abbreviation
      may be defined in different groups with different meanings, error if
      not found, categorized by default, may contain a translated category,
      may suppress the language-dependent category, may contain a link, may
      contain a tooltip, may contain an image), 2 subtypes:
      * "dot-type" ending with a dot "." (thus 3...13 char:s totally)
      * "colon-type" ie abbreviation element with extra information with
        2 more subsubtypes:
        * 3 UPPERCase letters followed by colon only for
          automatic extra information
        or
        * 3 UPPERCase letters followed by colon and space ": " and at least
          one more octet of manual extra information
        both automatic and manual only available for explicitly whitelisted
        triples (only "VRF" "VRG" "VRH" so far) that can be used in 3 ways:
        * "VRG." no extra information
        * "VRG:" automatic extra information
        * "VRG: ta sig an" manual extra information
- Element prefixes (usable with both plain text elements and abbreviation
  elements, but latter only the subtype without extra content ie "dot-type"):
  * By default an element generates both visible text and a category. If
    only one of those effects is desirable then an element prefix (one
    non-letter char followed by obligatory space) can be used:
    * "%" show text but do not categorize ("nopercatcent")
    * "&" categorize only but do not show text
- Element suffix for join request (usable with both plain text elements
  and abbreviation elements, even "colon-type") is a dash "-". Element
  having it will be joined with following element (show space only and keep
  continuous colour, instead of white semicolon plus space "; " between). Not
  more than 2 elements can be joined, and exactly one of those must use the
  prefix "%" too (show text but do not categorize), and none of them may
  have prefix "&" (categorize only but do not show text).
- Special rules for some groups:
  * group "T" translingual
    * the language code must be "mul", otherwise #E14
    * the language-dependent category is always omitted (as it obviously
      would not make sense) and control code "1" in abbreviation expansion
      is thus prohibited
  * group "D" dialect
    * special language-dependent mode of translation, see below
    * the language-dependent category is always omitted (as it obviously
      would not make sense) and control code "1" in abbreviation expansion
      is thus prohibited
- Translation of abbreviation elements:                                         !!!FIXME!!! add exclusive language code ie 6 -> 7 sections
  - The translation is provided by LUA tables. All elements are named and
    use a key/index (2...12 UPPERCase or lowercase letters, always without
    dot "." or colon ":", special rule applies for the dialect) and a value
    (expansion string) with 1 to 6 sections separated by walls "|". Missing
    sections are legal if at least the earliest one is present, truly
    empty ones (without dash "-" placeholder) are not.
  - The sections are:
    - (required) visible text and auto category
    - (optional) differring category
      * string without "Category:" prefix and without unnecessary
        capitalization, for example "fiziko"
      * default is same as the visible text
      * use dash "-" to "jump over" this section and take same
        as the visible text
    - (optional) link (lowercase string), default is no link,
      use dash "-" to "jump over" this section ie omit link
    - (optional) tooltip, default is no tooltip, use dash "-" to
      "jump over" this section ie omit tooltip
    - (optional) image (string without "File:" prefix),
      default is no image, image precedes the text in the output,
      use dash "-" to "jump over" this section ie omit image
    - (optional) control code
      * "1" (digit ONE) suppress language-dependent category, this is useful
        for language peculiarities that exist in one language only, by
        default both the generic and language-dependent category include are
        generated, note that this effect CANNOT be achieved with a plain
        text element, and this code is prohibited in groups T and D where
        this effect is always active
- Dialect abbreviations (D):
  Two types are defined:
  - state  -- 2 UPPERCase letters (code of a state, example "FI" or "US")       !!!FIXME!!! "RP" "GA"
  - region -- 3...8 lowercase letters (regions that are not states)
  A dialect abbreviation is specific for one language code, for example "UK"
  meaning "British" is valid with "en" but not with "sv". It could have some
  other meaning within an other language. Note that for example only "UK" has
  to be used in the control chain in the template call, but the index is
  "en-UK" in the imported LUA table, and finally "en" is the z-dimension
  whereas "UK" is the y-dimension in the separate module. Following special
  rules apply:
  * language code enters the translation process
  * in the LUA table, the LUA key/index is prepended by the language code
    (example: "en" -> en-ZA")
  * both the visible text and the category later generated by the module
    are prefixed by language name obtained from that lang code (example:
    "ZA" -> "Afrika Selatan" -> "Inggris Afrika Selatan", or
    "MX" -> "meksiklanda" -> "hispana meksiklanda"), and generated in two
    versions, one with small text for the screen, and one raw for the category
- Grammar abbreviations (G):
  There is a special handling of some types of verbs. For this reason, all
  7 abbreviations related to transitivity of verbs consist of 3 UPPERCase
  letters, but only 3 of them (VRF VRG VRH) can have the subtype "abbreviation
  element with extra information" (colon-type), besides the ordinary
  "abbreviation element" (dot-type).
  - VTR - transitive verb
  - VDT - doubly transitive verb
  - VOD - transitive verb with required object (in languages where the
          object usually can be omitted and thus implied)
  - VIT - intransitive verb
  - VRF - reflexive form of verb
  - VRG - exclusively reflexive verb (non-reflexive form does not exist)
  - VRH - quasi-exclusively reflexive verb (non-reflexive form exists but
          has a different meaning)
  For VRF VRG VRH the reflexive form can be autogenerated (depends on
  language code, unsupported language gives #E25) or manually provided.
- Field abbreviations (F):
  (see list below)
- Style abbreviations (S):
  (see list below)
- Other abbreviations (C):
  (see list below)

Following errors can occur:
- #E01 Internal error in module "mtagg"
  Possible causes:
  - strings not uncommented
  - function "mw.title.getCurrentTitle().text" AKA "{{PAGENAME}}" failed
  - pagename is invalid such as empty or too long or contains
    invalid brackets []{} or more than one consecutive apo, even if
    coming from "pagenameoverridetestonly="
- #E02 Erara uzo de sxablono "k", legu gxian dokumentajxon            !!!FIXME!!!
  Possible causes (early detected obvious problems with parameters):
  - less than 2 or more than 25 parameters supplied
  - wrong length of single parameter (must be 2...50 octet:s)
- #E03 Eraro en subsxablonoj uzataj far sxablono "k"                  !!!FIXME!!!
  Possible causes:
  - submodule failure (or not found ??) or returned invalid
- #E04 Evidente nevalida lingvokodo en sxablono "k"
  Possible causes:
  - the earliest parameter
- #E05 Nekonata lingvokodo en sxablono "k"
  Possible causes:
  - the earliest parameter
- #E06 Erara uzo de sxablono "k" pro rezervita signo en la stircxeno            !!!FIXME!!! NOT yet used
- #E07 Gxenerala eraro en la stircxeno
  Possible causes:
  - string too short (<2 octet:s) after removing valid
    prefixes (before removing this gives #E02 instead)
- #E08 Nevalida kodo de grupo en la stircxeno ... indekso
  Possible causes:
  - found UPPERCase letter different from T D G F S C + ": "
- #E09 Redunda aux nevalida grupa prefikso en la stircxeno
  Possible causes:
  - stuff like "D: D:" or "C: F:" ie redundant or conflicting group
    prefixes (note that the order of groups is checked only later)
  - stuff like "% % " "% & " ie redundant or conflicting element prefixes
  - stuff like "% F:" ie reversed order between group
    prefix and element prefix
- #E10 Erara ordo de grupoj en la stircxeno
  Possible causes:
  - wrong order, ie prefix of lower group appearing later
- #E11 Ripeta grupa prefikso en la stircxeno
  Possible causes:
  - prefix of same group appearing again later
- #E12 Enhavo ekstere de grupo
  Possible causes:
  - no group defined at the beginning
- #E13 Elementa prefikso uzata kun mallongiga elemento kun kroma enhavo
  Possible causes:
  - element prefix used together with abbreviation element
    with extra content ("colon-type"), this is prohibited
- #E14 Grupo T uzata kun lingvokodo alia ol mul
  Possible causes: !!!FIXME!!!
- #E20 Nekonata translingva mallongigo en la stircxeno
  Possible causes: !!!FIXME!!!
- #E21 Nekonata dialekta mallongigo en la stircxeno
  Possible causes: !!!FIXME!!!
- #E23 Nekonata kvargrupa (G F S C) mallongigo en la stircxeno
  Possible causes: !!!FIXME!!!
- #E25 Ne eblas auxtogeneri kroman enhavon
  Possible causes:
  - not supported for given language
- #E27 Nevalida kunigpostulo
  Possible causes:
  - rule that exactly one of two elements must have "%" is violated
  - attempt to merge more than two elements

EO:

Kategorio:Fiziko
Kategorio:Fiziko (angla)
Kategorio:Fiziko (Esperanto)

ID: !!!FIXME!!! NOT yet

Kategori:Mamalia
Kategori:id:Mamalia

]===]

local exporttable = {}

------------------------------------------------------------------------

---- CONSTANTS [O] ----

------------------------------------------------------------------------

  -- surrogate transcoding table (only needed for EO)

  local contabtransluteo = {}
  contabtransluteo[ 67] = 0xC488 -- CX
  contabtransluteo[ 99] = 0xC489 -- cx
  contabtransluteo[ 71] = 0xC49C -- GX
  contabtransluteo[103] = 0xC49D -- gx
  contabtransluteo[ 74] = 0xC4B4 -- JX
  contabtransluteo[106] = 0xC4B5 -- jx
  contabtransluteo[ 83] = 0xC59C -- SX
  contabtransluteo[115] = 0xC59D -- sx
  contabtransluteo[ 85] = 0xC5AC -- UX breve
  contabtransluteo[117] = 0xC5AD -- ux breve

  -- constant strings (error circumfixes)

  local constrelabg = '<span class="error"><b>'  -- lagom whining begin
  local constrelaen = '</b></span>'              -- lagom whining end
  local constrlaxhu = '&nbsp;&#91;&#93;&nbsp;'   -- lagom -> huge circumfix " [] "

  -- constant strings and table (tooltip and misc to be sent to the screen)

  local contabscrmisc = {}
  contabscrmisc[2] = 'style="border-bottom:1px dotted; cursor:help;"' -- lousy tooltip  !!!FIXME!!! NOT yet

  -- constant table -- ban list -- add obviously invalid access codes (2-letter or 3-letter) only

  -- length of the list is NOT stored anywhere, the processing stops
  -- when type "nil" is encountered, used by "lfivalidatelnkoadv" only

  -- "en.wiktionary.org/wiki/Wiktionary:Language_treatment" excluded languages
  -- "en.wikipedia.org/wiki/Spurious_languages"
  -- "iso639-3.sil.org/code/art" only valid in ISO 639-2
  -- "iso639-3.sil.org/code/zxx" "No linguistic content"

  local contabisbanned = {}
  contabisbanned = {'by','dc','ll','jp','art','deu','eng','epo','fra','gem','ger','ido','lat','por','rus','spa','swe','tup','zxx'} -- 1...20

  local contablimahuruf = {} -- T D G F S C
  contablimahuruf [1] = {'T','FFC8C8',''} -- pink
  contablimahuruf [2] = {'D','FFF0C0',''} -- orange
  contablimahuruf [3] = {'G','FFFFB0','D0FFD0'} -- yellow, green for "eo" (valid irrespective of "constrpriv")
  contablimahuruf [4] = {'F','D0F0FF',''} -- bluish cyan (bluebluegreen)
  contablimahuruf [5] = {'S','FFD0FF',''} -- violet
  contablimahuruf [6] = {'C','D8D8D8',''} -- grey

  -- uncommentable EO vs ID strings

  local constrpriv = "eo"                 -- EO (privileged site language)
  -- local constrpriv = "id"                 -- ID (privileged site language)
  local constringvoj = "Modulo:loaddata-tbllingvoj"    -- EO
  -- local constringvoj = "Modul:loaddata-tblbahasa"      -- ID
  local constrlektoj = "Modulo:loaddata-tbldialektoj"  -- EO
  -- local constrlektoj = "Modul:loaddata-tbldialek"      -- ID
  local constrkatp = "Kategorio:"                      -- EO !!!FIXME!!!
  -- local constrkatp = "Kategori:"                       -- ID !!!FIXME!!!

  -- uncommentable EO vs ID table (caller name for error messages and tracking cat:s)

  local contabkoll = {'SXablono:','k'}  -- EO name of the caller (semi-hardcoded, we do NOT peek it)
  -- local contabkoll = {'Templat:','k'}   -- ID name of the caller (semi-hardcoded, we do NOT peek it)

  -- uncommentable EO vs ID table (tracking cat:s)

  local contabtrako = {}
  contabtrako [0] = 'Erara uzo de sxablono'
  contabtrako [1] = 'Evidente nevalida'
  contabtrako [2] = 'Nekonata'
  contabtrako [3] = 'lingvokodo'
  contabtrako [4] = 'loke'
  contabtrako [5] = 'nome'

  -- uncommentable EO vs ID table (error messages)

  -- #E02...#E99, holes permitted, separate "contabkoll" needed for "\\@"
  -- note that #E00 and #E01 are NOT supposed to be included here

  local contaberaroj = {}
  contaberaroj[02] = 'Erara uzo de \\@, legu gxian dokumentajxon'                    -- EO #E02
  -- contaberaroj[02] = 'Penggunaan salah \\@, bacalah dokumentasinya'                  -- ID #E02
  contaberaroj[03] = 'Eraro en subsxablonoj uzataj far \\@'                          -- EO #E03
  -- contaberaroj[03] = 'Kesalahan dalam subtemplat digunakan oleh \\@'                 -- ID #E03
  contaberaroj[04] = 'Evidente nevalida lingvokodo sendita al \\@'                   -- EO #E04
  -- contaberaroj[04] = 'Kode bahasa jelas-jelas salah dikirim ke \\@'                  -- ID #E04
  contaberaroj[05] = 'Nekonata lingvokodo sendita al \\@'                            -- EO #E05
  -- contaberaroj[05] = 'Kode bahasa tidak dikenal dikirim ke \\@'                      -- ID #E05
  contaberaroj[06] = 'Riservita signo en la stircxeno por \\@'                       -- EO #E06
  -- contaberaroj[06] = 'Karakter direservasi dalam rantai pengendali untuk \\@'        -- ID #E06
  contaberaroj[07] = 'Gxenerala eraro en la stircxeno por \\@'                       -- EO #E07
  -- contaberaroj[07] = 'Kesalahan umum dalam rantai pengendali untuk \\@'              -- ID #E07
  contaberaroj[08] = 'Nevalida kodo de grupo en la stircxeno por \\@, indekso \\~, subtenataj T D G F S C'       -- EO #E08
  -- contaberaroj[08] = 'Huruf golongan salah dalam rantai pengendali untuk \\@, indeks \\~, didukung T D G F S C'  -- ID #E08
  contaberaroj[09] = 'Redunda aux nevalida grupa prefikso en la stircxeno por \\@'                     -- EO #E09
  -- contaberaroj[09] = 'Prefiks golongan redundan atau tidak valid dalam rantai pengendali untuk \\@'    -- ID #E09
  contaberaroj[10] = 'Erara ordo de grupoj en la stircxeno por \\@, estu T D G F S C'                  -- EO #E10
  -- contaberaroj[10] = 'Urutan golongan salah dalam rantai pengendali untuk \\@, sebaiknya T D G F S C'  -- ID #E10
  contaberaroj[11] = 'Ripeta grupa prefikso en la stircxeno por \\@'                                   -- EO #E11
  -- contaberaroj[11] = 'Prefiks golongan ulang dalam rantai pengendali untuk \\@'                        -- ID #E11
  contaberaroj[12] = 'Enhavo ekstere de grupo en la stircxeno por \\@'                                      -- EO #E12
  -- contaberaroj[12] = 'Keterangan luar golongan dalam rantai pengendali untuk \\@'                           -- ID #E12
  contaberaroj[13] = 'Elementa prefikso uzata kun mallongigo kroma en la stircxeno por \\@'                 -- EO #E13
  -- contaberaroj[13] = 'Prefiks element digunakan dengan singkatan ekstra dalam rantai pengendali untuk \\@'  -- ID #E13
  contaberaroj[14] = 'Grupo T uzata kun lingvokodo alia ol mul en la stircxeno por \\@'                     -- EO #E14
  -- contaberaroj[14] = 'Golongan T digunakan dengan kode berbeda dari mul dalam rantai pengendali untuk \\@'  -- ID #E14
  contaberaroj[20] = 'Nekonata translingva mallongigo en la stircxeno por \\@'                              -- EO #E20
  -- contaberaroj[20] = 'Singkatan antarbahasa tidak dikenal dalam rantai pengendali untuk \\@'                -- ID #E20
  contaberaroj[21] = 'Nekonata dialekta mallongigo en la stircxeno por \\@'                                 -- EO #E21
  -- contaberaroj[21] = 'Singkatan dialek tidak dikenal dalam rantai pengendali untuk \\@'                     -- ID #E21
  contaberaroj[23] = 'Nekonata kvargrupa (G F S C) mallongigo en la stircxeno por \\@'                      -- EO #E23
  -- contaberaroj[23] = 'Singkatan empat golongan (G F S C) tidak dikenal dalam rantai pengendali untuk \\@'   -- ID #E23
  contaberaroj[25] = 'Ne eblas auxtogeneri kroman enhavon en \\@'                                           -- EO #E25
  -- contaberaroj[25] = 'Tidak berhasil membangkitkan konten ekstra secara otomatis dalam \\@'                 -- ID #E25
  contaberaroj[27] = 'Nevalida kunigpostulo en \\@'                                                         -- EO #E27
  -- contaberaroj[27] = 'Permintaan tempel yang tidak valid dalam \\@'                                         -- ID #E27

  local contabtt = {} -- "T" -- translingual -- vis cat link tool image -- NOT ctl here -- code must be "mul"
  contabtt ['ASKI'] = 'askia signo'
  contabtt ['ARAB'] = 'araba cifero'
  contabtt ['ROMA'] = 'roma cifero'             -- EO single unicode character even if multiple digits
  contabtt ['ASKL'] = 'askia litero'
  contabtt ['LATI'] = 'latina litero'
  contabtt ['CIRI'] = 'cirila litero'
  contabtt ['CNTR'] = 'cxina signo tradicia'
  contabtt ['CNSI'] = 'cxina signo simpligita'
  contabtt ['CNKO'] = 'cxina signo komuna'
  contabtt ['JAKA'] = 'japana signo kanjxia'
  contabtt ['TIBE'] = 'tibeta signo'
  contabtt ['GRKO'] = 'greka aux kopta signo'
  contabtt ['MATE'] = 'matematika signo'
  contabtt ['IFAS'] = 'IFA-signo'
  contabtt ['VALU'] = 'signo de valuto'
  contabtt ['BILD'] = 'bilda signo'
  contabtt ['VALSI'] = 'simbolo de valuto'
  contabtt ['MATSI'] = 'matematika simbolo'
  contabtt ['KEMSI'] = 'kemia simbolo'          -- EO (YES "Pu" but NO "DDT")

  -- "D" -- dialect -- dialekto -- vis cat link tool image -- NOT ctl here

  local contabgg = {} -- "G" -- grammar -- vis(obligatory) cat link tool image ctl
  contabgg["kofr"] = "kofrovorto"               -- EO (en: smog transponder, eo: foklea, id: miras, sv: flextid bankomat)
  -- contabgg["kofr"] = "kata lakuran"             -- ID
  contabgg["miks"] = "miksajxo"                 -- EO (eo: volapugajxo, sv: snarkofag)
  -- contabgg["miks"] = "kata campuran"            -- ID
  contabgg["deta"] = "derivajxo de tabelvorto"  -- EO (nur eo: "iama", "kialo") (!!! tabelvorto mem estas vortospeco !!!)
  -- contabgg["deta"] = "derivasi kata tabel"      -- ID (hanya eo)
  contabgg["sing"] = "nursingulara|substantivo nursingulara"      -- EO (nur SB: en: "music" , eo: "kupro")
  -- contabgg["sing"] = "tidak terhitung|nomina tidak terhitung"     -- ID (hanya SB)
  contabgg["plpl"] = "nurplurala|substantivo nurplurala"          -- EO (nur SB: en: "trousers" , eo: "okulvitroj")
  -- contabgg["plpl"] = "selalu jamak|nomina selalu jamak"           -- ID (hanya SB)
  contabgg["sapl"] = "samformplurala|substantivo samformplurala"  -- EO (nur SB: en: "sheep")
  -- contabgg["sapl"] = "jamak bentuk sama|nomina jamak bentuk sama" -- ID (hanya SB)
  contabgg["nutr"] = "utruma(n)|substantivo utruma(n)"            -- EO (nur sv, eble ecx da: nur SB)
  -- contabgg["nutr"] = "utrum(n)|nomina utrum(n)"                   -- ID (hanya sv, mungkin bahkan da)
  contabgg["tneu"] = "neuxtruma(t)|substantivo neuxtruma(t)"      -- EO (nur sv, eble ecx da: nur SB)
  -- contabgg["tneu"] = "neutrum(t)|nomina neutrum(t)"               -- ID (hanya sv, mungkin bahkan da)
  contabgg["kole"] = "kolektiva|substantivo kolektiva"            -- EO
  -- contabgg["kole"] = "kolektif|nomina kolektif"                   -- ID
  contabgg["stat"] = "loka statika|adverbo loka statika"          -- EO (nur AV)
  -- contabgg["stat"] = "tempat statik|adverbia tempat statik"       -- ID (hanya AV)
  contabgg["dyna"] = "loka dinamika|adverbo loka dinamika"        -- EO (nur AV)
  -- contabgg["dyna"] = "tempat dinamik|adverbia tempat dinamik"     -- ID (hanya AV)
  contabgg["onom"] = "onomatopeo"                                 -- EO (nur IN)
  -- contabgg["onom"] = "onomatope"                                  -- ID (hanya IN)
  contabgg["ava"..string.char(197,173)] = "-aux|esperanta adverbo finigxanta per (-aux)|-|-|-|1"
  contabgg["ajly"]                      = "-ly|angla adjektivo finigxanta per (-ly)|-|-|-|1"
  contabgg["VTR"]  = "transitiva|verbo transitiva"                           -- EO (nur VE)
  contabgg["VDT"]  = "duoble transitiva verbo|verbo duoble transitiva"       -- EO (nur VE)
  contabgg["VOD"]  = "transitiva verbo kaj objekto deviga|verbo transitiva"  -- EO (nur VE)
  contabgg["VIT"]  = "netransitiva|verbo netransitiva"                       -- EO (nur VE)
  contabgg["VRF"]  = "refleksiva formo|verbo refleksiva"                     -- EO (nur VE)
  contabgg["VRG"]  = "nepre refleksiva verbo|verbo refleksiva"               -- EO (nur VE)
  contabgg["VRH"]  = "kvazauxnepre refleksiva verbo|verbo refleksiva"        -- EO (nur VE)

  local contabf = {} -- "F" -- field -- fako -- vis(obligatory) cat link tool image ctl
  contabf["pers"] = "persona nomo"                              -- EO (nur SB)
  -- contabf["pers"] = "nama orang"                                -- ID (hanya SB)
  contabf["lokn"] = "loknomo"                                   -- EO (nur SB)
  -- contabf["lokn"] = "nama tempat"                               -- ID (hanya SB)

  local contabs = {} -- "S" -- stilo, lingvoregistro -- vis(obligatory) cat link tool image ctl
  contabs["sla"] = "slanga"                                      -- EO
  -- contabs["sla"] = "bahasa gaul"                                 -- ID
  contabs["vul"] = "vulgara"                                     -- EO
  -- contabs["vul"] = "bahasa jahat"                                -- ID

  local contabc = {} -- "C" -- misc -- ceteraj -- vis(obligatory) cat link tool image ctl
  contabc["idi"]  = "idioma vortgrupo"               -- EO (eo: "nigra sxafo") (esprimo/idiomajxo) (subklaso de vortospeco vortgrupo GR)
  -- contabc["idi"]  = "kumpulan kata idioma"           -- ID
  contabc["par"]  = "idioma vortoparo"               -- EO (sv: "hugget som stucket") (subklaso de vortospeco vortgrupo GR)
  -- contabc["par"]  = "dua kata idioma"                -- ID
  contabc["prov"] = "proverbo"                       -- EO (sv: "Lika barn leka baest.") (subklaso de vortospeco frazo KA)
  -- contabc["prov"] = "peribahasa"                     -- ID
  contabc["bas"]  = "baza vortoprovizo"              -- EO
  -- contabc["bas"]  = "kosa kata dasar"                -- ID
  contabc["plen-inv"] = "vorto de la plena inventaro de Kotapedia|-|-|-|-|1"  -- EO suppress language-dependent category

  -- constants to control behaviour from source AKA semi-hardcoded parameters

  local conbookodlng  = false  -- "true" to allow long codes like "zh-min-nan"
  local conboomiddig  = false  -- "true" to allow middle digit "s7a"

------------------------------------------------------------------------

---- SPECIAL STUFF OUTSIDE MAIN [B] ----

------------------------------------------------------------------------

  -- SPECIAL VAR:S

local qldingvoj = {}     -- type "table" and nested
local qldlektoj = {}     -- type "table" and nested
local qbooguard = false  -- only for the guard test, pass to other var ASAP
local qboodetrc = true   -- from "detrc=true" but default is "true" !!!
local qstrtrace = '<br>' -- for main & sub:s, debug report request by "detrc="

---- GUARD AGAINST INTERNAL ERROR AND IMPORT TWO VIA LOADDATA ----

qbooguard = ((type(constrpriv)~='string') or (type(constringvoj)~='string') or (type(constrlektoj)~='string') or (type(constrkatp)~='string'))
if (not qbooguard) then
  qldingvoj = mw.loadData(constringvoj) -- can crash here
  qbooguard = (type(qldingvoj)~='table') -- seems to be always false
end--if
if (not qbooguard) then
  qldlektoj = mw.loadData(constrlektoj) -- can crash here
  qbooguard = (type(qldlektoj)~='table') -- seems to be always false
end--if

------------------------------------------------------------------------

---- DEBUG FUNCTIONS [D] ----

------------------------------------------------------------------------

-- Local function LFTRACEMSG

-- Enhance upvalue "qstrtrace" with fixed text.

-- for variables the other sub "lfdshowvar" is preferable but in exceptional
-- cases it can be justified to send text with values of variables to this sub

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

local function lftracemsg (strshortline)
  if (qboodetrc and (type(strshortline)=='string')) then
    qstrtrace = qstrtrace .. strshortline .. '.<br>' -- dot added !!!
  end--if
end--function lftracemsg

------------------------------------------------------------------------

-- Local function LFDMINISANI

-- Input  : * strdangerous -- must be type "string", empty legal

-- Output : * strsanitized -- can happen to be quasi-empty with <<"">>

-- Disallow: cross "#" 35 | apo "'" 39 | "<" 60 | ">" 62 | "[" 91 | "]" 93
-- To be called from "lfdshowvcore" <- "lfdshowvar" only.

local function lfdminisani (strdangerous)

  local strsanitized = '"' -- begin quot
  local numlecnx = 0
  local numindabx = 1 -- ONE-based
  local numsigno = 0

  numlecnx = string.len (strdangerous)
  while true do
      if (numindabx>numlecnx) then
        break -- done string char after char
      end--if
      numsigno = string.byte (strdangerous,numindabx,numindabx)
      if ((numsigno<36) or (numsigno==39) or (numsigno==42) or (numsigno==58) or (numsigno==60) or (numsigno==62) or (numsigno==91) or (numsigno==93) or (numsigno>122)) then
        strsanitized = strsanitized .. '{' .. tostring (numsigno) .. '}'
      else
        strsanitized = strsanitized .. string.char (numsigno)
      end--if
      if ((numlecnx>50) and (numindabx==20)) then
        numindabx = numlecnx - 20 -- jump
        strsanitized = strsanitized .. '" ... "'
      else
        numindabx = numindabx + 1
      end--if
  end--while
  strsanitized = strsanitized .. '"' -- don't forget final quot

  return strsanitized

end--function lfdminisani

------------------------------------------------------------------------

-- Local function LFDSHOWVCORE

-- Prebrew report about content of a variable (including optional full
-- listing of a table with integer indexes starting from ZERO). !!!FIXME!!!

-- Input  : * vardubious -- content (any type including "nil")
--          * strname -- name of the variable (string)
--          * vardescri -- optional, defa empty
--          * vartablim -- optional, defa 0

-- Depends on functions :
-- [D] lfdminisani

local function lfdshowvcore (vardubious, strname, vardescri, vartablim)

  local strtype = ''
  local strreport = ''
  local numindax = 0
  local numlencx = 0
  local numkeynumber = 0
  local numkeycetera = 0

  if (type(vartablim)~='number') then
    vartablim = 0 -- deactivate listing of a table
  end--if
  if (type(vardescri)~='string') then
    vardescri = '' -- omit comment
  else
    vardescri = ' (' .. vardescri .. ')' -- augment only if used !!!
  end--if
  if (type(strname)~='string') then -- this must be below "type(vardescri)"
    strname = '??' -- bite the bullet
  else
    strname = '"' .. strname .. '"' .. vardescri -- combo
  end--if

  strtype = type(vardubious)

  if (strtype=='table') then

      for k,v in pairs(vardubious) do
        if (type(k)=='number') then
          numkeynumber = numkeynumber + 1
        else
          numkeycetera = numkeycetera + 1
        end--if
      end--for

      strreport = 'Table ' .. strname .. ' contains ' .. tostring (numkeynumber)
      strreport = strreport .. ' numeric keys and ' .. tostring (numkeycetera)
      strreport = strreport .. ' other keys'

      if ((numkeynumber~=0) and (vartablim~=0)) then -- !!!FIXME!!!
        strreport = strreport .. ', content :'
        numindax = 0
        while true do
          if (numindax>vartablim) then
            break -- done table
          end--if
          strreport = strreport .. ' ' .. tostring (numindax) .. ' -> ' .. lfdminisani (tostring (vardubious[numindax]) )
          numindax = numindax + 1
        end--while
      end--if

  else

      strreport = 'Variable ' .. strname .. ' has type "' .. strtype .. '"'
      if (strtype=='string') then
        numlencx = string.len (vardubious)
        strreport = strreport .. ' and length ' .. tostring (numlencx)
        if (numlencx~=0) then
          strreport = strreport .. ' and content ' .. lfdminisani (vardubious)
        end--if
      else
        if (strtype~='nil') then
          strreport = strreport .. ' and content "' .. tostring (vardubious) .. '"'
        end--if
      end--if (strtype=='string') else

  end--if (strtype=='table') else

  return strreport

end--function lfdshowvcore

local function lfdshowvar (varduubious, strnaame, vardeskkri, vartabljjm)

  if (qboodetrc) then
    qstrtrace = qstrtrace .. lfdshowvcore (varduubious, strnaame, vardeskkri, vartabljjm) .. '.<br>' -- dot added !!!
  end--if

end--function lfdshowvar

------------------------------------------------------------------------

---- MATH FUNCTIONS [E] ----

------------------------------------------------------------------------

local function mathisintrange (numinpuut, numzjmin, numzjmax)
  local numclean = 0
  local booisclean = false
  numclean = math.floor (numinpuut) -- no transcendental
  numclean = math.max (numclean,numzjmin) -- not below minimum
  numclean = math.min (numclean,numzjmax) -- no trillions
  booisclean = (numclean==numinpuut)
  return booisclean
end--function mathisintrange

local function mathdiv (xdividend, xdivisor)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividend / xdivisor)
  return resultdiv
end--function mathdiv

local function mathmod (xdividendo, xdivisoro)
  local resultmod = 0 -- MOD operator is "%" and bitwise AND operator lack too
  resultmod = xdividendo % xdivisoro
  return resultmod
end--function mathmod

------------------------------------------------------------------------

-- Local function MATHBITWRIT

-- Write bit selected by ZERO-based index assigning it to "1" or "0".

-- Depends on functions :
-- [E] mathdiv mathmod

local function mathbitwrit (numinkoming, numbityndex, boowrite)
  local numpatched = 0
  local numcountup = 0
  local numweight = 1 -- single bit value 1 -> 2 -> 4 -> 8 ...
  local boosinglebit = false
  while true do
    if ((numinkoming==0) and (numcountup>numbityndex)) then
      break -- we have run out of bits on BOTH possible sources
    end--if
    if (numcountup==numbityndex) then
      boosinglebit = boowrite -- overwrite bit
    else
      boosinglebit = (mathmod(numinkoming,2)==1) -- pick bit
    end--if
    numinkoming = mathdiv(numinkoming,2) -- shift right
    if (boosinglebit) then
      numpatched = numpatched + numweight -- add one bit rtl only if true
    end--if
    numcountup = numcountup + 1 -- count up here until we run out of bits
    numweight = numweight * 2
  end--while
  return numpatched
end--function mathbitwrit

------------------------------------------------------------------------

-- Local function MATHBITTEST

-- Find out whether single bit selected by ZERO-based index is "1" / "true".

-- Result has type "boolean".

-- Depends on functions :
-- [E] mathdiv mathmod

local function mathbittest (numincoming, numbitindex)
  local boores = false
  while true do
    if ((numbitindex==0) or (numincoming==0)) then
      break -- we have either reached our bit or run out of bits
    end--if
    numincoming = mathdiv(numincoming,2) -- shift right
    numbitindex = numbitindex - 1 -- count down to ZERO
  end--while
  boores = (mathmod(numincoming,2)==1) -- pick bit
  return boores
end--function mathbittest

------------------------------------------------------------------------

---- NUMBER CONVERSION FUNCTIONS [N] ----

------------------------------------------------------------------------

-- Local function LFNUMTO2DIGIT

-- Convert integer 0...99 to decimal ASCII string always 2 digits "00"..."99".

-- Depends on functions :
-- [E] mathisintrange mathdiv mathmod

local function lfnumto2digit (numzerotoninetynine)
  local strtwodig = '??' -- always 2 digits
  if (mathisintrange(numzerotoninetynine,0,99)) then
    strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  end--if
  return strtwodig
end--function lfnumto2digit

------------------------------------------------------------------------

---- LOW LEVEL STRING FUNCTIONS [G] ----

------------------------------------------------------------------------

local function lfgtestnum (numkaad)
  local boodigit = false
  boodigit = ((numkaad>=48) and (numkaad<=57))
  return boodigit
end--function lfgtestnum

local function lfgtestuc (numkode)
  local booupperc = false
  booupperc = ((numkode>=65) and (numkode<=90))
  return booupperc
end--function lfgtestuc

local function lfgtestlc (numcode)
  local boolowerc = false
  boolowerc = ((numcode>=97) and (numcode<=122))
  return boolowerc
end--function lfgtestlc

------------------------------------------------------------------------

---- UTF8 FUNCTIONS [U] ----

------------------------------------------------------------------------

-- Local function LFULNUTF8CHAR

-- Evaluate length of a single UTF8 char in octet:s.

-- Input  : * numbgoctet  -- beginning octet of a UTF8 char

-- Output : * numlen1234x -- number 1...4 or ZERO if invalid

-- Does NOT thoroughly check the validity, looks at 1 octet only.

local function lfulnutf8char (numbgoctet)
  local numlen1234x = 0
    if (numbgoctet<128) then
      numlen1234x = 1 -- $00...$7F -- ANSI/ASCII
    end--if
    if ((numbgoctet>=194) and (numbgoctet<=223)) then
      numlen1234x = 2 -- $C2 to $DF
    end--if
    if ((numbgoctet>=224) and (numbgoctet<=239)) then
      numlen1234x = 3 -- $E0 to $EF
    end--if
    if ((numbgoctet>=240) and (numbgoctet<=244)) then
      numlen1234x = 4 -- $F0 to $F4
    end--if
  return numlen1234x
end--function lfulnutf8char

------------------------------------------------------------------------

-- Local function LFCASEREST

-- Adjust case of a single letter (restricted), only ASCII
-- plus a very limited set of 2-octet UTF8 letters. (this is REST)

-- Input  : * strucinrsut : single unicode letter (1 or 2 octet:s)
--          * booupcas    : for desired uppercase "true" and for
--                          lowercase "false"
--          * numselset   : 0 ASCII -- 2 eo -- 5 sv (value 255 NOT here)

-- Output : * strucinrsut : (same var, "ZZ" on failure)

-- Depends on functions : (this is REST)
-- [U] lfulnutf8char
-- [G] lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

-- Unknown non-ASCII input strictly returns "ZZ"

-- Defined sets:
-- 2: 2 x 6 uppercase and lowercase -eo- (CX GX HX JX SX UX cx gx hx jx sx ux)
--    upper CX $0108 GX $011C HX $0124 JX $0134 SX $015C UX $016C lower +1
-- 5: 2 x 4 uppercase and lowercase -sv- (AA AE OE EE aa ae oe ee)
--    upper AE $00C4 AA $00C5 EE $00C9 OE $00D6 lower +$20

local function lfcaserest (strucinrsut, booupcas, numselset)

  local numlaengdn = 0 -- length from "string.len"
  local numchaerr = 0 -- UINT8 beginning char
  local numchaess = 0 -- UINT8 later char (BIG ENDIAN, lower value here up)
  local numchareel = 0 -- UINT8 code relative to beginning of block $00...$FF
  local numdeta = 0 -- SINT8 +32 or -32 or +1 or -1 or ZERO
  local numtheemp = 0

  local boowantlowr = false
  local booiisuppr = false
  local booiislowr = false
  local boovalid = false -- preASSume guilt

  booupcas = not (not booupcas)
  boowantlowr = (not booupcas)

  while true do -- upper fake loop (this is REST)

    numlaengdn = string.len (strucinrsut)
    if ((numlaengdn<1) or (numlaengdn>2)) then -- only 1 or 2 accepted
      break -- bad string length
    end--if
    numchaerr = string.byte (strucinrsut,1,1)
    if ((lfulnutf8char(numchaerr))~=numlaengdn) then
      break -- mismatch with length
    end--if

    if (numlaengdn==1) then
      booiisuppr = lfgtestuc(numchaerr)
      booiislowr = lfgtestlc(numchaerr)
      if (booiisuppr and boowantlowr) then
        numdeta = 32 -- ASCII UPPER->lower
      end--if
      if (booiislowr and booupcas) then
        numdeta = -32 -- ASCII lower->UPPER
      end--if
      boovalid = true
      break -- success with ASCII, almost done
    end--if

    numchaess = string.byte (strucinrsut,2,2) -- only $80 to $BF
    numchareel = (mathmod(numchaerr,4)*64) + (numchaess-128) -- 4 times 64

    if ((numselset==2) and ((numchaerr==196) or (numchaerr==197))) then -- eo
      numtheemp = mathbitwrit (numchareel,0,false) -- bad way to do AND $FE
      if ((numtheemp==8) or (numtheemp==28) or (numtheemp==36) or (numtheemp==52) or (numtheemp==92) or (numtheemp==108)) then
        booiisuppr = (numtheemp==numchareel) -- UC below and even
        booiislowr = not booiisuppr
        if (booiisuppr and boowantlowr) then
          numdeta = 1 -- UPPER->lower
        end--if
        if (booiislowr and booupcas) then
          numdeta = -1 -- lower->UPPER
        end--if
        boovalid = true
        break -- success with -eo-, almost done
      end--if
    end--if ((numselset==2) and ...

    if ((numselset==5) and (numchaerr==195)) then -- sv
      numtheemp = mathbitwrit (numchareel,5,false) -- bad way to do AND $DF
      if ((numtheemp==196) or (numtheemp==197) or (numtheemp==201) or (numtheemp==214)) then
        booiisuppr = (numtheemp==numchareel) -- UC below and bit is ZERO
        booiislowr = not booiisuppr
        if (booiisuppr and boowantlowr) then
          numdeta = 32 -- UPPER->lower
        end--if
        if (booiislowr and booupcas) then
          numdeta = -32 -- lower->UPPER
        end--if
        boovalid = true
        break -- success with -sv-, almost done
      end--if
    end--if ((numselset==5) and ...

    break -- finally to join mark -- unknown non-ASCII char is a fact :-(
  end--while -- upper fake loop -- join mark (this is REST)

  while true do -- lower fake loop (this is REST)

    if (not boovalid) then
      strucinrsut = "ZZ" -- unknown non-ASCII char
      break -- helvete
    end--if

    if (numdeta==0) then
      break -- nothing to do
    end--if

    if (numlaengdn==1) then
      strucinrsut = string.char (numchaerr + numdeta) -- no risk of carry here
      break -- done
    end--if

    strucinrsut = string.char (numchaerr) .. string.char (numchaess + numdeta)

    break -- finally to join mark
  end--while -- lower fake loop -- join mark (this is REST)

  return strucinrsut -- same var for input and output !!!

end--function lfcaserest

------------------------------------------------------------------------

-- Local function LFUCASESTR

-- Adjust case of beginning letter or of all letters in a word or group
-- of words to upper or lower, with limited and adjustable unicode support.

-- Input  : * strenigo  : word or group of words (may be empty)
--          * boouprcas : "true" for uppercase and "false" for lowercase
--          * boodooall : "true" to adjust all letters, "false" only beginning
--          * numslsaet : 0 ASCII -- NOPE 2 eo -- 5 sv -- 255 generic

-- Depends on functions : (restricted LFCASEREST)
-- [U] lfulnutf8char lfcaserest
-- [G] lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

local function lfucasestr (strenigo, boouprcas, boodooall, numslsaet)

  local numlein = 0
  local numposi = 1 -- octet position ONE-based
  local numcut = 0 -- length of an UTF8 char
  local bootryadj = false -- try to adjust single char
  local strte7mp = ''
  local strelygo = ''

  boouprcas = not (not boouprcas)
  boodooall = not (not boodooall)
  numlein = string.len (strenigo)

  while true do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodooall or (numposi==1))
    numcut = lfulnutf8char(string.byte(strenigo,numposi,numposi))
    if ((numcut==0) or ((numposi+numcut-1)>numlein)) then
      numcut = 1 -- skip ie copy one faulty octet
      bootryadj = false
    end--if
    strte7mp = string.sub (strenigo,numposi,(numposi+numcut-1)) -- 1...4 oct
    if (bootryadj) then
      strte7mp = lfcaserest(strte7mp,boouprcas,numslsaet) -- (restricted LFCASEREST)
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut -- done 1...4 octet:s
  end--while

  return strelygo

end--function lfucasestr

------------------------------------------------------------------------

---- HIGH LEVEL STRING FUNCTIONS [I] ----

------------------------------------------------------------------------

-- Local function LFIBANMULTI

-- Test string for validity by banning listed single char:s by multiplicity.

-- Input  : * "strkoneven" -- even and 2...24, wrong length gives
--                            "true", tolerated multiplicity "0"..."9"
--          * "strsample" -- 0...1'024, empty gives "false",
--                           too long gives "true"

-- Output : * "booisevil" -- "true" if evil

-- Depends on functions :
-- [G] lfgtestnum
-- [E] mathmod

-- Incoming control string "strkoneven" with pairs of char:s, for
-- example "'2&0" will tolerate 2 consecutive apo:s but
-- not 3, and completely ban the and-sign "&".

local function lfibanmulti (strkoneven, strsample)

  local booisevil = false
  local numkonlen = 0 -- length of control string
  local numsamlen = 0 -- length of sample string
  local numinndex = 0 -- ZERO-based outer index
  local numinneri = 0 -- ZERO-based inner index
  local numchear  = 0
  local numnexxt  = 0
  local nummultiq = 1 -- counted multiplicity
  local numcrapp  = 0 -- from "strkoneven" char to test
  local numvrapp  = 0 -- from "strkoneven" multiplicity limit

  numsamlen = string.len (strsample)
  if (numsamlen~=0) then
    numkonlen = string.len (strkoneven)
    booisevil = (numkonlen<2) or (numkonlen>24) or (mathmod(numkonlen,2)~=0) or (numsamlen>1024)
    while true do -- outer loop
        if (booisevil or (numinndex>=numsamlen)) then
          break
        end--if
        numchear = string.byte (strsample,(numinndex+1),(numinndex+1))
        if (numchear==0) then
          booisevil = true -- ZERO is unconditionally prohibited
          break
        end--if
        numinndex = numinndex + 1
        numnexxt = 0
        if (numinndex~=numsamlen) then
          numnexxt = string.byte (strsample,(numinndex+1),(numinndex+1))
        end--if
        if (numchear==numnexxt) then
          nummultiq = nummultiq + 1
        end--if
        if ((numchear~=numnexxt) or (numinndex==numsamlen)) then
          numinneri = 0
          while true do -- innner loop
            if (numinneri==numkonlen) then
              break
            end--if
            numcrapp = string.byte (strkoneven,(numinneri+1),(numinneri+1))
            numvrapp = string.byte (strkoneven,(numinneri+2),(numinneri+2))
            if (not lfgtestnum(numvrapp)) then
              booisevil = true -- crime in control string detected
              break
            end--if
            if ((numchear==numcrapp) and (nummultiq>(numvrapp-48))) then
              booisevil = true -- multiplicity crime in sample string detected
              break
            end--if
            numinneri = numinneri + 2 -- ZERO-based inner index and STEP 2
          end--while -- innner loop
          if (booisevil) then
            break
          end--if
          nummultiq = 1 -- restart from ONE !!!
        end--if ((numchear~=numnexxt) or (numinndex==numsamlen)) then
    end--while -- outer loop
  end--if (numsamlen~=0) then

  return booisevil

end--function lfibanmulti

------------------------------------------------------------------------

-- Local function LFIVALIDATELNKOADV

-- Advanced test whether a string (intended to be a language code) is valid
-- containing only 2 or 3 lowercase letters, or 2...10 char:s and with some
-- dashes, or maybe a digit in middle position or maybe instead equals to "-"
-- or "??" and maybe additionally is not included on the ban list.

-- Input  : * strqooq -- string (empty is useless and returns
--                       "true" ie "bad" but cannot cause any major harm)
--          * booyesdsh -- "true" to allow special code dash "-"
--          * booyesqst -- "true" to allow special code doublequest "??"
--          * booloonkg -- "true" to allow long codes such as "zh-min-nan"
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- (inverted) "true" to skip test against ban table

-- Output : * booisvaladv -- true if string is valid

-- Depends on functions :
-- [G] lfgtestnum lfgtestlc

-- Depends on constants :
-- * table "contabisbanned"

-- Incoming empty string is safe but type "nil" is NOT.

-- Digit is tolerable only ("and" applies):
-- * if boodigit is "true"
-- * if length is 3 char:s
-- * in middle position

-- Dashes are tolerable (except in special code "-") only ("and" applies):
-- * if length is at least 4 char:s (if this is permitted at all)
-- * in inner positions
-- * NOT adjacent
-- * maximally TWO totally
-- There may be maximally 3 adjacent letters, this makes at least ONE dash
-- obligatory for length 4...7, and TWO dashes for length 8...10.

local function lfivalidatelnkoadv (strqooq, booyesdsh, booyesqst, booloonkg, boodigit, boonoban)

  local varomongkosong = 0 -- for check against the ban list
  local numchiiar = 0
  local numukurran = 0
  local numindeex = 0 -- ZERO-based -- two loops
  local numadjlet = 0 -- number of adjacent letters (max 3)
  local numadjdsh = 0 -- number of adjacent dashes (max 1)
  local numtotdsh = 0 -- total number of dashes (max 2)
  local booislclc = false
  local booisdigi = false
  local booisdash = false
  local booisvaladv = true -- preASSume innocence -- later final verdict here

  while true do -- fake (outer) loop

    if (strqooq=="-") then
      booisvaladv = booyesdsh
      break -- to join mark -- good or bad
    end--if
    if (strqooq=="??") then
      booisvaladv = booyesqst
      break -- to join mark -- good or bad
    end--if
    numukurran = string.len (strqooq)
    if ((numukurran<2) or (numukurran>10)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if
    if (not booloonkg and (numukurran>3)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if

    numindeex = 0
    while true do -- genuine inner loop over char:s
      if (numindeex>=numukurran) then
        break -- done -- good
      end--if
      numchiiar = string.byte (strqooq,(numindeex+1),(numindeex+1))
      booisdash = (numchiiar==45)
      booisdigi = lfgtestnum(numchiiar)
      booislclc = lfgtestlc(numchiiar)
      if (not (booislclc or booisdigi or booisdash)) then
        booisvaladv = false
        break -- to join mark -- inherently bad char
      end--if
      if (booislclc) then
        numadjlet = numadjlet + 1
      else
        numadjlet = 0
      end--if
      if (booisdigi and ((numukurran~=3) or (numindeex~=1) or (not boodigit))) then
        booisvaladv = false
        break -- to join mark -- illegal digit
      end--if
      if (booisdash) then
        if ((numukurran<4) or (numindeex==0) or ((numindeex+1)==numukurran)) then
          booisvaladv = false
          break -- to join mark -- illegal dash
        end--if
        numadjdsh = numadjdsh + 1
        numtotdsh = numtotdsh + 1 -- total
      else
        numadjdsh = 0 -- do NOT zeroize the total !!!
      end--if
      if ((numadjlet>3) or (numadjdsh>1) or (numtotdsh>2)) then
        booisvaladv = false
        break -- to join mark -- evil
      end--if
      numindeex = numindeex + 1 -- ZERO-based
    end--while -- genuine inner loop over char:s

    if (not boonoban) then -- if "yesban" then
      numindeex = 0
      while true do -- genuine lower inner loop
        varomongkosong = contabisbanned[numindeex+1] -- number of elem unknown
        if (type(varomongkosong)~="string") then
          break -- abort inner loop (then fake outer loop) due to end of table
        end--if
        numukurran = string.len (varomongkosong)
        if ((numukurran<2) or (numukurran>3)) then
          break -- abort inner loop (then fake outer loop) due to faulty table
        end--if
        if (strqooq==varomongkosong) then
          booisvaladv = false
          break -- abort inner loop (then fake outer loop) due to violation
        end--if
        numindeex = numindeex + 1 -- ZERO-based
      end--while -- genuine lower inner loop
    end--if (not boonoban) then

    break -- finally to join mark
  end--while -- fake loop -- join mark

  return booisvaladv

end--function lfivalidatelnkoadv

------------------------------------------------------------------------

-- Local function LFIFILLNAME

-- Replace placeholder "\@" "\\@" by augmented name of the caller.

-- To be called ONLY from "lfhfillsurrstrtab".

-- The name of the caller is submitted to us as a parameter thus we
-- do NOT access any constants and do NOT have to peek it either.

local function lfifillname (strmessage, strcaller)

  local strhasill = ''
  local numstrloen = 0
  local numindfx = 1 -- ONE-based
  local numcjar = 0
  local numcjnext = 0

  numstrloen = string.len (strmessage)

  while true do
    if (numindfx>numstrloen) then
      break -- empty input is useless but cannot cause major harm
    end--if
    numcjar = string.byte (strmessage,numindfx,numindfx)
    numindfx = numindfx + 1
    numcjnext = 0 -- preASSume no char
    if (numindfx<=numstrloen) then
      numcjnext = string.byte (strmessage,numindfx,numindfx)
    end--if
    if ((numcjar==92) and (numcjnext==64)) then
      strhasill = strhasill .. strcaller -- invalid input is caller's risk
      numindfx = numindfx + 1 -- skip 2 octet:s of the placeholder
    else
      strhasill = strhasill .. string.char (numcjar)
    end--if
  end--while

  return strhasill

end--function lfifillname

------------------------------------------------------------------------

-- Local function LFIKODEOSG

-- Transcode eo X-surrogates to cxapeloj in a single string (eo only).

-- Input  : * streosurr -- ANSI string (empty is useless but cannot
--                                      cause major harm)

-- Output : * strutf8eo -- UTF8 string

-- Depends on functions :
-- [E] mathdiv mathmod

-- Depends on constants :
-- * table "contabtransluteo" inherently holy

-- To be called ONLY from "lfhfillsurrstrtab".

-- * the "x" in a surr pair is case insensitive,
--   for example both "kacxo" and "kacXo" give same result
-- * avoid "\", thus for example "ka\cxo" would get converted but the "\" kept
-- * double "x" (both case insensitive) prevents conversion and becomes
--   reduced to single "x", for example "kacxxo" becomes "kacxo"

local function lfikodeosg (streosurr)

  local vareopeek = 0
  local strutf8eo = ''
  local numeoinplen = 0
  local numinpinx = 0 -- ZERO-based source index
  local numknar0k = 0 -- current char
  local numknaf1x = 0 -- next char (ZERO is NOT valid)
  local numknaf2x = 0 -- post next char (ZERO is NOT valid)
  local boonext1x = false
  local boonext2x = false
  local boosudahdone = false

  numeoinplen = string.len(streosurr)

  while true do

    if (numinpinx>=numeoinplen) then
      break
    end--if

    numknar0k = string.byte(streosurr,(numinpinx+1),(numinpinx+1))
    numknaf1x = 0 -- preASSume no char
    numknaf2x = 0 -- preASSume no char
    if ((numinpinx+1)<numeoinplen) then
      numknaf1x = string.byte(streosurr,(numinpinx+2),(numinpinx+2))
    end--if
    if ((numinpinx+2)<numeoinplen) then
      numknaf2x = string.byte(streosurr,(numinpinx+3),(numinpinx+3))
    end--if

    boonext1x = ((numknaf1x==88) or (numknaf1x==120)) -- case insensitive
    boonext2x = ((numknaf2x==88) or (numknaf2x==120)) -- case insensitive
    boosudahdone = false
    if (boonext1x and boonext2x) then -- got "xx"
      strutf8eo = strutf8eo .. string.char(numknar0k,numknaf1x) -- keep one "x" only
      numinpinx = numinpinx + 3 -- eaten 3 written 2
      boosudahdone = true
    end--if
    if (boonext1x and (not boonext2x)) then -- got yes-"x" and no-"x"
      vareopeek = contabtransluteo[numknar0k] -- UINT16 or type "nil"
      if (type(vareopeek)=='number') then
        strutf8eo = strutf8eo .. string.char(mathdiv(vareopeek,256),mathmod(vareopeek,256)) -- add UTF8 char
        numinpinx = numinpinx + 2 -- eaten 2 written 2
        boosudahdone = true
      end--if
    end--if
    if (not boosudahdone) then
      strutf8eo = strutf8eo .. string.char(numknar0k) -- copy char
      numinpinx = numinpinx + 1 -- eaten 1 written 1
    end--if

  end--while

  return strutf8eo

end--function lfikodeosg

------------------------------------------------------------------------

---- HIGH LEVEL FUNCTIONS [H] ----

------------------------------------------------------------------------

-- Local function LFHREBUILDTABLE

-- Create a copy of a table, even nested.

-- A table from "mw.loadData" is static. It MUST NOT be unhogged, otherwise
-- mess can arise, and it MUST NOT be modified, violation gives an error.
-- Any subtable picked from a table from "mw.loadData" is still static, no
-- data has been copied. Function "mw.clone" applied to such a table crashes
-- on the spot instead of creating a copy.

local function lfhrebuildtable (vareniro)
  local vareliro = 0
  if (type(vareniro)=='table') then
    vareliro = {}
    for kkk,vvv in pairs (vareniro) do
      if (type(vvv)=='table') then
        vareliro[kkk] = lfhrebuildtable(vvv) -- RECURSION
      else
        vareliro[kkk] = vvv -- here COPY value of elem, NOT a "pointer" to it
      end--if
    end--for
  else
    vareliro = vareniro
  end--if
  return vareliro
end--function lfhrebuildtable

------------------------------------------------------------------------

-- Local function LFBREWERRINSI

-- Brew error message with insertable details severity huge.

-- Input  : * numerrorcodu
--          * tabdetails -- for example {[0]="none","neniu"}, one element
--                          per placeholder "\~" "\\~" at given error code

-- Depends on functions :
-- [N] lfnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * 3 strings constrelabg constrelaen constrlaxhu
-- * table contaberaroj TWO-based

-- #E02...#E99, note that #E00 and #E01 are NOT supposed to be included here.

-- Placeholder "\@" "\\@" is used elsewhere, here we use "\~" "\\~".

local function lfbrewerrinsi (numerrorcodu, tabdetails)

  local vardeeskrip  = 0 -- from error code
  local vardetaalo   = 0 -- from table with details coming from caller
  local stritsacx    = '#E'
  local strfilling   = ''
  local numkfrodmfn  = 0 -- len
  local numkfrodzzz  = 0 -- len
  local numsfrcindex = 0 -- char index
  local numinsrtinde = 0 -- index in table with details
  local numtecken0d  = 0
  local numtecken1d  = 0

  vardeeskrip = contaberaroj[numerrorcodu] -- risk of type "nil"

  if (type(vardeeskrip)=="string") then
    stritsacx = stritsacx .. lfnumto2digit(numerrorcodu) .. ' ' -- see above
    numkfrodmfn = string.len (vardeeskrip)
    while true do
      if (numsfrcindex>=numkfrodmfn) then
        break
      end--if
      numtecken0d = string.byte(vardeeskrip,(numsfrcindex+1),(numsfrcindex+1))
      numsfrcindex = numsfrcindex + 1 -- INC here
      numtecken1d = 0 -- preASSume
      if (numsfrcindex<numkfrodmfn) then -- pick but do NOT INC
        numtecken1d = string.byte(vardeeskrip,(numsfrcindex+1),(numsfrcindex+1))
      end--if
      if ((numtecken0d==92) and (numtecken1d==126)) then
        strfilling = '??' -- preASSume
        if (type(tabdetails)=="table") then
          vardetaalo = tabdetails [numinsrtinde] -- risk of type "nil"
          if (type(vardetaalo)=="string") then
            numkfrodzzz = string.len(vardetaalo)
            if ((numkfrodzzz~=0) and (numkfrodzzz<40)) then -- !!!FIXME!!! nowiki and other sanitization
              strfilling = vardetaalo
            end--if
          end--if
        end--if
        numsfrcindex = numsfrcindex + 1 -- INC more, now totally + 2
        numinsrtinde = numinsrtinde + 1 -- INC tab index on every placeholder
      else
        strfilling = string.char (numtecken0d)
      end--if
      stritsacx = stritsacx .. strfilling -- add one of 3 possible types
    end--while
  else
    stritsacx = stritsacx .. '??'
  end--if

  stritsacx = constrlaxhu .. constrelabg .. stritsacx .. constrelaen .. constrlaxhu

  return stritsacx

end--function lfbrewerrinsi

------------------------------------------------------------------------

-- Local function LFHFILLSURRSTRTAB

-- Process (fill in, transcode surr) either a single string, or all string
-- items in a table using any type of keys/indexes (such as a holy number
-- sequence and non-numeric ones). Items with a non-string value are kept
-- as-is. For filling in own name, and converting eo and sv surrogates
-- (via 3 separate sub:s).

-- Input  : * varinkommen -- type "string" or "table"
--          * varfyllo -- string, or type "nil" if no filling-in desired
--          * strlingkod -- "eo" or "sv" to convert surrogates, anything
--                          else (preferably type "nil") to skip this

-- Depends on functions :
-- [I] lfifillname (only if filling-in desired)
-- [I] lfikodeosg (only if converting of eo X-surrogates desired)
-- [I] lfikodsvsg (only if converting of sv blackslash-surrogates desired)
-- [E] mathdiv mathmod (via "lfikodeosg" and "lfikodsvsg")

-- Depends on constants :
-- * table "contabtransluteo" inherently holy (via "lfikodeosg")
-- * table "contabtranslutsv" inherently holy (via "lfikodsvsg")

local function lfhfillsurrstrtab (varinkommen, varfyllo, strlingkod)

  local varkey = 0 -- variable without type
  local varele = 0 -- variable without type
  local varutmatning = 0
  local boodone = false

  if (type(varinkommen)=='string') then
    if (type(varfyllo)=='string') then
      varinkommen = lfifillname (varinkommen,varfyllo) -- fill-in
    end--if
    if (strlingkod=='eo') then
      varinkommen = lfikodeosg (varinkommen) -- surr
    end--if
    if (strlingkod=='sv') then
      varinkommen = lfikodsvsg (varinkommen) -- surr
    end--if
    varutmatning = varinkommen -- copy, risk for no change
    boodone = true
  end--if

  if (type(varinkommen)=='table') then
    varutmatning = {} -- brew new table
    varkey = next (varinkommen) -- try to pick 0:th (in no order) key/index
    while true do
      if (varkey==nil) then
        break -- empty table or end reached
      end--if
      varele = varinkommen[varkey] -- pick element of unknown type
      if (type(varele)=='string') then
        varele = lfhfillsurrstrtab (varele, varfyllo, strlingkod) -- RECURSION
      end--if
      varutmatning[varkey] = varele -- write at same place in dest table
      varkey = next (varinkommen, varkey) -- try to pick next key/index
    end--while
    boodone = true
  end--if

  if (not boodone) then
    varutmatning = varinkommen -- copy as-is whatever it is
  end--if

  return varutmatning

end--function lfhfillsurrstrtab

------------------------------------------------------------------------

-- Local function LFHFINDPHASE

-- Input  : * numphase -- 1...6 or type "nil"
--          * numasciiphase -- ASCII code or type "nil"

-- Output : * vartbrezulto -- on success table with indexes 0...3
--                            [0] phase [1] letter [2] xx color [3] eo color

-- Depends on constants :
-- * nested table "contablimahuruf" index 1...6

-- Exactly ONE of the TWO available types of input hint must be provided.

local function lfhfindphase (numphase, numasciiphase)
  local varpickero = 0
  local vartbrezulto = 0 -- type "nil" if NOT found
  local numtabudex = 1 -- ONE-based
  while true do
    varpickero = contablimahuruf [numtabudex] -- risk of "nil"
    if (type(varpickero)~="table") then
      vartbrezulto = nil
      break -- end reached and NOT found
    end--if
    if ((numphase==numtabudex) or (numasciiphase==string.byte(varpickero[1]))) then
      vartbrezulto = varpickero -- table indexex 1...3
      vartbrezulto[0] = numtabudex -- add found phase index 1...6 to index 0
      break -- found
    end--if
    numtabudex = numtabudex + 1
  end--while
  return vartbrezulto
end--function lfhfindphase

------------------------------------------------------------------------

-- Local function LFHGETWARNA

-- Depends on functions :
-- [H] lfhfindphase

-- Depends on constants :
-- * nested table "contablimahuruf" index 1...6

local function lfhgetwarna (numfaazzo, boospecialeo)
  local tabtaabo = {}
  local strkolooro = ''
  tabtaabo = lfhfindphase (numfaazzo, nil) or {} -- by index -- table or "nil"
  if (boospecialeo) then
    strkolooro = tabtaabo[3] -- risk of "nil" -- only for "G" and -eo-
  else
    strkolooro = tabtaabo[2] -- risk of "nil"
  end--if
  if (type(strkolooro)~="string") then -- protect against stupidity
    strkolooro = '505050'
  end--if
  if (string.len(strkolooro)~=6) then -- protect against stupidity
    strkolooro = '505050'
  end--if

  return strkolooro
end--function lfhgetwarna

------------------------------------------------------------------------

-- Local function LFHANALPREF

-- Analyze and remove possible group prefixes and element prefixes.

-- Input  : * strelemento

-- Output : * strelemento : valid prefixes removed on success, emptied on err
--          * numzagrupfn : 0: OK and nothing found
--                          1...6: found "T" ... "C" |
--                          11: unknown letter |
--                          12: redundant prefix | 13: became empty
--          * numzaelemfn : 0: OK and nothing found
--                          1: found "%" vis only
--                          2: found "&" cat only

-- Depends on functions :
-- [H] lfhfindphase
-- [G] lfgtestuc

-- Depends on constants :
-- * nested table "contablimahuruf" index 1...6

-- here we do NOT bother about the order of groups -- T:1 D:2 G:3 F:4 S:5 C:6
-- uppercase letter + colon + space is an attempted group prefix
-- "%" show text do not categorize -- "&" categorize do not show text
-- combined prefix possible "D: & " in this order ie group then element
-- minimal remaining length after removing all prefixes is 2
-- error codes 11..13 are specific for this sub, no #E99 here

local function lfhanalpref (strelemento)

  local varlimarisk = 0
  local numllenq = 0
  local numa0a = 0
  local numa1a = 0
  local numa2a = 0
  local numzagrupfn = 0
  local numzaelemfn = 0
  local booremvd = false -- assign to true if any valid prefix found

  while true do -- genuine loop but no index

    booremvd = false -- reset to false on every iteration
    numllenq = string.len(strelemento)
    if (numllenq<2) then
      break -- no chance for a prefix
    end--if
    numa0a = string.byte (strelemento,1,1)
    numa1a = string.byte (strelemento,2,2)
    numa2a = 0 -- preASSume
    if (numllenq>2) then
      numa2a = string.byte (strelemento,3,3)
    end--if

    if (lfgtestuc(numa0a) and (numa1a==58) and (numa2a==32)) then -- upper ": "
      varlimarisk = lfhfindphase (nil, numa0a) -- by letter -- table or "nil"
      if (type(varlimarisk)~="table") then
        numzagrupfn = 11 -- unknown letter
        break
      end--if
      if ((numzagrupfn~=0) or (numzaelemfn~=0)) then -- no group after element
        numzagrupfn = 12 -- redundant or wrong order
        break
      end--if
      numzagrupfn = varlimarisk [0] -- eat it, number 1...6 guaranteed
      strelemento = string.sub(strelemento,4,-1) -- remove 3, risk of empty
      booremvd = true
    end--if

    if (((numa0a==37) or (numa0a==38)) and (numa1a==32)) then -- "%" or "&"
      if (numzaelemfn~=0) then -- element after group OK, redu still illegal
        numzagrupfn = 12 -- redundant
        break
      end--if
      numzaelemfn = numa0a - 36 -- eat it, 1 or 2 only
      strelemento = string.sub(strelemento,3,-1) -- remove 2, risk of empty
      booremvd = true
    end--if

    if (not booremvd) then
      break -- out of prefixes :-(
    end--if

  end--while -- genuine loop but no index

  if (string.len(strelemento)<2) then
    numzagrupfn = 13 -- need at least 2 octet:s left :-(
  end--if

  if (numzagrupfn>9) then
    strelemento = '' -- punishment on error
  end--if

  return strelemento, numzagrupfn, numzaelemfn

end--function lfhanalpref

------------------------------------------------------------------------

-- Local function LFHANALSUFF

-- Analyze and remove possible suffixes (joiner dash, abbreviation
-- colon with or without extra content, abbreviation dot).

-- Input  : * strelemitno
--          * tabpositivelist : for example {"ONE","TWO"} to allow
--                              "ONE:" "ONE:"... and "TWO:" "TWO:"...

-- Output : * numlokaaerr
--          * strelemitno : valid suffixes removed on success,
--                          string emptied on error
--          * booreqjoin  : "true" if joiner dash "-" found at end
--          * booindeedab : "true" if either type "3 UPPERCase + colon" or
--                          dot "." found at end
--          * strekstera  : extra content if available, colon ":" for
--                          "colon-type" without extra content

-- Two of three possible discoveries ("colon-type" and "dot-type")
-- are mutually exclusive, but joiner dash is independent.

-- Type "3 UPPERCase + colon" ("colon-type") is prioritized over dot
-- ("dot-type"), but only possible for whitelisted triples supplied
-- via "tabpositivelist". If not on the whitelist, then the "dot-type"
-- has a chance.

local function lfhanalsuff (strelemitno, tabpositivelist) -- !!!FIXME!!! colon NOT yet supp

  local varfromwhite = 0
  local strekstera = '' -- preASSume empty
  local numlokaaerr = 0 -- preASSume innocence
  local numleenj = 0
  local numb77b = 0
  local numwhiteindex = 1 -- ONE-based
  local booreqjoin = false
  local booindeedab = false

  numleenj = string.len(strelemitno)
  numb77b = string.byte(strelemitno,-1,-1)

  while true do -- fake loop

    if (numb77b==45) then -- joiner dash "-"
      if (numleenj<2) then
        numlokaaerr = 17 -- bad ie alone dash
        break
      end--if
      booreqjoin = true -- NOT further restricted by length of string !!!
      strelemitno = string.sub(strelemitno,1,-2) -- chop it off
      numb77b = string.byte(strelemitno,-1,-1)
    end--if

    while true do -- inner genuine loop
      varfromwhite = tabpositivelist[numwhiteindex]
      break -- !!!FIXME!!! colon here
    end--while

    if (numb77b==46) then -- abbreviation dot "."
      if ((numleenj<2) or (numleenj>12)) then
        numlokaaerr = 36 -- bad dot                         !!!FIXME!!! incomplete check limited set
        break
      end--if
      booindeedab = true
      strelemitno = string.sub(strelemitno,1,-2) -- chop it off
    end--if

    break -- finally to join mark
  end--while -- fake loop -- join mark

  if (numlokaaerr~=0) then -- return both empty on error
    strelemitno = ''
    strekstera = ''
  end--if

  return numlokaaerr, strelemitno, booreqjoin, booindeedab, strekstera

end--function lfhanalsuff

------------------------------------------------------------------------

-- Local function LFHWALLTOTBL

-- Convert one string with 1...6 wall-separated substrings into a table
-- with 1...6 elements at indexes 0...5. Empty substrings must use dash "-"
-- as a placeholder in the input if something useful is to follow, but the
-- complete input string may be shorter and contain less than the maximal
-- number of 5 walls. The earliest substring (index 0) is obligatory and
-- must not be omitted or skipped by a dash. Type "nil" is returned on error.

-- Input  : * strdinding

-- Valid:
-- "a"
-- "jamak sama|nomina jamak bentuk sama"
-- "jamak sama|nomina jamak bentuk sama|-" (but not preferred)
-- "jamak sama|nomina jamak bentuk sama|-|-|-|hej" (6 substrings, max)

-- Invalid:
-- ""
-- "-"
-- "-|bad too"
-- "jamak sama|nomina jamak bentuk sama|"
-- "jamak sama|nomina jamak bentuk sama|-|-|-|hej|nej" (too long with 6 walls)

local function lfhwalltotbl (strdinding)

  local tabnolempat = {[0]='', '', '', '', '', ''} -- index 0...5
  local vartmmp = 0
  local stronesub = ''
  local numinlenin = 0
  local numchrindexx = 0
  local numsubstrinx = 0
  local booisbad = false

  numinlenin = string.len(strdinding)
  booisbad = (numinlenin==0) or (numinlenin>240)

  while true do
    if (booisbad) then
      break
    end--if
    vartmmp = string.find (strdinding, "|", (numchrindexx+1), true) -- plain text search
    if (vartmmp==nil) then
      vartmmp = numinlenin -- ZERO-based last inclusive pos is end of string
    else
      vartmmp = vartmmp - 1 -- ZERO-based last inclusive pos
    end--if
    if (numchrindexx>=vartmmp) then -- nothing to pick after wall
      booisbad = true
      break
    end--if
    stronesub = string.sub (strdinding,(numchrindexx+1),vartmmp) -- at least 1
    if (stronesub=='-') then -- dash "-" here
      if (numsubstrinx==0) then
        booisbad = true
        break
      else
        stronesub = '' -- dash was placeholder for empty
      end--if
    end--if
    tabnolempat[numsubstrinx] = stronesub -- store it
    numsubstrinx = numsubstrinx + 1
    if (vartmmp==numinlenin) then
      break -- OK !!!
    end--if
    if (((vartmmp+1)==numinlenin) or (numsubstrinx==6)) then
      booisbad = true -- ends with wall, or 6 substrings done and no end yet
      break
    end--if
    numchrindexx = vartmmp + 1 -- at least 1 octet left !!!
  end--while

  if (booisbad) then
    tabnolempat = nil -- F**K
  end--if

  return tabnolempat

end--function lfhwalltotbl

------------------------------------------------------------------------

-- Local function LFHNILDASHTOEMPTY

local function lfhnildashtoempty (tabmasukkeluar, numfromin, numtoto)
  local varonepicked = 0
  while true do
    if (numfromin>numtoto) then
      break
    end--if
    varonepicked = tabmasukkeluar[numfromin]
    if ((varonepicked==nil) or (varonepicked=='-')) then
      tabmasukkeluar[numfromin] = ''
    end--if
    numfromin = numfromin + 1
  end--while
  return tabmasukkeluar
end--function lfhnildashtoempty

------------------------------------------------------------------------

-- Local function LFHTRANSLATETRLI

-- Translate T ie translingual stuff (intended for signs and symbols)
-- ie phase 1. Caller must care that the lang code is "mul" only.

-- Input  : * strmytrli

-- Output : * vartbtrlitrli : 5 strings at [0]...[4] and 1 boolean at
--                            [33] (always false here), or type "number"
--                            error code on error

-- Depends on functions :
-- [H] lfhwalltotbl

-- possible errors:
-- * 2 trli not found (peeking "contabtt" fails)
-- * 3 trli malformed (split via "lfhwalltotbl" fails)
-- * 4 control code used (it is prohibited here for "T")

local function lfhtranslatetrli (strmytrli)

  local vartbtrlitrli = 0
  local varpicktrli = 0
  local numlouklera = 0

  while true do -- fake loop
    varpicktrli = contabtt [strmytrli] -- risk of "nil"
    if (type(varpicktrli)~="string") then
      numlouklera = 2
      break
    end--if
    vartbtrlitrli = lfhwalltotbl (varpicktrli) -- risk of "nil"
    if (type(vartbtrlitrli)~="table") then
      numlouklera = 3
      break
    end--if
    varpicktrli = vartbtrlitrli[5] -- low risk of bad content
    vartbtrlitrli[5] = nil -- remove it
    if (varpicktrli~="") then -- it is completely prohibited for "T"
      numlouklera = 4
      break
    end--if
    vartbtrlitrli[33] = false -- always false for group "T"
    if (vartbtrlitrli[1]=='') then
      vartbtrlitrli[1] = vartbtrlitrli[0] -- this will probably be the rule
    end--if
    break -- finally to join mark
  end--while -- fake loop -- join mark

  if (numlouklera~=0) then
    vartbtrlitrli = numlouklera
  end--if

  return vartbtrlitrli -- type "table" or "number"

end--function lfhtranslatetrli

------------------------------------------------------------------------

-- Local function LFHPOSTPROCESSDIAL

-- Postprocess table for D ie dialect ie phase 2.

-- Input  : * tabdialdial : see below, 5 strings at [0]...[4],
--                          fool-proof ie non-table legal
--          * strbahanomo : lang name

-- Output : * numdialerror : error code
--          * tabdialdial : see below, 5 strings at [0]...[4]
--                          and one boolean at [33]

-- Depends on functions :
-- [H] lfhnildashtoempty

-- example of incoming table table picked by caller from
-- "loaddata-tbldialektoj" at index "en-ZA" :
-- [0] (visi)                    "Afrika Selatan
-- [1] (kato)                    "-"
-- [2]                           "-"
-- [3] (tooltip)                 "-"
-- [4] (image)                   "South_Africa_Flag.svg"
-- [5] (prohibited control code) nil

-- example of result based on table above and "strbahanomo" = "Inggris" :
-- [0] (visi)                    "<small>Inggris</small> Afrika Selatan"
-- [1] (kato)                    "Inggris Afrika Selatan"
-- [33]                          false

-- augmentation including "<small>" is applied only to the visible
-- text in [0] AFTER the raw content has been used to brew [1]

-- possible errors:
-- * 6 incoming table is not a table
-- * 7 control code used (it is prohibited here for "D")

local function lfhpostprocessdial (tabdialdial, strbahanomo)

  local numlocalerr = 0

  while true do -- fake loop
    if (type(tabdialdial)~='table') then
      tabdialdial = {}
      numlocalerr = 6
      break
    end--if
    tabdialdial = lfhnildashtoempty(tabdialdial,0,5) -- type "table" sure
    if (tabdialdial[5]~='') then -- residual risk of bad content
      tabdialdial = {} -- value here completely prohibited for "D"
      numlocalerr = 7
      break
    end--if
    tabdialdial[33] = false -- always false for group "D"
    if (tabdialdial[1]=='') then
      tabdialdial[1] = strbahanomo .. ' ' .. tabdialdial[0] -- the common case
    end--if
    tabdialdial[0] = '<small>' .. strbahanomo .. '</small> ' .. tabdialdial[0] -- special rule
    break -- finally to join mark
  end--while -- fake loop -- join mark

  return numlocalerr, tabdialdial

end--function lfhpostprocessdial

------------------------------------------------------------------------

-- Local function LFHTRANSLATELAIN

-- Translate G F S C ie phase 3 to 6.

-- Input  : * strmylain
--          * numfazo : 3 ... 6 -- nope-T nope-D G F S C

-- Output : * tablainlain : 5 strings at [0]...[4] and 1 boolean at
--                          [33], or type "number" error code on error

-- Depends on functions :
-- [H] lfhwalltotbl

-- possible errors:
-- * 2 abbre not found (peeking "contab-gfsc" fails)
-- * 3 abbre malformed (split via "lfhwalltotbl" fails)
-- * 4 optional control code faulty (different from "" or "1")

local function lfhtranslatelain (strmylain, numfazo)

  local vartblainlain = 0
  local varpicklain = 0
  local numlokalera = 0

  while true do -- fake loop
    if (numfazo==3) then
      varpicklain = contabgg [strmylain] -- risk of "nil"
    end--if
    if (numfazo==4) then
      varpicklain = contabf [strmylain] -- risk of "nil"
    end--if
    if (numfazo==5) then
      varpicklain = contabs [strmylain] -- risk of "nil"
    end--if
    if (numfazo==6) then
      varpicklain = contabc [strmylain] -- risk of "nil"
    end--if
    if (type(varpicklain)~="string") then
      numlokalera = 2
      break
    end--if
    vartblainlain = lfhwalltotbl (varpicklain) -- risk of "nil"
    if (type(vartblainlain)~="table") then
      numlokalera = 3
      break
    end--if
    varpicklain = vartblainlain[5] -- low risk of bad content
    vartblainlain[5] = nil -- remove it
    if ((varpicklain~="") and (varpicklain~="1")) then
      numlokalera = 4
      break
    end--if
    vartblainlain[33] = (varpicklain~="1") -- usually true, false possible
    if (vartblainlain[1]=='') then
      vartblainlain[1] = vartblainlain[0] -- this will probably be the rule
    end--if
    break -- finally to join mark
  end--while -- fake loop -- join mark

  if (numlokalera~=0) then
    vartblainlain = numlokalera
  end--if

  return vartblainlain -- type "table" or "number"

end--function lfhtranslatelain

------------------------------------------------------------------------

---- VARIABLES [R] ----

------------------------------------------------------------------------

function exporttable.ek (arxframent)

  -- general unknown type

  local vartmp = 0       -- variable without type multipurpose

  -- special type "args"

  local arxsomons = 0    -- metaized "args" from our own or caller's "frame"

  -- general tab

  local tablg76yleft = {}  -- lang
  local tabdk78ysubt = {}  -- dial
  local tabx24x = {}       -- chain from arxsomons[2] ... arxsomons[25]
  local taberrdetail = {}  -- sent to "lfbrewerrinsi"

  -- general str

  local strkodbah  = ''  -- language code (2 or 3 lowercase) from arxsomons[1]
  local strpagenam = ''  -- from "{{PAGENAME}}" or "pagenameoverridetestonly"
  local strnambah  = ''  -- language name (without prefix "Bahasa")
  local strwarna    = ''  -- raw 6 hex digits
  local strshortnam = ''  -- name of templat no quot no NS prefix <<doit>>
  local strloongnam = ''  -- name of templat long yes quot <<"Template:doit">>

  local strelvisi  = ''  -- NOT yet UPPERcased (nurplurala, fiziko, vulgara)
  local strelkato  = ''  -- no "<small>" here !!!
  local strellink  = ''  -- "w:" permitted, wall NOT permitted
  local streltool  = ''  -- tooltip
  local strilimag  = ''  -- image name

  local strtmp     = ''  -- temp

  local strvisgud  = ''  -- visible good output
  local strinvkat  = ''  -- invisible category part
  local strviserr  = ''  -- visible error message
  local strtrakat  = ''  -- invisible tracking categories
  local strret     = ''  -- final result string

  -- general num

  local numerr    = 0  -- 1 in 2 pa 3 sub 4 neva 5 neko 6 res 7 gene ...
  local numjumele = 0  -- number of elements (1...24) in chain
  local numerrpos = 0  -- posi of error in elements (1...24) ie NOT octet:s
  local numtamp   = 0
  local num2statcode = 0

  -- general boo

  local boonocat  = false  -- from "nocat=true"
  local boonokta  = false  -- from "R" or "M" global prefix

  local booismini = false  -- "false" for full or "R" | "true" for "M" minimal
  local booabbre  = false  -- is abbreviation element (colon-type or dot-type)
  local boobrewkt = false  -- brew 1 or 2 cat:s
  local boobrewld = false  -- brew language-dependent cat too
  local booshowvi = false  -- show visible text for element

------------------------------------------------------------------------

---- MAIN [Z] ----

------------------------------------------------------------------------

  ---- GUARD AGAINST INTERNAL ERROR AGAIN ----

  -- later reporting of #E01 must NOT depend on uncommentable strings

  if (qbooguard) then
    numerr = 1 -- #E01 internal
  end--if

  lftracemsg ('This is "mtagg", requested "detrc" report')
  lfdshowvar (conbookodlng,'conbookodlng')
  lfdshowvar (conboomiddig,'conboomiddig')
  lfdshowvar (numerr,'numerr','should be ZERO')

  ---- BOTHER WITH NAME OF INTENDED CALLER ----

  strshortnam = contabkoll [2] -- no quot no NS prefix <<doit>>
  strloongnam = '"' .. contabkoll [1] .. contabkoll [2] .. '"'

  lfdshowvar (strloongnam,'strloongnam')

  ---- PROCESS ERROR MESSAGES, FILL IN ALWAYS, CONVERT EO ONLY IF NEEDED ----

  -- placeholder "\@" "\\@" is replaced by augmented name of the caller
  -- from "strloongnam" in any case, for example <<"SXablono:test">> or
  -- <<"Templat:test">>, only for EO the X-substitution is subsequently done

  if (numerr==0) then
    contaberaroj = lfhfillsurrstrtab (contaberaroj, strloongnam, constrpriv)
    contabtt     = lfhfillsurrstrtab (contabtt, nil, constrpriv)
    contabgg     = lfhfillsurrstrtab (contabgg, nil, constrpriv)
    contabtrako  = lfhfillsurrstrtab (contabtrako, nil, constrpriv)
  end--if

  lftracemsg ('Conversion and filling done')

  ---- GET THE ARX (ONE OF TWO) ----

  -- must be seized independently on "numerr" even if we already suck

  arxsomons = arxframent.args -- "args" from our own "frame"
  if (type(arxsomons)~="table") then
    arxsomons = {} -- guard against indexing error
    numerr = 1 -- #E01 internal
  end--if
  if (arxsomons['caller']=="true") then
    arxsomons = arxframent:getParent().args -- "args" from caller's "frame"
  end--if
  if (type(arxsomons)~="table") then
    arxsomons = {} -- guard against indexing error again
    numerr = 1 -- #E01 internal
  end--if

  ---- PICK TWO SUBTABLES ----

  -- on error we assign "numerr" and "num2statcode" both used far below

  while true do -- fake loop

    if (numerr~=0) then
      break -- to join mark
    end--if

    num2statcode = qldingvoj[2] -- from "loaddata-tbllingvoj"
    if (type(num2statcode)~='number') then -- important check
      numerr = 3 -- #E03 malica !!!FIXME!!! should be #E02
      break -- to join mark
    end--if
    if (num2statcode~=0) then
      if (mathisintrange(num2statcode,2,79)) then
        numerr = 3 -- #E03 nombrigita
      else
        numerr = 3 -- #E03 malica !!!FIXME!!! should be #E02
      end--if
      break -- to join mark
    end--if
    tablg76yleft = qldingvoj['T76']
    if (type(tablg76yleft)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if

    num2statcode = qldlektoj[2] -- from "loaddata-tbldialektoj"
    if (type(num2statcode)~='number') then -- important check
      numerr = 3 -- #E03 malica !!!FIXME!!! should be #E02
      break -- to join mark
    end--if
    if (num2statcode~=0) then
      if (mathisintrange(num2statcode,2,79)) then
        numerr = 3 -- #E03 nombrigita
      else
        numerr = 3 -- #E03 malica !!!FIXME!!! should be #E02
      end--if
      break -- to join mark
    end--if
    tabdk78ysubt = qldlektoj['T78']
    if (type(tabdk78ysubt)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if

    break -- finally to join mark
  end--while -- fake loop -- join mark

  ---- SEIZE 2...25 ANON PARAMETERS ----

  -- in arxsomons[1] expecting lang code
  -- in arxsomons[2]...arxsomons[25] expecting main control chain

  -- result in "strkodbah" will be later fed into "xxx"
  -- chain goes into "tabx24x" with prevalidation
  -- #E02 wrong number of params or wrong length of string
  -- #E04 bad langcode
  -- #E06 reserved characters !!!FIXME!!!

  if (numerr==0) then
    if ((arxsomons[1]==nil) or (arxsomons[2]==nil) or (arxsomons[26])) then
      numerr = 2 -- #E02 -- need 2 ... 25 anon params, NOT more
    else
      strkodbah = arxsomons[1] -- y-index word (lang code)
      if (not lfivalidatelnkoadv(strkodbah,false,false,conbookodlng,conboomiddig,false)) then
        numerr = 4 -- #E04 -- evidente nevalida
      end--if
    end--if
  end--if

  if (numerr==0) then

    do -- scope
    local numindxxi = 0
    local numjjlenj = 0
    local varjjtmpj = ''

    while true do
      varjjtmpj = arxsomons[numindxxi+2] -- TWO-based
      if (type(varjjtmpj)~="string") then
        if (numindxxi==0) then
          numerr = 2 -- #E02 -- we need at least one more
        end--if
        break -- abort with success or failure
      end--if
      numjjlenj = string.len(varjjtmpj)
      if ((numjjlenj<2) or (numjjlenj>50)) then
        numerr = 2 -- #E02
        break
      end--if
      tabx24x [numindxxi] = varjjtmpj -- ZERO-based
      numindxxi = numindxxi + 1
    end--while
    if (numerr==0) then
      numjumele = numindxxi -- 1...24
    end--if

    end--do scope

  end--if

  lfdshowvar (strkodbah,'strkodbah','language code')
  lfdshowvar (numjumele,'numjumele','number of elements')
  lfdshowvar (tabx24x,'tabx24x',nil,26)
  lfdshowvar (numerr,'numerr','after seizure of anon params')

  ---- PROCESS 3 HIDDEN NAMED PARAMS INTO 1 STRING AND 2 BOOLEAN:S ----

  -- this may override "mw.title.getCurrentTitle().text" and
  -- stipulate content in "strpagenam", empty is NOT valid

  -- bad "pagenameoverridetestonly=" can give #E01
  -- no error is possible from other hidden parameters

  -- "detrc=" and "nocat=" must be seized independently on "numerr"
  -- even if we already suck, but type "table" must be ensured !!!

  strpagenam = ''
  if (numerr==0) then
    vartmp = arxsomons['pagenameoverridetestonly']
    if (type(vartmp)=="string") then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartmp -- empty is not legal
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  if (arxsomons['nocat']=='true') then
    boonocat = true
  end--if

  if (arxsomons["detrc"]=="true") then
    lftracemsg ('Param "detrc=true" seized')
  else
    qboodetrc = false -- was preassigned to "true"
    qstrtrace = '' -- shut up now
  end--if

  lfdshowvar (numerr,'numerr','done with hidden params')
  lfdshowvar (boonocat,'boonocat')

  ---- SEIZE THE PAGENAME FROM MW ----

  -- later reporting of #E01 must NOT depend on uncommentable strings

  -- must be 1...120 octet:s keep consistent with "pagenameoverridetestonly="

  if ((numerr==0) and (strpagenam=='')) then
    vartmp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (type(vartmp)=="string") then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartmp -- pagename here (empty is NOT legal)
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  ---- STRICTLY CHECK THE PAGENAME ----

  -- for example "o'clock" is legal and "o'clock's" is legal too
  -- but "o''clock" is a crime

  if (numerr==0) then
    if (strpagenam=='') then
      numerr = 1 -- #E01 internal
    else
      if (lfibanmulti("'1[0]0{0}0",strpagenam)) then -- returns true if evil
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  lfdshowvar (numerr,'numerr','checked pagename')

  ---- WHINE IF YOU MUST #E01 ----

  -- reporting of this error #E01 must NOT depend on
  -- uncommentable strings as "contabkoll" and "contaberaroj"

  -- do NOT use sub "brewerr", report our name (NOT of template) and in EN

  if (numerr==1) then
    strtmp = '#E01 Internal error in module "mtagg".'
    strviserr = constrlaxhu .. constrelabg .. strtmp .. constrelaen .. constrlaxhu
  end--if

  ---- PEEK THE LANGUAGE NAME ----

  -- for lang name in site language (/c0/) peeked from "tablg76yleft":
  -- * type "nil" can become #E05 (unknown code) if the site
  --   language code works, otherwise #E03 (broken submodule)
  -- * "-" is unconditionally evil with #E03 (broken submodule)

  -- here we depend on "constrpriv" and submodule "loaddata-tbllingvoj"

  if (numerr==0) then

    while true do -- fake loop
      vartmp = tablg76yleft[strkodbah]
      if (type(vartmp)~='string') then
        if (type(tablg76yleft[constrpriv])=='string') then
          numerr = 5 -- #E05 unknown code given (since site code works)
        else
          numerr = 3 -- #E03 broken submodule (site code does NOT work either)
        end--if
        break
      end--if
      if (string.len(vartmp)<2) then -- less than 2 letters is not legal
        numerr = 3 -- #E03 broken submodule
        break
      end--if
      strnambah = vartmp -- got lang name :-)
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if

  lfdshowvar (strnambah,'strnambah','peeked lang name via T76')
  lfdshowvar (numerr,'numerr')

  ---- PARSE THE CONTROL CHAIN ----

  -- table "tabx24x" is guaranteed to contain at least one element at index 0
  -- the stipulated order is D:1 G:2 F:3 S:4 C:5 -- ZERO means no phase yet
  -- fill "strvisgud" and "strinvkat" here
  -- "%" show text do not categorize -- "&" categorize only do not show text
  -- combined prefix possible "D: & " in this order group -> element
  -- from "lfhanalpref" : 0 nope | 1..5 "D" ... "C" | 11 unk | 12 red | 13 emp
  --                      0 nope | 1 found "%" ("nopercatcent") vis only
  --                             | 2 found "&" cat only

  -- the 5 components are "strelvisi", "strelkato", "strellink",
  --                      "streltool", "strilimag"

  -- "[[File:Omong_Kosong.png|20px|link=]]" note that "link=" needs PD image

  -- there are 4 ways to suppress lemma categorization:
  -- * full suppression via "nocat=true" into "boonocat = true" (suppresses
  --   also tracking categories)
  -- * full suppression via "R" or "M" global prefix into "boonokta = true"
  -- * full suppression via prefix "%" AKA "nopercatcent" in a plain
  --   or abbreviation element (via numanlelpr==1) into "boobrewkt = false"
  -- * partial suppression via control code "1" in an abbreviation
  --   element into "boobrewld = false"

  if (numerr==0) then

    do -- scope
      local tabkompon = {}
      local strelemin = ''
      local strexxtra = '' -- autogenerated or explicit
      local strdialkey = ''
      local strspankolor = ''
      local numphaase = 0 -- group we are in, start from ZERO
      local numsrcpoi = 0 -- index
      local numanlgrpr = 0
      local numanlelpr = 0 -- 0 or 1 or 2 current
      local numprvelpr = 0 -- 0 or 1 or 2 previous
      local numoct    = 0
      local numerakod = 0 -- local error code
      local boojoinrekv = false -- join request copied into "boodojoin"
      local boodojoin = false

      while true do

      if (numsrcpoi==numjumele) then -- at least ONE iteration
        break
      end--if
      strelemin = tabx24x [numsrcpoi] -- type "nil" impossible

      lftracemsg ('Picked one element of the control chain')
      lfdshowvar (numsrcpoi,'numsrcpoi')
      lfdshowvar (strelemin,'strelemin')

      if (numsrcpoi==0) then

        numoct = string.byte(strelemin,1,1) -- length prevalidated 2...50
        if (numoct==82) then
          boonokta = true -- got "R"
          strelemin = string.sub(strelemin,2,-1)
        end--if
        if (numoct==77) then
          boonokta = true -- got "M"
          booismini = true -- !!!FIXME!!! implementation non-existent
          strelemin = string.sub(strelemin,2,-1)
        end--if

        lftracemsg ('Inspected and eaten possible special global ZERO-position prefix')
        lfdshowvar (strelemin,'strelemin','again')
        lfdshowvar (boonokta,'boonokta')
        lfdshowvar (booismini,'booismini')

      end--if

      strelemin, numanlgrpr, numanlelpr = lfhanalpref (strelemin)

      lftracemsg ('Chopped off possible prefixes')
      lfdshowvar (strelemin,'strelemin','again')
      lfdshowvar (numanlgrpr,'numanlgrpr')
      lfdshowvar (numanlelpr,'numanlelpr','tristate, ONE of ONE crucial for join')

      if (numanlgrpr==11) then
        numerr = 8 -- #E08 bad group code
        break
      end--if
      if (numanlgrpr==12) then
        numerr = 9 -- #E09 redu or invalid group prefix
        break
      end--if
      if (numanlgrpr==13) then
        numerr = 7 -- #E07 generic (string became too short)
        break
      end--if
      if (numanlgrpr~=0) then
        if (numanlgrpr<numphaase) then
          numerr = 10 -- #E10 wrong order of group prefixes
          break
        end--if
        if (numanlgrpr==numphaase) then
          numerr = 11 -- #E11 repetitive group prefix
          break
        end--if
        numphaase = numanlgrpr -- OK, got a new group
        boodojoin = false -- reset join request !!!FIXME!!! should be an error ??
      end--if
      if (numphaase==0) then -- this must be below "numphaase = numanlgrpr" !!!
        numerr = 12 -- #E12 element outside of group
        break
      end--if

      numerakod, strelemin, boojoinrekv, booabbre, strexxtra = lfhanalsuff (strelemin,{"VRF","VRG","VRH"})

      lftracemsg ('Chopped off possible suffixes')
      lfdshowvar (numerakod,'numerakod')
      lfdshowvar (strelemin,'strelemin','again and maybe reduced')
      lfdshowvar (boojoinrekv,'boojoinrekv')
      lfdshowvar (booabbre,'booabbre')
      lfdshowvar (strexxtra,'strexxtra')

      if (boodojoin) then
        if (boojoinrekv) then
          lftracemsg ('Both are true, #E27')
          numerr = 27 -- #E27 cannot join more than 2 elements
          break
        end--if
        if (not (((numprvelpr==0) and (numanlelpr==1)) or ((numprvelpr==1) and (numanlelpr==0)))) then
          lftracemsg ('NOT exactly ONE "%", #E27')
          numerr = 27 -- #E27 exactly ONE of them must have "%", prohibit "&"
          break
        end--if
      end--if

      if ((numanlelpr~=0) and booabbre and (strexxtra~='')) then
        lftracemsg ('Element prefix and colon-type, #E13')
        numerr = 13 -- #E13 do not combine like that
        break
      end--if

      -- !!!FIXME!!! #E25 failed auto

      if ((numphaase==1) and (strkodbah~="mul")) then -- "T"
        numerr = 14 -- #E14 group "T" requires "mul"
        break
      end--if

      if (booabbre) then
        if (numphaase==1) then -- "T"
          lfdshowvar (strelemin,'strelemin','before T-translation')
          tabkompon = lfhtranslatetrli (strelemin)
          lfdshowvar (tabkompon,'tabkompon','after T-translation')
          if (type(tabkompon)~="table") then
            numerr = 20 -- #E20 unknown translang  !!!FIXME!!! use local error code returned
            break
          end--if
        end--if
        if (numphaase==2) then -- "D"
          strdialkey = strkodbah .. '-' .. strelemin
          lfdshowvar (strdialkey,'strdialkey','before D-translation')
          tabkompon = lfhrebuildtable (tabdk78ysubt [strdialkey]) -- risk of "nil"
          lfdshowvar (tabkompon,'tabkompon','after D-translation')
          numerakod, tabkompon = lfhpostprocessdial (tabkompon, strnambah)
          lfdshowvar (tabkompon,'tabkompon','after D-postprocessing')
          if (numerakod~=0) then
            numerr = 21 -- #E21 unknown dialect  !!!FIXME!!! local code 6 vs local code 7
            break
          end--if
        end--if
        if (numphaase>2) then -- G F S C
          lfdshowvar (strelemin,'strelemin','before GFSC-translation')
          tabkompon = lfhtranslatelain (strelemin,numphaase)
          lfdshowvar (tabkompon,'tabkompon','after GFSC-translation')
          if (type(tabkompon)~="table") then
            numerr = 23 -- #E23 unknown 4 group !!!FIXME!!! use local error code returned
            break
          end--if
        end--if
        strelvisi = tabkompon[0] -- translated abbreviation element
        strelkato = tabkompon[1] -- same or different here
        strellink = tabkompon[2]
        streltool = tabkompon[3]
        strelimag = tabkompon[4]
        boobrewkt = (numanlelpr~=1) -- false for "%" ("nopercatcent") vis only
        boobrewld = tabkompon[33]
        booshowvi = (numanlelpr~=2) -- false for "&" cat only
      else
        strelvisi = strelemin -- plain text element
        strelkato = strelemin -- always same here
        strellink = '' -- inherently impossible
        streltool = '' -- inherently impossible
        strelimag = '' -- inherently impossible
        boobrewkt = (numanlelpr~=1)
        boobrewld = true -- true here (false for T and D see below)
        booshowvi = (numanlelpr~=2)
      end--if

      if ((numphaase==1) or (numphaase==2)) then
        boobrewld = false -- always false for groups T and D
      end--if

      if (boobrewkt and (not boonocat) and (not boonokta)) then -- do 1 or 2 cats from "strelkato"
        strtmp = lfucasestr(strelkato,true,false,2) -- make upper, only one char, set -eo-
        strinvkat = strinvkat .. '[[' .. constrkatp .. strtmp .. ']]'
        if (boobrewld) then
          strinvkat = strinvkat .. '[[' .. constrkatp .. strtmp .. ' (' .. strnambah .. ')]]'
        end--if
      end--if

      if (booshowvi) then -- do show text from "strelvisi" from above and more
        strwarna = lfhgetwarna (numphaase,((strkodbah=="eo") and (numphaase==3))) -- only by index
        strspankolor = '<span style="background-color:#' .. strwarna .. '>'
        if (strellink~='') then
          strelvisi = '[[' .. strellink .. '|' .. strelvisi .. ']]'
        end--if
        strelvisi = strspankolor .. '<i>' .. strelvisi .. '</i></span>'
        if (strelimag~='') then
          strelvisi = '&nbsp;[[File:'.. strelimag ..'|20px]]&nbsp;&nbsp;' .. strelvisi
        end--if
        if (strvisgud~='') then
          if (boodojoin) then
            strvisgud = strvisgud .. strspankolor .. ' ' .. '</span>' -- separate them by coloured space only
          else
            strvisgud = strvisgud .. '; ' -- separate them harder and interrupt color
          end--if
        end--if
        strvisgud = strvisgud .. strelvisi
      end--if

        boodojoin = boojoinrekv -- join request applies to following element
        numprvelpr = numanlelpr -- and crucial check of "%" will be done there
        numsrcpoi = numsrcpoi + 1

      end--while

      numerrpos = numsrcpoi -- posi into more durable var, used on error only

    end--do scope

  end--if (numerr==0) then

  if ((numerr==0) and (strvisgud~='')) then
    strvisgud = '(' .. strvisgud .. ')'
  end--if

  ---- BREW TRACKING CAT:S #E02...#E99 ----

  -- no tracking cat:s for #E01
  -- "nocat=true" suppresses even tracking cat:s  !!!FIXME!!!
  -- special handling of #E04 evid and #E05 neko
  -- here we use "contabtrako" 0...5
  -- here we use "contabkoll" via "strshortnam" name of calling template
  -- here we use "strkodbah"

  if ((numerr>1) and (not boonocat)) then
    if ((numerr==4) or (numerr==5)) then
      if (numerr==4) then
        strtmp = contabtrako [1] -- #E04 evid
      else
        strtmp = contabtrako [2] -- #E05 neko
      end--if
      strtmp = '[[' .. constrkatp .. strtmp .. ' ' .. contabtrako [3] -- [3] is term for "language code"
      strtrakat = strtmp .. ']]' -- generic #E04 #E05
      strtrakat = strtrakat .. strtmp .. ' ' .. contabtrako [4] .. ' (' .. strshortnam .. ')]]' -- loke
      strtrakat = strtrakat .. strtmp .. ' ' .. contabtrako [5] .. ' (' .. strkodbah .. ')]]' -- nome
    else
      strtmp = '[[' .. constrkatp .. contabtrako [0] -- [0] is term for "bad use of template"
      strtrakat = strtmp .. ']]' -- generic other errors #E02 #E03 E#06 ...
      strtrakat = strtrakat .. strtmp .. ' (' .. strshortnam .. ')]]' -- specific
    end--if
  end--if

  ---- WHINE IF YOU MUST #E02...#E99 ----

  -- reporting of errors #E02...#E99 depends on uncommentable strings
  -- and name of the caller filled in from "contabkoll" in "lfhfillsurrstrtab"

  -- some of them need "numerrpos" via "taberrdetail"

  if (numerr>1) then -- NOT for #E01
    if (numerr==8) then
      taberrdetail = {[0]=tostring(numerrpos+2)} -- #E08
    end--if
    strviserr = lfbrewerrinsi (numerr,taberrdetail)
  end--if

  ---- RETURN THE JUNK STRING ----

  -- on #E02 and higher we risk partial results in "strvisgud" and "strinvkat"

  lftracemsg ('Ready to return string glued together from 1 + 4 parts')
  lfdshowvar (strvisgud,'strvisgud')
  lfdshowvar (strinvkat,'strinvkat')
  lfdshowvar (strviserr,'strviserr')
  lfdshowvar (strtrakat,'strtrakat')

  if (numerr==0) then
    strret = strvisgud .. strinvkat
  else
    strret = strviserr .. strtrakat
  end--if
  if (qboodetrc) then -- "qstrtrace" declared separately outside main function
    strret = "<br>" .. qstrtrace .. "<br><br>" .. strret
  end--if
  return strret

end--function

  ---- RETURN THE JUNK TABLE ----

return exporttable