Módulo:auto cat

Esta documentación está transcluida desde Módulo:auto cat/doc.
Los editores pueden experimentar en la zona de pruebas de este módulo.
Por favor, añade las categorías e interwikis a la subpágina de documentación. Subpáginas de este módulo.
local export = {}

local insert = table.insert
local concat = table.concat
local m_str = require("Módulo:String")

local strexplode = m_str.explode_utf8
local strfind = m_str.find
local strmatch = m_str.match
local strsub = m_str.gsub
local strsplit = m_str.split
local strlower = m_str.lower
local strupper = m_str.upper
local strucfirst = m_str.ucfirst
local strlcfirst = m_str.lcfirst
local substr = m_str.sub
local strlen = m_str.len
local m_leng = require("Módulo:lenguas")
local m_nombres = mw.loadData("Módulo:lenguas/nombre_a_cod")

local WIKCIONARIO = 1
local COD = 2
local SWADESH = 3
local TRADUCCIONES = 4
local RIMAS = 5
local PRON_GRAF = 6
local ETIMOLOGIA = 7
local GRAMATICA = 8
local CONTEXTO = 9
local LENGUAS = 10
local PLANTILLAS = 11
local MANTENIMIENTO = 12
local POR_IDIOMA = 13
local USUARIOS = 14
local ESCRITURAS = 15

local function obtener_alfabeto(args)
	-- por el momento, si no está standardChars, entonces no genero el alfabeto.
	-- En un futuro, podría hacer que genere el alfabeto que provee directamente el script,
	-- pero hay que implementar la función que devuelva eso porque la que está ahora mismo devuelve un rango de caracteres en regex
	if not args["idioma"] or not args["idioma"].standardChars then 
		return ""
	end
	local ignorar_script = {["Brai"] = true, ["Sgnw"] = true}
	local abc = {}
	if type(args["idioma"].standardChars) == "string" then
		abc[1] = args["idioma"].standardChars
	else
		local m_sc = require("Módulo:scripts")
		for script in args["idioma"][4]:gmatch("([^,]+)%s*,?%s*") do -- tomo todos los scripts que haya, y para cada uno busco el alfabeto
			if script == "All" then
				return ""
			end
			if not ignorar_script[script] then
				local chars = m_sc.getByCode(script):getCharacters()
				script_ = args["idioma"].standardChars[script]
				if script_ then 
					insert(abc, strexplode(strupper(script_)))
				end
			end
		end
	end
	local t = {}
	for _,alfabeto in ipairs(abc) do
		if alfabeto then
			local mem = {["-"] = true} --ignoro el guion porque eso es parte de los regex en el caso de los alfabetos obtenidos directo del script
			insert(t, "<div style=\"font-size:90%; text-align:center\">\n")
			insert(t, "{|id=\"toc\" class=\"plainlinks\" align=\"center\" style=\"padding:3px 15px 3px 15px;\"\n")
			insert(t, "|style=\"text-align: center; font-weight:bold; font-size:100%; padding-left:15px; padding-right:15px\"|\n")
			local pg = args["pagename"]
			for i,c in ipairs(alfabeto) do
				if not mem[c] then
					mem[c] = true
					insert(t, "["..tostring(mw.uri.canonicalUrl("Categoría:"..pg, "from="..c)).." "..c.."]&nbsp;&nbsp;\n")
				end
			end
			insert(t, "|}\n</div>\n")
		end
	end
	return concat(t)
end

local function generar_titulo(texto, args)
	local t = {}
	insert(t, "<div style=\"background-color:#f6f6f6; border:1px dotted #ABCDEF; padding:8px; width:95%; margin:0 auto;\">\n")
	if texto then
		if args["Idioma"] then
			insert(t, "<div style=\"font-size:110%; text-align:center\">"..args["Idioma"]..": "..texto)
		else
			insert(t, "<div style=\"font-size:110%; text-align:center\">"..texto)
		end
		if args["oculta"] then
			insert(t, "__HIDDENCAT__<br>Esta es una categoría de mantenimiento que no se muestra directamente en los artículos. Si deseas habilitar la visualización de estas categorías, puedes hacerlo en [[Especial:Preferencias|tus preferencias]].")
		end
		insert(t, "</div>\n")
	end
	return concat(t)..obtener_alfabeto(args).."</div>\n"
