Modulo:maudilingdial

 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 "MAUDILINGDIAL" (audio lingvo dialekto)

"eo.wiktionary.org/wiki/Modulo:maudilingdial" <!--2023-Aug-07-->

Purpose: picks language and dialect codes from name of an audio file
         or explicit override parameters "ling=" and "dial="

Utilo: elprenas lingvan kaj dialektan kodon el nomo de sondosiero
       aux eksplicitaj transpasaj parametroj "ling=" kaj "dial="

Used by templates / Uzata far sxablonoj:
- "auxdo"

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

Incoming: - 3 anonymous obligatory parameters (can cause an error)
            - name of the audio file (caller must forward it to us)
              (5...120 octet:s to prevent error, further limits
              apply for seizures)
            - type of information to seize: "l" for "ling" or "d" for "dial"
            - default value for result if seizure fails (1...32 octet:s),
              even this one will get victim of case fixing if 2...3 char:s
          - 2 named optional parameters (CANNOT cause an error, caller
              must forward them to us by "ling={{{ling}}}" etc, wall "|"
              is NOT needed, all 1...32 octet:s, rejected and ignored if
              it begins with curly "{")
            - "ling=" (should be code, other text not appreciated here)
            - "dial=" (can be code or other text)

Returned: - code (2 or 3 letters) or other text picked from name of the
            audio file or provided override parameter "ling=" or "dial=" if
            available, or default string (from anonymous obligatory parameter)
            otherwise, case is fixed to lowercase ("l") or uppercase ("d")
            if 2 or 3 letters, or unchanged otherwise (can occur only with
            "dial=" parameter and theoretically even with "ling=" parameter,
            at worst hardcoded string "=" if an error occurs (bad anonymous
            obligatory parameters)

This module is unbreakable (when called with correct module name
and function name).

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio).

There are 2 common naming patterns for pronunciation files at
"Commons" and we support them both:
* (traditional pattern) "en-us-rail.oga" with dialect or
                        "en-rail.oga" without dialect
* (LLQ pattern, "Lingua Libre")
  "LL-Q143 (epo)-Mojosulo-nenio.wav" always without dialect
Early branching checks:
In order to qualify for the LLQ pattern the name must (and applies):
* be at least 22 char:s long (this means at least 2 char:s user name with
  3-digit number and 1 char word and 3+1 char:s extension)
* begin with "LL-Q" (exact case sensitive comparison) and the following
  char must be a digit
Otherwise it is considered as traditional pattern. This means also that
a dialect cannot be found if LLQ pattern is detected, no attempt to read
it via traditional pattern is made.
Subsequent checks:
- 3...9 digits ("Q100"..."Q999999999", actually "100"..."999999999")
- Q-item successfully converted via "T81-1" from "loaddata-tbllingvoj"
Failure here means no pick and give up, not fallback to traditional pattern.
We completely ignore the later following ISO 639-3:2007 code in any case.

Usage examples with results / Ekzemploj de uzo
kun rezultoj / Contoh penggunaan dengan hasil:

{{hr3}} <!-------------------------------->

* #T00 (no params, too few)
* expected result: "="
* actual result: "{{#invoke:maudilingdial|ek}}"

::* #T01 (two params, still too few)
::* expected result: "="
::* actual result: "{{#invoke:maudilingdial|ek|nonsense|bullshit}}"

* #T02 ({maudilingdial|ek|four|l|nine}, 3 params, left one bad)
* expected result: "="
* actual result: "{{#invoke:maudilingdial|ek|four|l|nine}}"

::* #T03 ({maudilingdial|ek|seven|eight|nine}, 3 params, middle one bad)
::* expected result: "="
::* actual result: "{{#invoke:maudilingdial|ek|seven|eight|nine}}"

* #T04 ({maudilingdial|ek|seven|d|abcdefghijklmnopxabcdefghijklmnop}, 3 params, right one bad)
* expected result: "="
* actual result: "{{#invoke:maudilingdial|ek|seven|d|abcdefghijklmnopxabcdefghijklmnop}}"

