Modulo:mlili

El Vikivortaro
Salti al navigilo Salti al serĉilo
 MODULO
Memtesto disponeblas sur la dokumentaĵa subpaĝo.
Ĉ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 "MLILI" (lingvokodo ligilo)

"eo.wiktionary.org/wiki/Modulo:mlili" <!--2022-Mar-10-->

Purpose: converts language code to language name (with some bonus features)
         and optionally brews a link according to one of many available types

Utilo: konvertas lingvokodon al lingvomono (kun kelkaj bonusaj funkcioj)
       kaj opcie kreas ligilon laux unu inter multaj disponeblaj tipoj

Manfaat: mengonversi kode bahasa ke nama bahasa (dengan ...)

Syfte: konverterar spraakkod till spraakets namn (med ...)

Used by templates / Uzata far sxablonoj:
- only "lili" (not to be called from any other place)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- "mpiktbllki" in turn requiring "mbllingvoj" (EO) or "mtblbahasa" (ID)
- "mtbllingvoj" in turn requiring template "tbllingvoj" (EO)
- "mtblbahasa" in turn requiring template "tblbahasa" (ID)

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. Empty parameters and parameters longer than 160
octet:s are (except "noc=") inherently invalid (#E02), further
checks follow.

Parameters: (1...10)
            - 1 anonymous obligatory parameter
              - language code
            - 0...9 named optional parameters
              - "tip=" - base type of output or link, 3 possible values: "LA",
                         "LK" (visible link to category), "KE" (adding page
                         into category), default is "raw" plain text no link
              - "nem=" - 1 possible value: "1", avoid wall in the link, show
                         it plain and honest, default is wall with type
                         "LA" or "LK" but not with "KE", only legal with
                         base type "LA" or "LK" (otherwise error), not
                         together with "mon=" or "maj=1" (otherwise error)
              - "mon=" - type of language name, 4 possible values: "la" (add
                         "la" if appropriate), "aj" (brew adjective), "sf"
                         ("senfinajxa" ie without ending for example "kore",
                         does not work with all languages), "av" (brew adverb,
                         if one word then this is "mon=" type "sf" plus "e",
                         else this is string "en" plus space + "mon=" type
                         "la"), "pl" (brew plural adjective, works for sane
                         multiword language names too), default is native
                         ie SB or AJ and no "la", only legal
                         with base type "LA" or "LK" or "raw"
                         (otherwise error), not together with "nem=1" or
                         "prf=" (otherwise error)
              - "maj=" - 1 possible value: "1", force begin uppercase, default
                         false ie native case for "mon=" types native and
                         "la", and lowercase for "mon=" types "aj" "sf"
                         "av", only legal with base type "LA" or "LK" or
                         "raw" (otherwise error), not together
                         with "nem=1" (otherwise error)
              - "hin=" - string (2...80 octet:s), name of category, language
                         name will become the sorting hint/key all lowercase
                         and without spaces and dashes, name of category
                         is fixed, default no hint, only legal with base
                         type "KE" (otherwise error), not together with
                         "prf=" (otherwise error)
              - "prf=" - string (2...80 octet:s), add prefix before language
                         name and brackets, default no prefix, only legal with
                         base type "LA" or "LK" or "KE" (otherwise error), not
                         together with "mon=" or "hin=" (otherwise error)
              - "noc=" - 1 possible value: "true" (any other value ignored),
                         "true" causes empty result with "KE" ie suppresses
                         categorization, only useful with base type "KE"
                         (otherwise ignored), forward "nocat=" to this
                         one if desired
              - "per=" - 1 possible value: "1", keep link or category if
                         language code is bad (obviously invalid or unknown),
                         default is off and means to switch base
                         type "LA" or "LK" to type "raw", base
                         type "KE" to empty result, only legal with
                         base type "LA" or "LK" or "KE" (otherwise error)
              - "err=" - string (2...80 octet:s), common placeholder, by
                         default the errors are "evidente nevalida lingvokodo"
                         and "nekonata lingvokodo"

* parameter "noc=" cannot cause an error
* parameters "mon=" "maj=1" and on the other side "hin=" indirectly exclude
  each other absolutely via the base type
* for base type "KE" the language can be in the catlink target or in the
  hint, but not in both
* for types "LA" and "LK" the language can be in the single common field
  (with "nem=1"), or in both, but separately processed ("mon=" and "maj=1
  affect only the visible part)
* wikilink target or category name for "LA" "LK" "KE" always gets
  uppercase beginning letter
* sorting hint/key is always converted to lowercase (all letters) and junk
  char:s (particularly spaces and dashes) are removed
* if language code is bad (obviously invalid or unknown) then by default
  both "tip=" and "mon=" are force-switched to default values "raw" and
  "native", yet with "per=1" the result is constructed with "tip="
  unaffected but "mon=" still force-switched to "native"

Returned: - one moderately long string with raw name or link, can be empty
            in some cases, or red error message

This module is unbreakable (when called with correct module name
and function name). Every imaginable input from the caller and
from the imported modules will output either a useful result or
at least a helpful 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 importataj moduloj eldonos aux utilan rezulton aux
almenaux helpeman eraranoncan signocxenon.

Following errors are possible:
- <<#E01 Internal error in module "mlili">>
  Possible causes:
  - strings not uncommented
- <<#E02 Erara uzo de sxablono "lili", legu gxian dokumentajxon>>
  Possible causes (early detected obvious problems with parameters):
  - one obligatory anonymous parameter missing
  - more than one anonymous parameter supplied
  - empty parameters or parameters longer than 160 octet:s (except "noc=")
- <<#E03 Eraro en subsxablonoj uzataj far sxablono "lili">>
  Possible causes:
  - submodule failure (or not found ??)
- <<#E04 Nevalida baza tipo "tip=" en sxablono "lili">>
- <<#E05 Nevalida signocxeno "hin=" "prf=" "err=" en sxablono "lili">>
  Possible causes:
  - string length out of range (2...80 octet:s)
- <<#E06 Erara uzo de sxablono "lili" pro parametroj>>
  Possible causes (later detected more clandestine problems with parameters):
  - bad value of a single parameter not caught by #E02 #E04 #E05
  - conflicting parameters
Note that obviously invalid or unknown language code is NOT an error here.

: ---------------------------------------

* #T00 ("", no params, evil)
* expected result: #E02
* actual result: "{{#invoke:mlili|ek}}"

::* #T01 ("eo|vo", two anonymous params, evil)
::* expected result: #E02
::* actual result: "{{#invoke:mlili|ek|eo|vo}}"

* #T02 ("vo", simplest example, native SB)
* expected result: "Volapuko"
* actual result: "{{#invoke:mlili|ek|vo}}"

::* #T03 ("ko", simplest example, native AJ)
::* expected result: "korea"
::* actual result: "{{#invoke:mlili|ek|ko}}"

* #T04 ("io|mon=la", raw, add "la" if needed from native SB)
* expected result: "Ido"
* actual result: "{{#invoke:mlili|ek|io|mon=la}}"

::* #T05 ("sv|mon=la", raw, add "la" if needed from native AJ)
::* expected result: "la sveda"
::* actual result: "{{#invoke:mlili|ek|sv|mon=la}}"

* #T06 ("sma|mon=la", raw, add "la" if needed from native AJ multiword)
* expected result: "la samea suda"
* actual result: "{{#invoke:mlili|ek|sma|mon=la}}"

: ---------------------------------------

* #T10 ("io|mon=aj", raw, force AJ from native SB)
* expected result: "ida"
* actual result: "{{#invoke:mlili|ek|io|mon=aj}}"

::* #T11 ("sv|mon=aj", raw, force AJ from native AJ)
::* expected result: "sveda"
::* actual result: "{{#invoke:mlili|ek|sv|mon=aj}}"

* #T12 ("sma|mon=aj", raw, force AJ from native AJ multiword)
* expected result: "samea suda"
* actual result: "{{#invoke:mlili|ek|sma|mon=aj}}"

::* #T13 ("io|mon=sf", raw, force root from native SB)
::* expected result: "id" (note that this is name "Ido", not code "id")
::* actual result: "{{#invoke:mlili|ek|io|mon=sf}}"

* #T14 ("sv|mon=sf", raw, force root from native AJ)
* expected result: "sved"
* actual result: "{{#invoke:mlili|ek|sv|mon=sf}}"

::* #T15 ("sma|mon=sf", raw, force root from native AJ multiword)
::* expected result: "samea sud" (useless)
::* actual result: "{{#invoke:mlili|ek|sma|mon=sf}}"

* #T16 ("sv|mon=pl", raw, force AJ and plural from native AJ)
* expected result: "svedaj"
* actual result: "{{#invoke:mlili|ek|sv|mon=pl}}"

::* #T17 ("sma|mon=pl", raw, force AJ and plural from native AJ multiword)
::* expected result: "sameaj sudaj"
::* actual result: "{{#invoke:mlili|ek|sma|mon=pl}}"

* #T18 ("sa|mon=pl", raw, force AJ and plural from native SB)
* expected result: "sanskritaj"
* actual result: "{{#invoke:mlili|ek|sa|mon=pl}}"

: ---------------------------------------

* #T20 ("io|mon=av", raw, force AV from native SB)
* expected result: "ide"
* actual result: "{{#invoke:mlili|ek|io|mon=av}}"

::* #T21 ("sv|mon=av", raw, force AV from native AJ)
::* expected result: "svede"
::* actual result: "{{#invoke:mlili|ek|sv|mon=av}}"

* #T22 ("sma|mon=av", raw, force AV from native AJ multiword)
* expected result: "en la samea suda" (fake AV by PP "en", is useful again)
* actual result: "{{#invoke:mlili|ek|sma|mon=av}}"

::* #T23 ("io|mon=av|maj=1", raw, force AV from native SB and uppercase)
::* expected result: "Ide"
::* actual result: "{{#invoke:mlili|ek|io|mon=av|maj=1}}"

* #T24 ("sv|mon=av|maj=1", raw, force AV from native AJ and uppercase)
* expected result: "Svede"
* actual result: "{{#invoke:mlili|ek|sv|mon=av|maj=1}}"

::* #T25 ("sma|mon=av|maj=1", raw, force AV from native AJ multiword and uppercase)
::* expected result: "En la samea suda" (fake AV by PP "en", is useful again)
::* actual result: "{{#invoke:mlili|ek|sma|mon=av|maj=1}}"

: ---------------------------------------

* #T30 ("eo|tip=LA", simple example, native SB)
* expected result: link to "Aldono:Esperanto" with text "Esperanto"
* actual result: "{{#invoke:mlili|ek|eo|tip=LA}}"

::* #T31 ("ko|tip=LA", simple example, native AJ)
::* expected result: link to "Aldono:Korea" with text "korea" (note the letter case)
::* actual result: "{{#invoke:mlili|ek|ko|tip=LA}}"

* #T32 ("eo|tip=LK", simple example, native SB)
* expected result: link to "Kategorio:Esperanto" with hidden colon ":"
* actual result: "{{#invoke:mlili|ek|eo|tip=LK}}"
* actual result via debu: "{{debu|{{#invoke:mlili|ek|eo|tip=LK}}|nw}}"

::* #T33 ("ko|tip=LK", simple example, native AJ)
::* expected result: link to "Kategorio:Korea" with hidden colon ":"
::* actual result: "{{#invoke:mlili|ek|ko|tip=LK}}"
::* actual result via debu: "{{debu|{{#invoke:mlili|ek|ko|tip=LK}}|nw}}"

* #T34 ("sma|tip=LK|mon=pl", force AJ and plural from native AJ multiword)
* expected result: link to "Kategorio:Samea suda" with hidden colon ":" and text "sameaj sudaj"
* actual result: "{{#invoke:mlili|ek|sma|tip=LK|mon=pl}}"
* actual result via debu: "{{debu|{{#invoke:mlili|ek|sma|tip=LK|mon=pl}}|nw}}"

: ---------------------------------------

* #T40 ("vo|tip=KE", simple example, native SB)
* expected result: sube "Kategorio:Volapuko"
* actual result: "{ {#invoke:mlili|ek|vo|tip=KE} }" (blocked)
* actual result via debu: "{{debu|{{#invoke:mlili|ek|vo|tip=KE}}|nw}}"

::* #T41 ("ko|tip=KE", simple example, native AJ)
::* expected result: sube "Kategorio:Korea"
::* actual result: "{ {#invoke:mlili|ek|ko|tip=KE} }" (blocked)
::* actual result via debu: "{{debu|{{#invoke:mlili|ek|ko|tip=KE}}|nw}}"

* #T42 ("ia|tip=KE|hin=Fivortaro", native SB, supply cat name and use hint)
* expected result: sube "Kategorio:Fivortaro" with hint "interlingvao"
* actual result: "{ {#invoke:mlili|ek|ia|tip=KE|hin=Fivortaro} }" (blocked)
* actual result via debu: "{{debu|{{#invoke:mlili|ek|ia|tip=KE|hin=Fivortaro}}|nw}}"

::* #T43 ("ko|tip=KE|hin=Fivortaro", native AJ, supply cat name and use hint)
::* expected result: sube "Kategorio:Fivortaro" with hint "korea"
::* actual result: "{ {#invoke:mlili|ek|ko|tip=KE|hin=Fivortaro} }" (blocked)
::* actual result via debu: "{{debu|{{#invoke:mlili|ek|ko|tip=KE|hin=Fivortaro}}|nw}}"

: ---------------------------------------

* note that tests #T32 ... #T34 and #T40 ... #T43 depend on "debu"
* note that tests #T40 ... #T43 cannot be reasonably executed on the docs subpage without help of "pate" or "debu"

: ---------------------------------------

]===]