end

local function generar_aviso(tipo, texto)
	local header = {
		["info"] = "<table style=\"aviso\" class=\"plainlinks ambox ambox-notice\">"
	}
	
	local imagen = {
		["info"] = "[[Imagen:Emblem-notice.svg]]"
	}
	
	return header[tipo].."<tr><td class=\"ambox-image\"><div style=\"width:52px;\">"..imagen[tipo].."</div></td><td class=\"ambox-text\">"..texto.."</td></tr></table>"
end

local function generar_categorias(padres, args)
	if not args["COD"] then
		return padres
	end
	local padres_ = {}
	for _,padre in ipairs(padres) do
		if padre and padre ~= "" then
			insert(padres_, args["COD"]..":"..padre)
		else
			insert(padres_, args["Idioma"]..'| ')
		end
	end
--	if args["wik"] then
--		insert(padres_,"Wikcionario")
--	end
    return padres_
end

local handlers = {}

-- Detección de "Wikcionario"
handlers[WIKCIONARIO] =
function(clave, args)
	if clave == "Wikcionario" then
		args["wik"] = true
	end
end

-- Detección de código de idioma en mayúscula
handlers[COD] = 
function(clave, args)
	local x,y,z
	if strfind(clave, "^%u%u+$") or strfind(clave, "^%u%u+%-%u%u+$") or strfind(clave, "^%u%u+%-%u%u+%-%u%u+$") then
		x = strlower(clave)
		y = clave
		z = m_leng.cod_a_idioma(x)
	end
	if x and y and z then
		args["cod"] = x
		args["COD"] = y
		args["idioma"] = z
		args["Idioma"] = strucfirst(z[1])
		return false
	end
	return nil
end

--Lista Swadesh
handlers[SWADESH] = 
function(clave, args)
	if clave ~= "Lista Swadesh" then
		return nil
	end
	return true, generar_titulo("Lista de [[w:Lista Swadesh|vocabulario básico altamente resistente a préstamos]], concepto del lingüista [[w:Morris Swadesh|Mauricio Swadesh]].", args), generar_categorias({""}, args)
end

--Traducciones: debería estar en mantenimiento??
handlers[TRADUCCIONES] =
function (clave, args)
	local m = {strmatch(clave, "^([a-zA-ZÀ-ž ]+)%-([a-zA-ZÀ-ž ]+)$")}
	
	if not m[1] or not m[2] then
		m = strfind(clave, "^Traducciones desde el español$")
		if not m then
			return nil
		end
		args["oculta"] = true
		return true, generar_titulo("En esta categoría se encuentran todas las entradas de traducción desde el español.", args), {}
	end
	
	local a = strlcfirst(m[1])
	local b = strlcfirst(m[2])
	
	local idioma1, idioma2 = m_nombres[a], m_nombres[b]
	
	if not idioma1 or not idioma2 then
		return nil
	end
	args["oculta"] = true
	return true, generar_titulo("En esta categoría se encuentran todas las entradas de traducción del idioma "..a.." al "..b..".", args), {"Traducciones desde el "..a}
end

--Rimas
handlers[RIMAS] =
function (clave, args)
	if clave == "Rimas" then
		if args["i_"] == args["L_"] then
			args["no sufijo"] = true
			return true, generar_titulo("Lista de rimas.", args), generar_categorias({""}, args)
		else
			args["Rimas"] = true
			return nil
		end
	elseif args["Rimas"] then
		return true, generar_titulo("Lista de rimas con -"..clave..".", args), generar_categorias({"Rimas|"..clave}, args)
	end
	return nil