{{hr3}} <!-------------------------------->

* #T10 ({maudilingdial|ek|en-us-rail.oga|l|-}, request "l" and SUCCESS)
* expected result: "en"
* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|l|-}}"

::* #T11 ({maudilingdial|ek|en-us-rail.oga|d|-}, request "d" and SUCCESS)
::* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|d|-}}"
::* expected result: "US"

* #T12 (maudilingdial|ek|en-us-r.oga|d|-}, request "d" and SUCCESS, equal minimum, valid)
* expected result: "US"
* actual result: "{{#invoke:maudilingdial|ek|en-us-r.oga|d|-}}"

::* #T13 ({maudilingdial|ek|en-us+r.oga|d|xx}, request "d" and FAILURE, equal minimum but only one dash "-" in filename)
::* expected result: "XX" (uppercase even here)
::* actual result: "{{#invoke:maudilingdial|ek|en-us+r.oga|d|xx}}"

* #T14 ({maudilingdial|ek|en-us+r.oga|d|xxxx}, request "d" and FAILURE, equal minimum but only one dash "-" in filename)
* expected result: "xxxx" (NOT uppercase)
* actual result: "{{#invoke:maudilingdial|ek|en-us+r.oga|d|xxxx}}"

::* #T15 ({maudilingdial|ek|en-us-r.og|d|-}, request "d" and FAILURE, length below minimum)
::* expected result: "-"
::* actual result: "{{#invoke:maudilingdial|ek|en-us-r.og|d|-}}"

* #T16 ({maudilingdial|ek|eN-uS-rail.oga|d|-}, request "d" and SUCCESS, tolerable strange case)
* expected result: "US"
* actual result: "{{#invoke:maudilingdial|ek|eN-uS-rail.oga|d|-}}"

::* #T17 ({maudilingdial|ek|pen-bus-rail.oga|l|-}, request "l" and SUCCESS)
::* expected result: "pen"
::* actual result: "{{#invoke:maudilingdial|ek|pen-bus-rail.oga|l|-}}" (3+3)

* #T18 ({maudilingdial|ek|pen-bus-rail.oga|d|-}, request "d" and SUCCESS)
* expected result: "BUS"
* actual result: "{{#invoke:maudilingdial|ek|pen-bus-rail.oga|d|-}}" (3+3)

{{hr3}} <!-------------------------------->

* #T20 ({maudilingdial|ek|en-abus-rail.oga|l|-}, request "l" and SUCCESS, although "d" in filename is bad)
* expected result: "en"
* actual result: "{{#invoke:maudilingdial|ek|en-abus-rail.oga|l|-}}"

::* #T21 ({maudilingdial|ek|en-abus-rail.oga|d|-}, request "d" and FAILURE, since "d" in filename is bad)
::* expected result: "-"
::* actual result: "{{#invoke:maudilingdial|ek|en-abus-rail.oga|d|-}}"

* #T22 ({#invoke:maudilingdial|ek|entr-us-rail.oga|l|-}, bad "l" NOT found)
* expected result: "-"
* actual result: "{{#invoke:maudilingdial|ek|entr-us-rail.oga|l|-}}"

::* #T23 ({#invoke:maudilingdial|ek|entr-us-rail.oga|d|-}, "d" NOT found since "l" bad)
::* expected result: "-"
::* actual result: "{{#invoke:maudilingdial|ek|entr-us-rail.oga|d|-}}"

{{hr3}} <!-------------------------------->

* #T30 ({maudilingdial|ek|en-us-rail.oga|l|-|ling=id}, request "l", parameter "ling=" overrides)
* expected result: "id"
* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|l|-|ling=id}}"

::* #T31 ({maudilingdial|ek|en-us-rail.oga|l|-|dial=uk}, "dial=" supplied but request "l", no override)
::* expected result: "en"
::* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|l|-|dial=uk}}"