local lili = {}

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

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

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

  -- uncommentable EO vs ID constant strings (core site-related features)

  local constrpriv = "eo"                 -- EO (privileged site language)
  -- local constrpriv = "id"                 -- ID (privileged site language)
  local constrplki = "Modulo:mpiktbllki"  -- EO
  -- local constrplki = "Modul:mpiktbllki"   -- ID
  local constrkatp = 'Kategorio:'         -- EO namespace prefix (incl colon ":")
  -- local constrkatp = 'Kategori:'          -- ID namespace prefix (incl colon ":")
  local constraldp = 'Aldono:'            -- EO namespace prefix (incl colon ":")
  -- local constraldp = 'Lampiran:'          -- ID namespace prefix (incl colon ":")

  -- constant table (ban list)

  -- so far only bad 3-letter codes, but bad 2-letter codes would work too

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

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

  -- constant table (surrogate transcoding table, only needed for EO)

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

  -- constant strings (error circumfixes)

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

  -- uncommentable EO vs ID (caller name for error messages)

  local constrkoll = 'sxablono "lili"'  -- EO augmented name of the caller (hardcoded, we do NOT peek it)
  -- local constrkoll = 'templat "lili"'   -- ID augmented name of the caller (hardcoded, we do NOT peek it)

  -- uncommentable EO vs ID constant table (error messages)

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

  local contaberaroj = {}
  contaberaroj[2] = 'Erara uzo de \\@, legu gxian dokumentajxon'           -- EO #E02
  -- contaberaroj[2] = 'Penggunaan salah \\@, bacalah dokumentasinya'         -- ID #E02
  contaberaroj[3] = 'Eraro en subsxablonoj uzataj far \\@'                 -- EO #E03
  -- contaberaroj[3] = 'Kesalahan dalam subtemplat digunakan oleh \\@'        -- ID #E03
  contaberaroj[4] = 'Nevalida baza tipo "tip=" en \\@'                     -- EO #E04
  -- contaberaroj[4] = 'Tipe dasar salah "tip=" di \\@'                       -- ID #E04
  contaberaroj[5] = 'Nevalida signocxeno "hin=" "prf=" "err=" en \\@'      -- EO #E05
  -- contaberaroj[5] = 'String salah "hin=" "prf=" "err=" di \\@'             -- ID #E05
  contaberaroj[6] = 'Erara uzo de \\@ pro parametroj'                      -- EO #E06
  -- contaberaroj[6] = 'Penggunaan salah \\@ oleh karena parameter'           -- ID #E06

  -- uncommentable EO vs ID constant strings (misc)

  local constrevid = "evidente nevalida lingvokodo"                -- EO placeholder
  -- local constrevid = "kode bahasa jelas-jelas salah"               -- ID placeholder
  local constrneli = "nekonata lingvokodo"                         -- EO placeholder
  -- local constrneli = "kode bahasa tidak dikenal"                   -- ID placeholder

  -- constant table (3 integers for preliminary parameter check)

  local contabparam = {}
  contabparam[0] = 1   -- minimal number of anon parameters
  contabparam[1] = 1   -- maximal number of anon parameters
  contabparam[2] = 160 -- maximal length of single para (min is hardcoded as ONE)

  -- constant bool (controls lng code checking)

  local conmiddig = false  -- assign to "true" to allow middle digit "s7a"

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

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

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