end

-- Pron-graf
handlers[PRON_GRAF] = 
function(clave, args)
	local padres = mw.loadData("Módulo:auto cat/pron-graf")[clave]
	if not padres then
		return nil
	end
	if clave == "Palabras sin transcripción fonética" then
		args["oculta"] = true
		args["no sufijo"] = true
	end
	return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args), generar_categorias(padres, args)
end

-- Etimología
handlers[ETIMOLOGIA] =
function (clave, args)
	-- Etimología de otro idioma
	local m = {strmatch(clave, "^Palabras provenientes del ([a-zA-ZÀ-ž ]+)$")}
	if m[1] then
		args["no sufijo"] = true
		return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args),  generar_categorias({"Palabras por idioma de origen|"..m[1]}, args)
	end
	m = {strmatch(clave, "^Palabras documentadas desde el siglo [IVX]+$")}
	if m[1] then
		args["no sufijo"] = true
		return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args),  generar_categorias({"Palabras por época de origen"}, args)
	end
	-- Prefijos, infijos, sufijos
	m = {strmatch(clave, "^Palabras con el prefijo (.*)$")}
	if m[1] then
		args["no meta"] = true
		return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args),  generar_categorias({"Palabras formadas por prefijación|"..m[1]}, args)
	end
	m = {strmatch(clave, "^Palabras con el infijo (.+)$")}
	if m[1] then
		args["no meta"] = true
		return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args),  generar_categorias({"Palabras formadas por infijación|"..m[1]}, args)
	end
	m = {strmatch(clave, "^Palabras con el sufijo -(.+)$")}
	if m[1] then
		args["no meta"] = true
		return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args),  generar_categorias({"Palabras formadas por sufijación|"..m[1]}, args)
	end
	if clave == "Palabras de etimología sin precisar" then
		args["no sufijo"] = true
		args["oculta"] = true
	end
	-- Otros tipos de etimologías
	local padres = mw.loadData("Módulo:auto cat/etimología")[clave]
	if not padres then
		return nil
	end
	
	return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args), generar_categorias(padres, args)
end

--Gramática
handlers[GRAMATICA] =
function (clave, args)
	local padres = mw.loadData("Módulo:auto cat/gramática")[clave]
	if not padres then
		return nil
	end
	args["no sufijo"] = true
	return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args), generar_categorias(padres, args)	
end

--Csem, ambito, uso
handlers[CONTEXTO] =
function (clave, args)
	if strfind(clave, "^Idiomas de [^a-z]") then
		return true, generar_titulo(clave..".", args), generar_categorias({"Idiomas"}, args)	
	end
	local padres = mw.loadData("Módulo:auto cat/uso")[clave]
	if padres then
		return true, generar_titulo("Lista de "..clave..".", args), generar_categorias(padres, args)
	end
	
	local padres = mw.loadData("Módulo:auto cat/campos semánticos")[clave]
	if padres then
		return true, generar_titulo("Lista de términos relacionados con "..strlcfirst(clave)..".", args), generar_categorias(padres, args)
	end
	
	local padres = mw.loadData("Módulo:auto cat/ámbito")[clave]
	if padres then
		args["no sufijo"] = true
		return true, generar_titulo("Lista de términos propios de "..clave..".", args), generar_categorias(padres, args)
	end
	
	-- casos especiales (serán muchos)
	--- Topónimos
	m = {strmatch(clave, "^Topónimos de (.+)$")}
	if m[1] then
		return true, generar_titulo("Lista de "..strlcfirst(clave), args), generar_categorias({'Topónimos|'..m[1]}, args)	
	end
	return nil
	
end