* #T32 ({maudilingdial|ek|en-us-rail.oga|d|-|dial=uk}, request "d", parameter "dial=" overrides)
* expected result: "UK"
* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|d|-|dial=uk}}"

::* #T33 ({maudilingdial|ek|en-us-rail.oga|d|-|dial=Fuk}, request "d", parameter "dial=" overrides)
::* expected result: "FUK"
::* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|d|-|dial=Fuk}}"

* #T34 ("dial=Fusk")
* expected result: "Fusk"
* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|d|-|dial=Fusk}}"

::* #T35 ("dial=(uk)", bad but acceptable for us)
::* expected result: "(uk)"
::* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|d|-|dial=(uk)<!--o-->}}"

* #T36 ("dial={uk}", rejected due to curly at begin)
* expected result: "US"
* actual result: "{{#invoke:maudilingdial|ek|en-us-rail.oga|d|-|dial={uk}<!--o-->}}"

{{hr3}} <!-------------------------------->

* #T40 ({maudilingdial|ek|LL-Q143 (epo)-Mojosulo-nenio.wav|l|damn}, request "l")
* expected result: "eo"
* actual result: "{{#invoke:maudilingdial|ek|LL-Q143 (epo)-Mojosulo-nenio.wav|l|damn}}"

::* #T41 ({maudilingdial|ek|LL-Q1405077 (zepo)-Mojosulo-nuenio.wav|l|damn}, request "l")
::* expected result: "kj"
::* actual result: "{{#invoke:maudilingdial|ek|LL-Q1405077 (zepo)-Mojosulo-nuenio.wav|l|damn}}"

* #T42 ({maudilingdial|ek|LL-Q143 (epo)-Mojosulo-nenio.wav|d|damn}, request "d", no dialect exists)
* expected result: "damn"
* actual result: "{{#invoke:maudilingdial|ek|LL-Q143 (epo)-Mojosulo-nenio.wav|d|damn}}"

::* #T43 ({maudilingdial|ek|LL-P143 (epo)-Mojosulo-nenio.wav|l|damn}, request "l")
::* expected result: "ll" (name is invalid for LLQ pattern and thus processed as traditional with bad result)
::* actual result: "{{#invoke:maudilingdial|ek|LL-P143 (epo)-Mojosulo-nenio.wav|l|damn}}"

* #T44 ({maudilingdial|ek|LL-Q99 (zep)-Mojosulo-nuen.wav|l|damn}, request "l", number too short)
* expected result: "damn"
* actual result: "{{#invoke:maudilingdial|ek|LL-Q99 (zep)-Mojosulo-nuen.wav|l|damn}}"

::* #T45 ({maudilingdial|ek|LL-Q9999999999 (zzp)-Malmojosulo-xxnuen.wav|l|toolong}, request "l", number too long)
::* expected result: "toolong"
::* actual result: "{{#invoke:maudilingdial|ek|LL-Q9999999999 (zzp)-Malmojosulo-xxnuen.wav|l|toolong}}"

* #T46 ({maudilingdial|ek|LL-Q1405077 (zep)-Mojosulo-nuenio.wav|l|damn|ling=SV}, request "l", parameter "ling=" overrides)
* expected result: "sv"
* actual result: "{{#invoke:maudilingdial|ek|LL-Q1405077 (zep)-Mojosulo-nuenio.wav|l|damn|ling=SV}}"

{{hr3}} <!-------------------------------->

]===]

local exporttable = {}

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

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

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

  -- uncommentable EO vs ID constant strings (core site-related features, "constrpriv" NOT needed)

  local constringvoj = "Modulo:loaddata-tbllingvoj"  -- EO
  -- local constringvoj = "Modul:loaddata-tblbahasa"    -- ID

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

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

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

---- SPECIAL VAR:S ----