local qpiktbllki  = {}   -- import, type "table" with type "function" inside

qpiktbllki  = require(constrplki) -- can crash here !!!FIXME!!!

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

---- MATH FUNCTIONS [A] ----

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

local function mathdiv (xdividens, xdivisero)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividens / xdivisero)
  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".

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"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 STRING FUNCTIONS ----

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

-- test whether char is an ASCII digit "0"..."9", return bool

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

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

-- test whether char is an ASCII uppercase letter, return bool

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

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

-- test whether char is an ASCII lowercase letter, return bool

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

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

-- Local function LFIS62SAFE

-- Test whether incoming ASCII char is very safe (0...9 A...Z a...z).

-- Depends on functions :
-- [G] lftestnum lftestuc lftestlc

local function lfis62safe (numcxair)
  local booguud = false
  booguud = lftestnum (numcxair) or lftestuc (numcxair) or lftestlc (numcxair)
  return booguud
end--function lfis62safe

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

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

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

-- Local function LFULNUTF8CHAR

-- Measure length of a single UTF8 char, return ZERO if invalid.

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

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

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

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
-- and 6 extra EO letters.

-- Input  : * strucinrsut : single unicode letter (1 or 2 octet:s)
--          * booupcas    : for desired uppercase "true" and for
--                          lowercase "false"

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

-- This will not give useful results for any non-ASCII char:s other
-- than uppercase and lowercase EO letters (CX GX HX JX SX UX
-- cx gx hx jx sx ux). Invalid non-ASCII input generates "ZZ".

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "MATH FUNCTIONS"\"mathdiv" and "MATH FUNCTIONS"\"mathbitwrit" and
-- "STRING FUNCTIONS"\"lftestuc" and "STRING FUNCTIONS"\"lftestlc" and
-- "UTF8 FUNCTIONS"\"lfulnutf8char".