--Nombre de idioma o familia
handlers[LENGUAS] =
function (clave, args)
	clave = strlcfirst(clave)
	local cod = m_nombres[clave]
	if not cod then
		return nil
	end
	
	local es_familia = strmatch(clave, "^lenguas")

	local idioma
	if not es_familia then
		idioma = m_leng.cod_a_idioma(cod)
	else
		idioma = m_leng.cod_a_familia(cod)
	end
	local dag, familia = m_leng.obtener_dag(idioma, es_familia)
	local arbol = require("Módulo:DAG").dagAHtml(dag, "Árbol genealógico")
	
	local t = {}
	local i = 1
	local function insert(s)
		t[i] = s
		i = i + 1
	end
	
	local cat = {}
	local i_ = 1
	local function insert_cat(s)
		cat[i_] = s
		i_ = i_ + 1
	end
	
	-- En realidad final va al comienzo de todo, me confundí xd
	local final = {}
	local i__ = 1
	local function insert_final(s)
		final[i__] = s
		i__ = i__ + 1
	end
	
	
	insert("=== Información ===\n")
	insert(":* '''Código''': <code>"..cod.."</code>\n")
	insert(":* '''Nombre canónico''': "..clave.."\n")
	insert(":* '''ID de Wikidata''': [https://www.wikidata.org/wiki/Q"..tostring(idioma[2]).." "..tostring(idioma[2]).."]\n")
	
	if familia then
		insert(":* '''Familia''': [[:Categoría:"..familia.."|"..familia.."]]\n")
		insert_cat(familia)
	else
		insert_cat("Lenguas")
	end
	
	if not es_familia then
		insert(":* '''Script(s)''': ")
		
		for script in idioma[4]:gmatch("([^,]+)%s*,?%s*") do
			insert("[[Wikcionario:Lista de scripts#" .. script .. "|<code>" .. script .. "</code>]] ")
			--insert_cat("Script "..script)
		end
		insert_cat("Idiomas")
		insert("\n")
	end
	
	if idioma.otherNames then
		insert(":* '''Otros nombres''': ")
		for l,n in ipairs(idioma.otherNames) do
			if l == 1 then
				insert(n)	
			else
				insert(", "..n)
			end
		end
		insert(".\n")
	end
	
	insert("=== Véase también ===\n")
	if not es_familia then
		insert(":* [[File:Wiktionary-logo.svg|25px]] [["..clave.."|Entrada]] en el Wikcionario.\n")
	end
	
	local titulo_wikipedia = mw.wikibase.sitelink("Q"..tostring(idioma[2]), 'eswiki')
	if titulo_wikipedia then
		insert(":* [[File:Wikipedia-logo.svg|25px]] [[w:"..titulo_wikipedia.."|Entrada]] en Wikipedia.\n")
	end
	
	insert(":* [[File:Wikidata-logo.svg|25px]] [[wikidata:Q"..tostring(idioma[2]).."|Entrada]] en Wikidata.\n")
	insert(":* [[File:Commons-logo.svg|25px]] [[commons:"..clave.."|Búsqueda]] en Wikimedia Commons.\n")
	--insert(":* [[Wikcionario:Convenciones para nombrar entradas/"..cod.."|Convenciones]] para nombrar entradas.\n")
	
	if not es_familia then
		insert(":* [[Wikcionario:Códigos de idioma|Lista]] completa de idiomas.\n")	
	else
		insert(":* [[Wikcionario:Familias de idiomas|Lista]] completa de familias.\n")
	end
	
	local im = mw.title.new("Lang-"..cod..".gif", 6)
    if im and im.fileExists then
    	insert_final("[[File:Lang-"..cod..".gif|200px]]\n\n")
    end

	
	insert_final("Esta es la categoría principal ")
	if not es_familia then
		insert_final("del idioma "..clave..".\n")
	else
		insert_final("de las "..clave..".\n")
	end
	
	local hay_arg = false
	for j, arg in ipairs(args) do
		if j == 1 then
			insert_final("Se habla en: [["..args[1].."]]")
			insert_cat("Idiomas de "..args[1])
		else
			insert_final(", [["..args[j].."]]")
			insert_cat("Idiomas de "..args[j])
		end
		hay_arg = true
	end
	
	if hay_arg then
		insert_final(".\n")	
	end
  
	-- https://stackoverflow.com/questions/31455768/html-table-cell-spacing-only-between-cells-no-outer-one
	return true, "<table border=0 cellspacing=0 cellpadding=0 style=\"margin:-10px;width:99.9%;border-spacing:10px;\">\n<tr>\n<td style=\"text-align:left;vertical-align:top;\">\n"..concat(final)..concat(t).."</td>\n<td style=\"text-align:center;\">\n"..arbol.."</td>\n</tr>\n</table>\n", cat