local qldingvoj = {}     -- type "table" and nested
local qbooguard = false  -- only for the guard test, pass to other var ASAP

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-- Local function LFTESTLITERO

-- Test whether supplied char is an ASCII letter (65...90 and 97...122).

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

local function lftestlitero (numinkoo)
  local boorezult = false
  boorezult = lfgtestuc (numinkoo) or lfgtestlc (numinkoo)
  return boorezult
end--function lftestlitero

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

-- Local function LFFIXCASE

-- Adjust letter case of all ASCII letters in a string, other
-- char:s untouched.

-- Input  : * strmasuk -- string (empty tolerable)
--          * booupper -- "true" for uppercase and "false" for lowercase

-- Output : * strkeluar -- string

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

local function lffixcase (strmasuk,booupper)
  local strkeluar = ""
  local numlejn = 0
  local numindexx = 0 -- ZERO-based
  local numchar = 0
  numlejn = string.len (strmasuk)
  while true do
    if (numindexx==numlejn) then
      break
    end--if
    numchar = string.byte (strmasuk,(numindexx+1),(numindexx+1))
    if (lfgtestuc(numchar) and (not booupper)) then
      numchar = numchar + 32
    end--if
    if (lfgtestlc(numchar) and booupper) then
      numchar = numchar - 32
    end--if
    strkeluar = strkeluar .. string.char (numchar)
    numindexx = numindexx + 1
  end--while
  return strkeluar
end--function lffixcase

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

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

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

-- Local function LFVALI1X32NOCUR

-- Validate a string (length 1...32 octet:s, must not begin with "{").

-- Input  : * stryyn : string

-- Output : * booisvalid : "true" if the string is valid

local function lfvali1x32nocur (stryyn)
  local numomongkosong = 0
  local booisvalid = false -- preASSume guilt
  numomongkosong = string.len (stryyn)
  if ((numomongkosong~=0) and (numomongkosong<=32)) then
    numomongkosong = string.byte (stryyn,1,1)
    booisvalid = (numomongkosong~=123) -- curly "{" is a crime
  end--if
  return booisvalid
end--function lfvali1x32nocur

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

-- Local function LFPICKDASH

-- Pick a dash-delimited substring (2 or 3 char:s) from given
-- position of a string.

-- Input  : * straudio : string (example: "en-uk-nonsense.oga")
--          * numzpos  : ZERO-based position of start

-- Output : * strkode  : result (empty string if no pick)

-- Picking occurs if:
-- * remaining length from "numzpos" to end is at least 8 octet:s
-- * there is a dash at relative position + 2 or + 3
-- * there are at least 5 octet:s left after the dash at relative + 3
-- * char at given position is a letter
-- * char at next position and maybe overnext position is a letter

-- Depends on functions :
-- [G] lftestlitero

local function lfpickdash (straudio,numzpos)
  local strkode = ''
  local numlenn = 0
  local numdpos = 0 -- ZERO-based position of dash
  local numascii = 0
  local boogottri = false
  while true do -- fake loop
    numlenn = string.len (straudio)
    if ((numzpos+8)>numlenn) then
      break -- to join mark -- NOT at least 8 octet:s after "numzpos"
    end--if
    numascii = string.byte (straudio,(numzpos+3),(numzpos+3)) -- pos + 2
    if (numascii==45) then
      numdpos = numzpos+2 -- found a dash at + 2 !!!
    else
      numascii = string.byte (straudio,(numzpos+4),(numzpos+4)) -- pos + 3
      if ((numascii==45) and ((numzpos+8)<numlenn)) then
        numdpos = numzpos+3 -- found a dash and at least 9 char:s left !!!
        boogottri = true
      end--if
    end--if
    if (numdpos==0) then
      break -- to join mark -- no Boulder Dash found
    end--if
    numascii = string.byte (straudio,(numzpos+1),(numzpos+1))
    if (lftestlitero(numascii)==false) then
      break -- to join mark -- no letter at + 0
    end--if
    numascii = string.byte (straudio,(numzpos+2),(numzpos+2))
    if (lftestlitero(numascii)==false) then
      break -- to join mark -- no letter at + 1
    end--if
    if (boogottri) then
      numascii = string.byte (straudio,(numzpos+3),(numzpos+3))
      if (lftestlitero(numascii)==false) then
        break -- to join mark -- no letter at + 2
      end--if
    end--if
    strkode = string.sub (straudio,(numzpos+1),numdpos) -- do NOT include "-"
    break -- finally to join mark -- success
  end--while -- fake loop -- join mark
  return strkode