local function lfcaserest (strucinrsut, booupcas)

  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

  boowantlowr = (not booupcas)

  while (true) do -- fake loop

    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 = lftestuc(numchaerr)
      booiislowr = lftestlc(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, almost done
    end--if

    if ((numchaerr<196) or (numchaerr>197)) then -- only 196 or 197 accepted
      break -- unsupported UTF8 block
    end--if
    numchaess = string.byte (strucinrsut,2,2)
    numchareel = (numchaerr-196)*64 + (numchaess-128) -- begins at $C4
    numtheemp = mathbitwrit (numchareel,0,false) -- bad way to do AND $FE
    if ((numtheemp~=8) and (numtheemp~=28) and (numtheemp~=36) and (numtheemp~=52) and (numtheemp~=92) and (numtheemp~=108)) then
      break -- unsupported UTF8 char
    end--if

    booiislowr = ((mathmod(numchareel,2))==1) -- UC even (ordinary)
    booiisuppr = not booiislowr
    if (booiisuppr and boowantlowr) then
      numdeta = 1 -- UPPER->lower
    end--if
    if (booiislowr and booupcas) then
      numdeta = -1 -- lower->UPPER
    end--if
    boovalid = true -- success, almost done

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

  while (true) do -- fake loop

    if (boovalid==false) then
      strucinrsut = "ZZ" -- illegal 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 -- fake loop -- join mark

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

end--function lfcaserest

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

-- Local function LFXCASEULT

-- Adjust letter case of beginning letter or all letters in a word or group of
-- words to upper or lower, limited unicode support (restricted LFCASEREST).

-- Input  : * strenigo : word or group of words (may be empty)
--          * booupcas : "true" for uppercase and "false" for lowercase
--          * boodoall : "true" to adjust all letters, "false" only beginning

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "STRING FUNCTIONS"\"lftestuc" and "STRING FUNCTIONS"\"lftestlc" and
-- "UTF8 FUNCTIONS"\"lfulnutf8char" and "UTF8 FUNCTIONS"\"lfcaserest"
-- (restricted LFCASEREST).

local function lfxcaseult (strenigo, booupcas, boodoall)

  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 = ""

  numlein = string.len (strenigo)
  while (true) do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodoall 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,booupcas) -- (restricted LFCASEREST)
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut
  end--while
  return strelygo

end--function lfxcaseult

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

---- LOCAL HIGH LEVEL FUNCTIONS ----

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

-- Local function LFBREWERROR

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

-- We need const strings "constrkros", "constrelabg",
-- "constrelaen" and const table "contaberaroj"

local function lfbrewerror (numerrorcode)
  local stritsucks = '#E'
  if ((numerrorcode>=2) and (numerrorcode<=6)) then -- #E06 hardcoded here
    stritsucks = stritsucks .. '0' .. string.char(numerrorcode+48) .. ' ' .. contaberaroj[numerrorcode]
  else
    stritsucks = stritsucks .. '??'
  end--if
  stritsucks = constrkros .. constrelabg .. stritsucks .. constrelaen .. constrkros
  return stritsucks
end--function lfbrewerror

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

-- Local function LFHVALIDATELNKOADV

-- Check whether a string (intended to be a language code) contains 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)
--          * numnokod -- "0" ... "3" how special codes "-" "??" should pass
--          * boolonkg -- "true" to allow long codes such as "zh-min-nan"
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- "true" to skip test against ban table

-- Output : * booisvaladv -- boolean "true" if the string is valid

-- Depends on functions :
-- [G] lftestnum lftestlc