end

-- Plantillas (muy ampliable)
handlers[PLANTILLAS] =
function (clave, args)
	m = {}
	if strmatch(clave, "Plantillas de flexión de") 
		or strmatch(clave, "Plantillas auxiliares de flexión")
		or strmatch(clave, "Plantillas de mutación") then
		args["no sufijo"] = true
		m[clave] = {"Plantillas de flexión"}
	elseif strmatch(clave, "Plantillas$") then
		args["no sufijo"] = true
		m[clave] = {}
	elseif strmatch(clave, "Plantillas") then
		args["no sufijo"] = true
		m[clave] = {"Plantillas"}
	end

	local padres = m[clave]
	if not padres then
		return nil	
	end
	return true, generar_titulo("Lista de "..strlcfirst(clave)..".", args), generar_categorias(padres, args)	
end

-- Detección de Mantenimiento
handlers[MANTENIMIENTO] = 
function(clave, args)
--	if not args["wik"] then
--		return nil
--	end
	local m = mw.loadData("Módulo:auto cat/mantenimiento")
	if m[clave] then
		return true, generar_aviso("info", "__HIDDENCAT__"..
			"Esta categoría es de mantenimiento"..
			" y por lo tanto no se mostrará directamente. Si desea que se "..
			"muestre en cada página, puede cambiar "..
			"[[Especial:Preferencias|sus preferencias]]."),
			generar_categorias(m[clave], args)
		
		-- TODO: Nombres de categorías. ¿Eliminar Wikicionario:? Creo q es razonable 
		-- dejar esas categorías sólo para páginas del espacio Wikcionario
	end
end

-- Contrucción de metacategorías por idioma
handlers[POR_IDIOMA] = 
function(clave, args)
	if args["metacat"] then
		return nil -- por las dudas	
	end
	
	local m = {strmatch(clave, "^(.+) por idioma$")}
	local clave_ = m[1]
	
	if clave_ then
		args["metacat"] = true -- útil?
		for i,handler in ipairs(handlers) do -- cómo evito que vuelva a pasar recursivamente de forma ilimitada
			local padres_ = {}
	    	local terminar, msj, padres = handler(clave_, args)
	    	if terminar == true then
				for i, padre_ in ipairs(padres) do
					if padre_ ~= "" then
	    				insert(padres_, padre_..' por idioma| ')
						insert(padres_, clave_..'| ')
	    			else
	    				-- puede tener un mejor nombre? creo que sí
	    				insert(padres_, 'Metacategorías espejo| ')
	    				insert(padres_, clave_..'| ')
	    			end
	    		end	    		
				return true, 
					-- quizá mejorar este título
					generar_titulo("Metacategoría de ''"..strlcfirst(clave_).."'' por idioma", args), 
					generar_categorias(padres_, args)			
    		elseif terminar == false then -- si no es nil
    			break
	    	end
	    end
	end
	return nil
end