end--function lfpickdash

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

-- Local function LFVALILLQ

-- Detect possible LLQ pattern, return boolean.

-- Example: "LL-Q143 (epo)-Mojosulo-nenio.wav"

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

local function lfvalillq (straudiollq)
  local numjunklen = 0
  local numjunkchr = 0
  local booisllqjes = false
  while true do -- fake loop
    numjunklen = string.len (straudiollq)
    if (numjunklen<22) then
      break -- too short
    end--if
    if (string.sub(straudiollq,1,4)~="LL-Q") then
      break -- too bad
    end--if
    numjunkchr = string.byte (straudiollq,5,5)
    booisllqjes = lfgtestnum (numjunkchr) -- success or failure
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return booisllqjes
end--function lfvalillq

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

-- Local function LFPICKLLQ

-- Try to pick a Q-number (WikiData item) as a substring.

-- String must be already validated, among others >= 22 char:s. This means
-- that we do NOT have to bother about possible end of it since we run from
-- one-based pos 5 to 14 max far away from the dangerous end.

-- Need 3...9 digits for "Q100"..."Q999999999", actually "100"..."999999999".

-- Input  : * strllqaudi : string (example: "LL-Q143 (epo)-Anon-nenio.wav")

-- Output : * strkodde   : result as string (for example "Q143", empty
--                         string if no pick)

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

local function lfpickllq (strllqaudi)
  local strkodde = "Q" -- type "string" or "nil"
  local numddpos = 5 -- ONE-based position
  local numeating = 0 -- char code, start eating __AFTER__ the "Q"
  while true do
    if (numddpos==15) then
      break -- damn, number too long
    end--if
    numeating = string.byte (strllqaudi,numddpos,numddpos)
    if (not lfgtestnum(numeating)) then
      break -- good or bad
    end--if
    strkodde = strkodde .. string.char (numeating) -- "100"..."999999999" vali
    numddpos = numddpos + 1
  end--while
  if ((numddpos<8) or (numddpos==15)) then
    strkodde = "" -- 8 means 3 digits (good) ... 15 means 10 digits (bad)
  end--if
  return strkodde
end--function lfpickllq

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

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

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