-- 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 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 lfhvalidatelnkoadv (strqooq, numnokod, boolonkg, 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=="-") and ((numnokod==1) or (numnokod==3))) then
      break -- to join mark -- good
    end--if
    if ((strqooq=="??") and ((numnokod==2) or (numnokod==3))) then
      break -- to join mark -- good
    end--if
    numukurran = string.len (strqooq)
    if ((numukurran<2) or (numukurran>10)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if
    if (not boolonkg and (numukurran>3)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if

    numindeex = 0
    while (true) do -- ordinary inner loop over char:s
      if (numindeex>=numukurran) then
        break -- done -- good
      end--if
      numchiiar = string.byte (strqooq,(numindeex+1),(numindeex+1))
      booislclc = lftestlc(numchiiar)
      booisdigi = lftestnum(numchiiar)
      booisdash = (numchiiar==45)
      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 -- ordinary inner loop over char:s

    if (not boonoban) then -- if "yesban" then
      numindeex = 0
      while (true) do -- ordinary 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 -- ordinary lower inner loop
    end--if (not boonoban) then

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

  return booisvaladv

end--function lfhvalidatelnkoadv

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

-- Local function LFFILLNAME

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

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

local function lffillname (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
    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 lffillname

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

-- Local function LFKODEOSG

-- Transcode X-surrogates (without "\", thus for example "kacxo",
-- NOT "ka\cxo") to cxapeloj in a string (EO only)

-- Input  : * strsurr : string (empty is useless but can't cause major harm)

-- Output : * strcxapeloj

-- We need const table "contabtransiltable".

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

local function lfkodeosg (strsurr)
  local varpeek = 0
  local strcxapeloj = ''
  local numinputl = 0
  local numininx = 0 -- ZERO-based source index
  local numknark = 0 -- current char (ZERO is NOT valid)
  local numknarp = 0 -- previous char (ZERO is NOT valid)
  local numlow = 0
  local numhaj = 0
  numinputl = string.len (strsurr)
  while (true) do
    if (numininx==numinputl) then
      break
    end--if
    numknark = string.byte(strsurr,(numininx+1),(numininx+1))
    numininx = numininx + 1
    numhaj = 0 -- pre-assume no translation
    if ((numknarp~=0) and ((numknark==88) or (numknark==120))) then -- got "x"
      varpeek = contabtransiltable[numknarp] -- UINT16 or nil
      if (varpeek~=nil) then
        numlow = mathmod (varpeek,256)
        numhaj = mathdiv (varpeek,256)
      end--if
    end--if
    if (numhaj~=0) then
      strcxapeloj = strcxapeloj .. string.char(numhaj,numlow)
      numknark = 0 -- invalidade current char
    else
      if (numknarp~=0) then -- add previous char only if valid
        strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
      end--if
    end--if
    numknarp = numknark -- copy to previous even if invalid
  end--while
  if (numknarp~=0) then -- add previous and last char only if valid
    strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
  end--if
  return strcxapeloj
end--function lfkodeosg

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

-- Local function LFBREWCATHINT

-- Lower all letters and remove junk other than letters (ASCII and
-- non-ASCII) and numbers, particularly ban spaces and dashes, used for
-- the sorting hint/key.

-- Simplified strategy:
-- * numbers unchanged
-- * ASCII lowercase unchanged
-- * ASCII uppercase lowered
-- * non-ASCII assumed to be EO letter and passed through "lfcaserest"
-- * everything else dropped

-- This will not give useful results for any non-ASCII char:s other
-- than uppercase and lowercase EO letters (CX GX HX JX SX UX
-- cx gx hx jx sx ux).

-- Depends on functions :
-- [U] lfulnutf8char lfcaserest
-- [G] lftestnum lftestuc lftestlc
-- [A] mathdiv mathmod mathbitwrit

local function lfbrewcathint (strhinthink)

  local strhasiil = ''
  local stronechaar = ''
  local numstrleon = 0
  local numeindx = 1 -- ONE-based
  local numczaar = 0
  local numczanx = 0 -- pre-picked next char

  numstrleon = string.len (strhinthink)

  while (true) do

    if (numeindx>numstrleon) then
      break -- empty input is useless but cannot cause major harm
    end--if

    numczaar = string.byte (strhinthink,numeindx,numeindx)
    numeindx = numeindx + 1 -- do INC here
    numczanx = 0
    if (numeindx<=numstrleon) then
      numczanx = string.byte (strhinthink,numeindx,numeindx) -- do NOT INC
    end--if

    while (true) do -- fake (inner) loop
      if (lftestnum(numczaar) or lftestlc(numczaar)) then
        stronechaar = string.char (numczaar)
        break -- numbers and ASCII lowercase pass unchanged
      end--if
      if (lftestuc(numczaar)) then
        stronechaar = string.char (numczaar+32)
        break -- lower it
      end--if
      if ((numczaar==196) or (numczaar==197)) then
        stronechaar = lfcaserest (string.char (numczaar,numczanx),false)
        numeindx = numeindx + 1 -- do INC here, totally + 2
        break -- maybe lower it, "ZZ" on error
      end--if
      stronechaar = "" -- discard garbage
      break -- finally to join mark
    end--while -- fake loop -- join mark

    strhasiil = strhasiil .. stronechaar

  end--while

  return strhasiil

end--function lfbrewcathint

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

---- VERY HIGH LEVEL FUNCTIONS [Y] ----

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

-- Local function LFYLKI

-- Depends on imports :
-- * qpiktbllki.ek ie imported mpiktbllki via constrplki

-- NOTE: x-index is NOT fed into mpiktbllki in "bin=1" mode

local function lfylki (strlangcode, strxindeeks)
  local strmnbvcxz = ''
  if (strxindeeks=="%BIN%") then
    strmnbvcxz = qpiktbllki.ek { args = { strlangcode , bin = "1" } }
  else
    strmnbvcxz = qpiktbllki.ek { args = { strlangcode , strxindeeks } }
  end--if
  return strmnbvcxz
end--function lfylki

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

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

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

function lili.ek (arxframent)

  -- general unknown type

  local vartmp = 0     -- variable without type

  -- special type "args" AKA "arx"

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

  -- general "str"

  local strnambah  = ""  -- language name (without prefix "Bahasa")
  local strnambauc = ""  -- language name uppercased begin ("Angla")
  local strnambaty = ""  -- lng name processed according to "mon=" and "maj=1"
  local strtmp     = ""
  local strtspm    = ""
  local strviserr  = ""  -- visible error
  local strrawlnk  = ""  -- raw or link output
  local strret     = ""  -- result string

  -- parameters "str"

  local strkodbah  = ""  -- language code (2 or 3 lowercase) from arxsomons[1]
  local strhinhin  = ""  -- name of category KE (lng code will brew the hint)
  local strprfprf  = ""  -- prefix before language name
  local strerrerr  = ""  -- common placeholder

  -- general "num"

  local numerr     = 0 -- 1 inter 2 para 3 sub 4 tip 5 cxeno3 6 misc
  local numpindex  = 0 -- number of anon params
  local numlong    = 0 -- length of parameter mm
  local numtamp    = 0
  local numlangsta = 0 -- 2 known -- 1 unknown -- 0 obviously invalid

  -- parameters "num"

  local numbasetipe = 0 -- bast : 0 raw -- 1 LA -- 2 LK -- 3 KE -- 4 empty
  local nummontip   = 0 -- lngt : 0 nt -- 1 la -- 2 aj -- 3 sf -- 4 av -- 5 pl

  -- parameters "boo"

  local boonem  = false -- avoid wall in visible link of type LA LK
  local boomaj  = false -- force uppercase
  local boonoc  = false -- infamous "nocat" (cannot cause an error)
  local booper  = false -- keep link or category on error
  local boohavehin = false
  local boohaveprf = false
  local boohaveerr = false

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

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

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

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

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

  if ((type(constrplki)=="nil") or (type(constrkatp)~="string") or (type(constrpriv)~="string") or (type(constrkoll)~="string")) then
    numerr = 1 -- #E01 internal
  end--if

  ---- FILL IN ERROR MESSAGES AND TRANSCODE EO IF NEEDED ----

  -- placeholder "\@" "\\@" is replaced by augmented name of the caller
  -- from "constrkoll" in any case, for example 'sxablono "lili"'
  -- or 'templat "lili"'

  -- only for EO the X-substitution is subsequently performed

  if (numerr==0) then

    numtamp = 2 -- start with #E02
    while (true) do
      vartmp = contaberaroj[numtamp]
      if ((type(vartmp))=="nil") then -- number of messages is NOT harcoded
        break
      end--if
      strtmp = lffillname (vartmp,constrkoll)
      if (constrpriv=="eo") then
        strtmp = lfkodeosg (strtmp)
      end--if
      contaberaroj[numtamp] = strtmp
      numtamp = numtamp + 1 -- TWO-based
    end--while

  end--if

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

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

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

  if (numerr==1) then
    strtspm = '#E01 Internal error in module "mlili".'
    strviserr = constrkros .. constrelabg .. strtspm .. constrelaen .. constrkros
  end--if

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

  if (numerr==0) then
    arxsomons = arxframent.args -- "args" from our own "frame"
    if (arxsomons['caller']=="true") then
      arxsomons = arxframent:getParent().args -- "args" from caller's "frame"
    end--if
  end--if

  ---- PRELIMINARILY ANALYZE ANONYMOUS PARAMETERS ----

  -- this will catch holes, empty parameters, too long parameters,
  -- and wrong number of parameters

  -- below on exit var "numpindex" will contain number of
  -- prevalidated anonymous params

  -- this depends on 3 constants:
  -- * contabparam[0] minimal number
  -- * contabparam[1] maximal number
  -- * contabparam[2] maximal length (default 160)

  if (numerr==0) then
    numpindex = 0 -- ZERO-based
    numtamp = contabparam[1] -- maximal number of params
    while (true) do
      vartmp = arxsomons [numpindex+1] -- can be "nil"
      if ((type(vartmp)~="string") or (numpindex>numtamp)) then
        break -- good or bad
      end--if
      numlong = string.len (vartmp)
      if ((numlong==0) or (numlong>contabparam[2])) then
        numerr = 2 -- #E02 param/RTFD
        break -- only bad here
      end--if
      numpindex = numpindex + 1 -- on exit has number of valid parameters
    end--while
    if ((numpindex<contabparam[0]) or (numpindex>numtamp)) then
      numerr = 2 -- #E02 param/RTFD
    end--if
  end--if

  ---- PROCESS 1 OBLIGATORY ANONYMOUS PARAM INTO 1 STRING AND 1 NUM ----

  -- now var "numpindex" sudah contains number of prevalidated params
  -- and is always 1 and is useless

  -- here we prevalidate and assign "strkodbah" and "numlangsta"

  -- this depends directly on const bool "conmiddig" !!!FIXME!!! long codes

  -- this depends indirectly on const
  -- table "contabisbanned" via "lfhvalidatelnkoadv"

  if (numerr==0) then
    numlangsta = 2 -- this is just weak hope ...
    strkodbah = arxsomons[1] -- language code (obligatory)
    if (not lfhvalidatelnkoadv(strkodbah,2,false,conmiddig,false)) then
      numlangsta = 0 -- 2 known -- 1 unknown -- 0 obviously invalid
    end--if
  end--if

  ---- PROCESS 0...9 OPTIONAL NAMED PARAMS ----

  -- data type "boo" (3+1) (assign a "boo") :
  -- * "nem=" - avoid wall in visible link of type LA LK
  -- * "maj=" - force uppercase
  -- * "noc=" - "true" - this is "nocat" (no error possible)
  -- * "per=" - keep link or category if unknown
  -- multiple choice (2) (assign a "num") :
  -- * "tip=" - base type, 4 possible values (later 5)
  --            0 raw -- 1 LA -- 2 LK -- 3 KE -- (later also: 4 empty)
  -- * "mon=" - type of language name, 4 possible values
  --            0 native -- 1 la -- 2 aj -- 3 sf -- 4 av
  -- string (2...80 octet:s) (3) (fill string and assign a "boo" to "true") :
  -- * "hin=" - name of category KE (lng code will brew the hint)
  -- * "prf=" - prefix before language name
  -- * "err=" - common placeholder

  if (numerr==0) then
    while (true) do -- fake loop

      boonem = false
      vartmp = arxsomons['nem'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        if (vartmp=="1") then
          boonem = true
        else
          numerr = 6 -- #E06 -- other param crime
          break
        end--if
      end--if

      boomaj = false
      vartmp = arxsomons['maj'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        if (vartmp=="1") then
          boomaj = true
        else
          numerr = 6 -- #E06 -- other param crime
          break
        end--if
      end--if

      boonoc = false
      vartmp = arxsomons['noc'] -- optional, NOT prevalidated
      if (vartmp=="true") then
        boonoc = true -- this one needs word "true" and CANNOT cause an error
      end--if

      booper = false
      vartmp = arxsomons['per'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        if (vartmp=="1") then
          booper = true
        else
          numerr = 6 -- #E06 -- other param crime
          break
        end--if
      end--if

      numbasetipe = 0
      vartmp = arxsomons['tip'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        if (vartmp=="LA") then
          numbasetipe = 1
        end--if
        if (vartmp=="LK") then
          numbasetipe = 2
        end--if
        if (vartmp=="KE") then
          numbasetipe = 3
        end--if
        if (numbasetipe==0) then
          numerr = 4 -- #E04 -- faulty base type
          break
        end--if
      end--if

      nummontip = 0 -- default "raw" / "native"
      vartmp = arxsomons['mon'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        if (vartmp=="la") then
          nummontip = 1
        end--if
        if (vartmp=="aj") then
          nummontip = 2
        end--if
        if (vartmp=="sf") then
          nummontip = 3
        end--if
        if (vartmp=="av") then
          nummontip = 4
        end--if
        if (vartmp=="pl") then
          nummontip = 5
        end--if
        if (nummontip==0) then
          numerr = 6 -- #E06 -- other param crime
          break
        end--if
      end--if

      boohavehin = false
      vartmp = arxsomons['hin'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        numtamp = string.len (vartmp)
        if ((numtamp>=2) and (numtamp<=80)) then
          strhinhin = vartmp
          boohavehin = true
        else
          numerr = 5 -- #E05 -- crime with "hin=" "prf=" "err="
          break
        end--if
      end--if

      boohaveprf = false
      vartmp = arxsomons['prf'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        numtamp = string.len (vartmp)
        if ((numtamp>=2) and (numtamp<=80)) then
          strprfprf = vartmp
          boohaveprf = true
        else
          numerr = 5 -- #E05 -- crime with "hin=" "prf=" "err="
          break
        end--if
      end--if

      boohaveerr = false
      vartmp = arxsomons['err'] -- optional, NOT prevalidated
      if (type(vartmp)=="string") then
        numtamp = string.len (vartmp)
        if ((numtamp>=2) and (numtamp<=80)) then
          strerrerr = vartmp
          boohaveerr = true
        else
          numerr = 5 -- #E05 -- crime with "hin=" "prf=" "err="
          break
        end--if
      end--if

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

  ---- CHECK FOR CONFLICTING PARAMETERS ----

  -- "tip="  no conflicts possible (only values 0...3 can be
  --                                requested, value 4 not directly)
  --         default is fixed to "raw" 0
  --         may be changed later from "LA" 1 "LK" 2 to "raw" 0 (for 1 reason)
  --                              or from "KE" 2 to "empty" 4 (for 2 reasons)
  -- "nem=1" is illegal for base type "KE" 3 or "raw" 0
  --         default is fixed to false (wall with base type "KE" is separate)
  --         is negative (no-default "1" means "no wall")
  -- "mon="  is illegal for base type "KE" 3
  --         is illegal together with "nem=1"
  --         default is fixed to "native" 0
  -- "maj=1" is illegal for base type "KE" 3
  --         is illegal together with "nem=1"
  --         default is fixed to false (lowercasing separate from this type)
  -- "hin="  is illegal for base type other than "KE" 3
  --         default is fixed to false and empty
  -- "prf="  is illegal for base type "raw" 0
  --         is illegal together with "mon="
  --         is illegal together with "hin="
  --         default is fixed to false and empty
  -- "per=1" is illegal for base type "raw" 0
  --         default is fixed to OFF
  -- "err="  no conflicts possible

  if (numerr==0) then

      if (boonem and ((numbasetipe==0) or (numbasetipe==3))) then
        numerr = 6 -- #E06 -- other param crime
      end--if

      if ((nummontip~=0) and (numbasetipe==3)) then
        numerr = 6 -- #E06 -- other param crime
      end--if
      if ((nummontip~=0) and boonem) then
        numerr = 6 -- #E06 -- other param crime
      end--if

      if (boomaj and (numbasetipe==3)) then
        numerr = 6 -- #E06 -- other param crime
      end--if
      if (boomaj and boonem) then
        numerr = 6 -- #E06 -- other param crime
      end--if

      if (boohavehin and (numbasetipe~=3)) then
        numerr = 6 -- #E06 -- other param crime
      end--if

      if (boohaveprf and (numbasetipe==0)) then
        numerr = 6 -- #E06 -- other param crime
      end--if
      if (boohaveprf and (nummontip~=0)) then
        numerr = 6 -- #E06 -- other param crime
      end--if
      if (boohaveprf and boohavehin) then
        numerr = 6 -- #E06 -- other param crime
      end--if

      if (booper and (numbasetipe==0)) then
        numerr = 6 -- #E06 -- other param crime
      end--if

  end--if

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

  -- reporting of errors #E02...#E06 depends on uncommentable strings

  if (numerr>1) then
    strviserr = lfbrewerror(numerr)
  end--if

  ---- CHANGE BASE TYPE IF NEEDED DUE TO NOCAT ----

  if ((numerr==0) and boonoc and (numbasetipe==3)) then
    numbasetipe = 4 -- switch to empty result pseudo type 4
  end--if

  ---- PEEK THE LANGUAGE NAME VIA SUBMODULE ----

  -- tristate "numlangsta" : 2 known -- 1 unknown -- 0 obviously invalid
  -- from above 2 or 0, now confirm 2 or down to 1 or skip this

  -- for lng name in site language ("c0"):
  -- * "-" is unconditionally evil with #E03 (broken submodule)
  -- * "=" can become (numlangsta=1) (unknown code) if the site language
  --   code works, otherwise #E03 (broken submodule) too

  -- here we depend on "constrpriv" and submodule "mpiktbllki" via "lfylki"

  if ((numerr==0) and (numbasetipe~=4) and (numlangsta==2)) then

    strnambah = lfylki (strkodbah,"0") -- name of lang, no "rl"

    while (true) do -- fake loop
      if (strnambah=="=") then
        strtspm = lfylki (constrpriv,"%BIN%") -- test own lang code
        if (strtspm=="1") then
          numlangsta = 1 -- unknown code given (site code works)
        else
          numerr = 3 -- #E03 broken submodule (site code does NOT work either)
        end--if
        break
      end--if (strnambah=="=")
      if (strnambah=="-") then -- better content in "c0" absolutely required
        numerr = 3 -- #E03 broken submodule
        break
      end--if
      if (string.len(strnambah)<2) then -- less than 2 letters is not legal
        numerr = 3 -- #E03 broken submodule
        break
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if

  ---- CHANGE BASE TYPE AND MON TYPE TO RAW IF NEEDED DUE TO BAD CODE ----

  -- only if code is bad (obviously invalid 0 or unknown 1) and
  -- "per=1" was NOT used then change base type "LA" 1 or "LK" 2 to type
  -- "raw" 0, and base type "KE" 3 to empty result AKA pseudo type 4

  -- if code is bad then always reset "mon=" type to "raw" 0

  if ((numerr==0) and (numlangsta~=2) and (booper==false)) then
    if ((numbasetipe==1) or (numbasetipe==2)) then
      numbasetipe = 0 -- switch to type "raw"
    end--if
    if (numbasetipe==3) then
      numbasetipe = 4 -- switch to empty result pseudo type 4
    end--if
  end--if

  if ((numerr==0) and (numlangsta~=2)) then
    nummontip = 0 -- default "raw" / "native"
  end--if

  ---- APPLY PLACEHOLDERS FOR BAD CODES ----

  if ((numerr==0) and (numbasetipe~=4)) then

    if (boohaveerr) then
      strtmp  = strerrerr -- common placeholder
      strtspm = strerrerr -- common placeholder
    else
      strtmp  = constrevid -- invalid
      strtspm = constrneli -- unknown
    end--if
    if (numlangsta==0) then
      strnambah = strtmp -- invalid
    end--if
    if (numlangsta==1) then
      strnambah = strtspm -- unknown
    end--if

  end--if

  ---- PERFORM CASE MESSING ----

  -- strkodbah  -- not needed anymore -- code
  -- strnambah  -- from above language name (without prefix "Bahasa")
  --               always >= 2 letters
  -- strnambauc -- language name uppercased begin ("Angla")
  -- strnambaty -- language name processed later according
  --               to "mon=" and "maj=1"

  if ((numerr==0) and (numbasetipe~=4)) then
    strnambauc = lfxcaseult (strnambah,true,false) -- make upper one letter
    strnambaty = strnambah
  end--if

  ---- BREW THE VISIBLE PART FOR RAW OR FOR LINK ----

  -- "numbasetipe" : 0 raw -- 1 LA -- 2 LK colon YES -- 3 KE -- 4 empty
  -- "nummontip"   : 0 native -- 1 la -- 2 aj -- 3 sf -- 4 av -- 5 pl

  -- augment "strnambaty" here as needed (incoming multiword SB is bad)

  -- note that for "nummontip" 1 ie "la" we will add the article to native
  -- AJ only, never to native SB (but type 4 ie "av" can force AJ and
  -- subsequently add "la")

  -- note that we MUST NOT change the word class to AJ
  -- for "nummontip" 4 ie "av" if multiword

  -- for "nummontip" 5 ie "pl" we change the word class to AJ too

  do
  local strpluralizator = ""
  local numloong     = 0
  local numplurindex = 0
  local nummychar    = 0
  local nummyches    = 0
  local booisnoun    = false
  local boomulwords  = false
  local booaddprepen = false
  local booaddartla  = false

  if ((numerr==0) and (numbasetipe~=4) and (nummontip~=0)) then

    numloong = string.len (strnambaty)
    nummychar = string.byte (strnambaty,numloong,numloong)

    booisnoun = (nummychar==111) -- "o"
    boomulwords = (string.find (strnambaty, " ", 1, true)~=nil) -- plain text
    booaddprepen = ((nummontip==4) and boomulwords)
    booaddartla = ((booisnoun==false) and ((nummontip==1) or booaddprepen))

    if ((nummontip>=2) and (nummontip<=5) and (booaddprepen==false)) then
      strnambaty = string.sub (strnambaty,1,(numloong-1)) -- cut off last lettr
      strnambaty = lfxcaseult (strnambaty,false,false) -- lower it
    end--if
    if (booaddartla) then
      strnambaty = "la " .. strnambaty -- we will NOT pluralize this
    end--if
    if ((nummontip==2) or (nummontip==5)) then
      strnambaty = strnambaty .. "a" -- adjective (pluralize later if needed)
    end--if

    if (nummontip==4) then
      if (boomulwords) then
        strnambaty = "en " .. strnambaty -- fake adverb PP "en" + ...
      else
        strnambaty = strnambaty .. "e" -- adverb
      end--if
    end--if

    if (nummontip==5) then
      if (boomulwords) then
        strpluralizator = strnambaty
        strnambaty = ""
        numloong = string.len (strpluralizator)
        numplurindex = 0
        while (true) do
          if (numplurindex==numloong) then
            break
          end--if
          nummychar = string.byte (strpluralizator,(numplurindex+1),(numplurindex+1))
          numplurindex = numplurindex + 1 -- ZERO-based
          nummyches = 0
          if (numplurindex~=numloong) then
            nummyches = string.byte (strpluralizator,(numplurindex+1),(numplurindex+1))
          end--if
          if ((nummychar==97) and (nummyches==32)) then
            strnambaty = strnambaty .. "aj "
            numplurindex = numplurindex + 1 -- eaten 2 char:s, written 3
          else
            strnambaty = strnambaty .. string.char(nummychar) -- copy char
          end--if
        end--while
      end--if (boomulwords) then
      strnambaty = strnambaty .. "j" -- pluralize last or only AJ
    end--if (nummontip==5) then

  end--if ((numerr==0) and (numbasetipe~=4) ...

  if ((numerr==0) and (numbasetipe~=4) and boomaj) then
    strnambaty = lfxcaseult (strnambaty,true,false)
  end--if

  end--do

  ---- CARRY OUT THE TRIVIAL WORK WITH BASE TYPE RAW ----

  if ((numerr==0) and (numbasetipe==0)) then
    strrawlnk = strnambaty
  end--if

  ---- CARRY OUT THE HARD WORK WITH A LINK ----

  -- "numbasetipe" : 0 raw -- 1 LA -- 2 LK colon YES -- 3 KE -- 4 empty
  -- "nummontip"   : 0 native -- 1 la -- 2 aj -- 3 sf -- 4 av -- 5 pl

  -- "prf=" is legal for all LA LK KE but not together with "hin="

  do
  local strbasepagename = ""
  local strprefix = "" -- "Appendix:" 1 or ":Category:" 2 or "Category:" 3
  local strbefore = "" -- before wall (link target)
  local strafteer = "" -- aft wall (visible link text) (no wall if this empty)

  if ((numerr==0) and (numbasetipe~=0) and (numbasetipe~=4)) then

    if (numbasetipe==1) then
      strprefix = constraldp -- LA "Appendix:"
    end--if
    if (numbasetipe==2) then
      strprefix = ":" .. constrkatp -- LK ":Category:"
    end--if
    if (numbasetipe==3) then
      strprefix = constrkatp -- KE "Category:"
    end--if

    if (boohaveprf) then
      strbasepagename = strprfprf .. " (" .. strnambah .. ")" -- no force uppercase
    else
      strbasepagename = strnambauc -- force uppercased begin
    end--if

    if (numbasetipe==3) then
      if (boohavehin) then
        strbefore = strhinhin
        strafteer = lfbrewcathint (strnambah)
      else
        strbefore = strbasepagename
        strafteer = '' --  no wall is tolerable
      end--if
    else
      strbefore = strbasepagename
      if (boonem) then
        strafteer = '' --  no wall is tolerable in visible link of type LA LK
      else
        strafteer = strnambaty -- augmented language name
      end--if
    end--if (numbasetipe==3) else

    strrawlnk = "[[" .. strprefix .. strbefore -- prefix includes colon ":"
    if (strafteer~='') then
      strrawlnk = strrawlnk .. "|" .. strafteer
    end--if
    strrawlnk = strrawlnk .. "]]"

  end--if ((numerr==0) and (numbasetipe~=0) and (numbasetipe~=4)) then

  end--do

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

  strret = strviserr .. strrawlnk
  return strret -- can be empty

end--function

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

return lili