-- Usuarios por idioma, debería fusionarlo con el handler de arriba?
handlers[USUARIOS] = 
function(clave, args)
	local m = {strmatch(clave, "^Usuarios por idioma %- ?(.+)$")}
	
	if not m[1] then
		return nil
	end
	
	local c = {"0", "básico", "intermedio", "avanzado", "experto", "nativo"}
	local r = nil
	local s = nil
	for _,q in ipairs(c) do
		local m2 = {strmatch(m[1], "^(.+) ?"..q.."$")}
		if m2[1] then
			r = m2[1]
			s = q
			break
		end
	end
	
	local padres_ = {}
	
	if not r then
		insert(padres_, "Usuarios por idioma")
		return true, generar_titulo("Lista de usuarios que hablan "..m[1], args), generar_categorias(padres_, args)
	else
		insert(padres_, "Usuarios por idioma - "..r)
		if s == "0" then
			return true, generar_titulo("Lista de usuarios que NO entienden "..r, args), generar_categorias(padres_, args)
		else
			return true, generar_titulo("Lista de usuarios que hablan "..r..", nivel "..s, args), generar_categorias(padres_, args)
		end
	end
end

-- Escrituras
handlers[ESCRITURAS] =
function(clave, args)
	local m_escrituras = mw.loadData("Módulo:auto cat/escrituras")
	local codigos = m_escrituras[clave]
	if codigos then
		for _,cod in ipairs(codigos) do
			if cod == args["COD"] then
				return true, generar_titulo("Lista de palabras pertenecientes a la escritura "..clave..".", args), generar_categorias({"Escrituras"}, args)
			end
		end
	end
	local m = strfind(clave, "^Escrituras$")
	if m then
		return true, generar_titulo("Lista de escrituras.", args), generar_categorias({}, args)
	end
	return nil
end

function export.categorizar(frame)
	local title = mw.title.getCurrentTitle().fullText
    if title == "Plantilla:auto cat" then
    	return "Use esta plantilla en el espacio de Categorías."	
    end
	
	local pagename = mw.title.getCurrentTitle().text
	if pagename == "Módulo:auto cat" then
		return nil	
	end
	    
	local ns = mw.title.getCurrentTitle().namespace
    assert(ns == 14, "La plantilla sólo puede utilizarse en el espacio de Categorías")
    
    local args = frame:getParent().args
    args["pagename"] = pagename
    
    local claves = strsplit(pagename, ":")
    local L = #claves
    args["i_"] = 1
    args["L_"] = L
    
	local t = {}
    
    while args["i_"] <= L do
    	local clave = claves[args["i_"]]
	    for i,handler in ipairs(handlers) do
	    	local terminar, msj, padres = handler(clave, args)
	    	if terminar == true then
    			insert(t, msj)
    			-- Paso 1: metacategorías
    			-- Caso a: "no meta" = no metacategoría x idioma
    			if args["no meta"] then
    				-- nada
    			-- Caso b: sin sufijo
    			elseif args["COD"] and L == 2 and args["no sufijo"] then
    				insert(t, "[[Categoría:"..clave.."|"..args["COD"].."]]")
    			-- Caso c: caso general
    			elseif args["COD"] and L == 2 then 
    				insert(t, "[[Categoría:"..clave.." por idioma|"..args["COD"].."]]")
    			-- Caso d: eventualmente eliminar Categorías:Wikicionario: 
    			-- salvo para páginas realmente internas. Por ahora mantener
    			elseif args["COD"] and args["wik"] and L == 3 then -- caso para Categoría:Wikcionario:XXXX
    				insert(t, "[[Categoría:Wikcionario:"..claves[3].."|"..args["COD"].."]]")
    			end
    			-- Paso 2: árbol de categorías
    			for i,padre in ipairs(padres) do
    				-- Caso a: Categorías fundamentales. Padre "", sin código
    				if not args["wik"] and not args["COD"] and padre == "" then
    					insert(t, "[[Categoría:Fundamental| ]]\n")
    				elseif padre ~= "" then
    					insert(t, "[[Categoría:"..padre.."| ]]\n")
    				end
    			end
    			return concat(t)
    		elseif terminar == false then -- si no es nil
    			break
	    	end
	    end
	    args["i_"] = args["i_"] + 1
    end
	if args["wik"] then
		return nil
	end
    error("La categoría no puede ser reconocida.")
end

return export