function exporttable.ek (arxframent)

  -- general unknown type "var"

  local vartmp = 0     -- variable without type multipurpose

  -- special type "args" AKA "arx"

  local arxourown = 0  -- metaized "args" from our own "frame" (NOT caller's)

  -- general "str"

  local strnam    = ''  -- incoming parameter filename
  local strret    = ''  -- incoming parameter default output and output string
  local strling   = ''  -- override parameter or empty string
  local strdial   = ''  -- override parameter or empty string
  local strtump   = ''

  -- general "num"

  local numlong   = 0  -- length of parameter
  local numoct    = 0  -- temp some char

  -- general "boo"

  local booerr    = false  -- fatal error flag
  local boodia    = false  -- false for "l" and true for "d"

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

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

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

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

  booerr = qbooguard

  ---- PICK ONE SUBTABLE ----

  while true do -- fake loop

    if (booerr) then
      break -- to join mark
    end--if
    num2statcode = qldingvoj[2] -- risk of type "nil"
    if (num2statcode~=0) then
      booerr = true -- #E02 malica
      break -- to join mark
    end--if
    tab811colone = qldingvoj['T81-1'] -- WikiData Q-item -> langcode
    if (type(tab811colone)~='table') then -- important check
      booerr = true -- #E02 malica
      break -- to join mark
    end--if

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

  ---- SEIZE 3 ANON OBLIGATORY PARAMETERS FROM CALLER ----

  -- assign "strnam" and "boodia" and "strret"

  arxourown = arxframent.args -- "args" from our own "frame" only

  if ((arxourown[1]==nil) or (arxourown[2]==nil) or (arxourown[3]==nil) or (arxourown[4])) then
    booerr = true -- need 3 obligatory params, 4 are not appreciated
  else
    strnam = arxourown[1] -- name of the audio file
    numlong = string.len(strnam)
    if ((numlong<5) or (numlong>120)) then
      booerr = true -- must be 5...120 octet:s
    end--if
    strtump = arxourown[2] -- only "l" or "d" -- becomes a boolean
    numlong = string.len(strtump)
    if (numlong~=1) then
      booerr = true -- must be 1 octet
    else
      numoct = string.byte(strtump,1,1)
      if (numoct==100) then
        boodia = true
      else
        if (numoct~=108) then
          booerr = true -- only "l" or "d" tolerable
        end--if
      end--if (numoct==100) else
    end--if (numlong~=1) else
    strret = arxourown[3] -- string 1...32 -- next-worst default result
    numlong = string.len(strret)
    if ((numlong==0) or (numlong>32)) then
      booerr = true -- must be 1...32 octet:s
    end--if
  end--if

  ---- SEIZE 2 NAMED OPTIONAL PARAMETERS FROM CALLER ----

  -- seize (ling,dial) into (strling,strdial)

  -- both 1...32 octet:s, rejected if it begins with "{", but no "booerr=true"

  -- otherwise leave (strling,strdial) empty

  -- no error possible here

  vartmp = arxourown["ling"]
  if (type(vartmp)=="string") then
    if (lfvali1x32nocur(vartmp)) then
      strling = vartmp
    end--if
  end--if

  vartmp = arxourown["dial"]
  if (type(vartmp)=="string") then
    if (lfvali1x32nocur(vartmp)) then
      strdial = vartmp
    end--if
  end--if

  ---- CARRY OUT THE HARD WORK ----

  -- "strret" is prefilled from arxourown[3] with next-worst default
  -- result length 1...32 char:s, it CANNOT be empty (very worst is "=")

  -- (strling,strdial) independently contain override code or are empty

  if (booerr) then
    strret = "=" -- damn (hardcoded worst error string)
  else
    if (boodia) then
      strtump = strdial -- "d" override but can also be empty
    else
      strtump = strling -- "l" override but can also be empty
    end--if
    if (strtump~="") then
      strret = strtump -- repl nxwor defa fr arxourown[3] by "ling=" or "dial="
    else
      if (lfvalillq(strnam)) then -- LLQ pattern
        if (not boodia) then
          strtump = lfpickllq (strnam) -- attempt to pick substring
          if (strtump~='') then
            strtump = tab811colone[strtump] -- attempt convert into langcode
            if (type(strtump)~='string') then
              strtump = '' -- finally out of luck, no langcode
            end--if
          end--if
        end--if
      else
        strtump = lfpickdash (strnam,0) -- try to pick "l" but can be empty
        numlong = string.len (strtump)
        if (boodia and (numlong~=0)) then
          strtump = lfpickdash (strnam,(numlong+1)) -- try to pick "d" but ...
        end--if
      end--if (lfvalillq(strnam)) else
      if (strtump~="") then
        strret = strtump -- use successful pick, otherwise leave next-worst
      end--if
    end--if (strtump~="") else
    numlong = string.len (strret) -- "strret" CANNOT be empty here
    if ((numlong==2) or (numlong==3)) then
      strret = lffixcase (strret,boodia) -- lowercase "l" -- uppercase "d"
    end--if
  end--if

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

  return strret

end--function

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

return exporttable