Module:Utilisateur:Maëlan/wd-langue
La documentation de ce module est générée par le modèle {{Documentation module}}.
Les éditeurs peuvent travailler dans le bac à sable (créer).
Voir les statistiques d'appel depuis le wikicode sur l'outil wstat et les appels depuis d'autres modules.
-- 2024-06-05
-- un module pour obtenir le nombre de locuteurs de langues depuis Wikidata.
--------------------------------------------------------------------------------
-- les bibliothèques dont on dépend:
Args = require("Module:Arguments")
Wd = require("Module:Wikidata")
FmtNum = require("Module:Conversion")
-- notre module:
local M = {}
-- valeurs configurables:
local arrondi_par_defaut = 2
local inconnu = '<abbr title="nombre inconnu de locuteurs">?</abbr>'
-- arrondit la valeur donnée au nombre de chiffres significatifs demandé
-- (0 signifie pas d’arrondi):
local function arrondir(x, nb_chiffres_significatifs)
if x == 0 or nb_chiffres_significatifs <= 0 then
return x
else
local p10 = 10 ^ (math.floor(math.log10(math.abs(x))) + 1 - nb_chiffres_significatifs)
return p10 * math.floor(x / p10 + 0.5)
end
end
-- génère un message d’erreur en wikicode:
local function erreur(msg)
local txt = '<strong class="error">Usage erroné de {{nb locuteurs}} : '
txt = txt .. msg
txt = txt .. '</strong>'
return txt
end
-- enveloppe de la fonction `Wd.filterClaims` qui corrige deux choses:
-- (1) ne modifie pas le tableau d’origine, renvoie un nouveau tableau;
-- (2) renvoie un tableau vide plutôt que `nil`.
local function filterClaims(list_of_claims, params)
local copied_list_of_claims = { unpack(list_of_claims) }
return Wd.filterClaims(copied_list_of_claims, params) or { }
end
-- fonction principale:
function M._nb_locuteurs(args)
-- paramètre obligatoire 1:
-- détermine le type de locuteurs (L1 | L2 | L1+L2):
local L1 = false -- langue maternelle
local L2 = false -- langue seconde
local LE = false -- langue étrangère
if args[1] == "L1" then
L1 = true
elseif args[1] == "L2" then
L2 = true
elseif args[1] == "LE" then
LE = true
elseif args[1] == "L1+L2" then
L1 = true
L2 = true
elseif args[1] == "L1+L2+LE" then
L1 = true
L2 = true
LE = true
else
return erreur("le type de locuteurs doit être L1, L2, LE, L1+L2 ou L1+L2+LE")
end
-- paramètre optionnel «arrondi»:
-- détermine le nombre de chiffres significatifs;
-- 0 signifie pas d’arrondi:
local arrondi = nil
if args.arrondi ~= nil then
arrondi = tonumber(args.arrondi)
end
if arrondi == nil or arrondi < 0 then
arrondi = arrondi_par_defaut
end
-- on va sommer le nombre de locuteurs de toutes les langues demandées:
local somme = 0
-- paramètre 2:
-- liste de langues à additionner, séparées par "+";
-- les langues, en tant qu’entités Wikidata, sont identifiées par le titre de
-- leur page associée dans un certain wiki (ne tolère pas les redirections),
-- par exemple:
-- "enwiki:French language"
-- "frwiki:français"
-- en l’absence d’indication de wiki, c’est le wiki courant qui est utilisé
-- (c’est-à-dire ici la Wikipédia francophone).
-- cas limite où le paramètre n’est pas fourni:
-- on renvoie 0 (somme de zéro langue) plutôt qu’une erreur:
if args[2] == nil or args[2] == "" then
return "0"
end
-- pour chaque langue demandée…
for titre_langue in mw.text.gsplit(args[2], "+", --[[plain=]]true) do
titre_langue = mw.text.trim(titre_langue)
-- obtient son identifiant Wikidata à partir de son titre wiki associé:
local id_entite = nil
local i = mw.ustring.find(titre_langue, ":", --[[init=]]1, --[[plain=]]true)
if i ~= nil then -- wiki spécifié
local site = mw.ustring.sub(titre_langue, 1, i-1)
local titre_langue_dans_site = mw.ustring.sub(titre_langue, i+1)
id_entite = mw.wikibase.getEntityIdForTitle(titre_langue_dans_site, site)
else -- wiki par défaut
id_entite = mw.wikibase.getEntityIdForTitle(titre_langue)
end
if id_entite == nil then
return erreur("langue inconnue : [[" .. titre_langue .. "]]")
end
-- télécharge toutes les affirmations de cette entité
-- à propos du nombre de locuteurs (propriété P1098):
local toutes_affirmations_P1098 = mw.wikibase.getAllStatements(id_entite, "P1098")
-- s’il n’y a aucune telle affirmation, on obtient une liste vide;
-- inutile de traiter ce cas ici, il est pris en charge par ce qui suit.
--if #toutes_affirmations_P1098 == 0 then ... end
-- Dans la suite, on suppose qu’une affirmation de nombre de locuteurs
-- sans qualificatif «langue maternelle/seconde/étrangère» dans Wikidata
-- correspond à un nombre de locuteurs L1 (plutôt que L1+L2).
-- obtient le nombre de locuteurs L1 si nécessaire:
local locuteurs_L1 = nil
if L1 then
-- choisit la meilleure affirmation portant le qualificatif «langue maternelle»:
local affirmations_L1 = filterClaims(toutes_affirmations_P1098, {
-- seulement «langue maternelle»:
qualifier="P518", qualifiervalue="Q36870",
-- exclut les affirmations «hypothétiques»:
excludequalifier="P1480", excludequalifiervalue="Q18603603",
-- seulement les affirmations «préférées»; à défaut, les «normales»:
rank="best",
-- s’il reste plusieurs affirmations, ne garde que la plus récente:
sorttype="inverted", numval=1,
})
-- si aucune affirmation ne correspond,
-- recommence sans le critère «langue maternelle»
-- (une valeur est parfois fournie sans être qualifiée comme telle):
if #affirmations_L1 == 0 then
affirmations_L1 = filterClaims(toutes_affirmations_P1098, {
excludequalifier="P1480", excludequalifiervalue="Q18603603",
rank="best", sorttype="inverted", numval=1,
})
-- s’il n’y a vraiment rien, renvoie un nombre inconnu de locuteurs
-- (inutile de traiter les autres langues, la somme serait faussée)
if #affirmations_L1 == 0 then
return inconnu
end
end
-- transforme l’affirmation Wikidata en nombre brut:
locuteurs_L1 = Wd.formatStatement(affirmations_L1[1], { displayformat="raw" })
locuteurs_L1 = tonumber(locuteurs_L1)
end
-- obtient le nombre de locuteurs L2 si nécessaire:
local locuteurs_L2 = nil
if L2 then
-- choisit la meilleure affirmation portant le qualificatif «langue seconde»:
local affirmations_L2 = filterClaims(toutes_affirmations_P1098, {
-- seulement «langue seconde»:
qualifier="P518", qualifiervalue="Q125421",
excludequalifier="P1480", excludequalifiervalue="Q18603603",
rank="best", sorttype="inverted", numval=1,
})
-- si aucune affirmation ne correspond,
-- considère que le nombre de locuteurs L2 est zéro (plutôt qu’inconnu):
if #affirmations_L2 == 0 then
locuteurs_L2 = 0
else
-- transforme l’affirmation Wikidata en nombre brut:
locuteurs_L2 = Wd.formatStatement(affirmations_L2[1], { displayformat="raw", default=0 })
locuteurs_L2 = tonumber(locuteurs_L2)
end
end
-- obtient le nombre de locuteurs LE si nécessaire:
local locuteurs_LE = nil
if LE then
-- choisit la meilleure affirmation portant le qualificatif «langue seconde»:
local affirmations_LE = filterClaims(toutes_affirmations_P1098, {
-- seulement «langue étrangère»:
qualifier="P518", qualifiervalue="Q150352",
excludequalifier="P1480", excludequalifiervalue="Q18603603",
rank="best", sorttype="inverted", numval=1,
})
-- si aucune affirmation ne correspond,
-- renvoie un nombre inconnu de locuteurs
-- (inutile de traiter les autres langues, la somme serait faussée)
if #affirmations_LE == 0 then
return inconnu
else
-- transforme l’affirmation Wikidata en nombre brut:
locuteurs_LE = Wd.formatStatement(affirmations_LE[1], { displayformat="raw" })
locuteurs_LE = tonumber(locuteurs_LE)
end
end
-- ajoute tous les nombres:
if locuteurs_L1 ~= nil then
somme = somme + locuteurs_L1
end
if locuteurs_L2 ~= nil then
somme = somme + locuteurs_L2
end
if locuteurs_LE ~= nil then
somme = somme + locuteurs_LE
end
end -- /boucle sur chaque langue
-- arrondit la valeur au nombre de chiffres significatifs demandé,
-- la formate joliment en HTML et la renvoie:
--
-- NOTE: `FmtNum.displayvalue` présente 3 bugs :
-- (1) la fonctionnalité qui abrège les nombres au moyen des suffixes
-- "million (M), milliard (G)" arrondit à 1 chiffre après la virgule
-- (donc à 100_000 près dans le cas de "million"
-- et à 100_millions près dans le cas de "milliard")
-- même si on a demandé une précision + grande via le paramètre `rounding`
-- => tant pis, il faudrait recoder la fonction…
-- (2) si on utilise sa fonctionnalité d’arrondi (paramètre `rounding`),
-- alors le résultat n’est pas abrégé en "million, milliard"
-- => on fait l’arrondi nous-même
local somme_arrondie = arrondir(somme, arrondi)
-- (3) si le nombre est exactement 10^6 ou 10^9,
-- alors le résultat n’est pas abrégé en "million, milliard"
-- => on contourne le problème en faisant +1 (qui disparaitra avec l’arrondi, cf (1))
if somme_arrondie == 1000000 or somme_arrondie == 1000000000 then
somme_arrondie = somme_arrondie + 1
end
-- formate le nombre lisiblement pour un humain:
local txt = FmtNum.displayvalue(somme_arrondie, "", {
--rounding=arrondi-1-math.floor(math.log10(somme)),
showunit="short",
})
-- utilise des espaces insécables fines pour séparer les milliers
-- (remplace les espaces insécables non-fines):
txt = mw.ustring.gsub(txt, " ", " ") -- ici le motif est une NBSP
txt = mw.ustring.gsub(txt, " ", " ")
--txt = mw.ustring.gsub(txt, "([0-9]) %f[0-9]", "%1 ") -- ici le motif est une espace normale entourée de chiffres
-- ajoute une clé de tri (requis pour un tri correct dans un tableau triable):
txt = '<span data-sort-value="' .. tostring(somme) .. '">' .. txt .. '</span>'
return txt
end
-- fonction d’entrée, invoquée par [[Modèle:nb locuteurs]]:
function M.nb_locuteurs(frame)
return M._nb_locuteurs(Args.getArgs(frame))
end
-- publication de notre module:
return M
--------------------------------------------------------------------------------
-- OPTIONS UTILES DU [[Module:Wikidata]]:
-- entity=....
-- property="P1098" -- nombre de locuteurs
-- filtrage:
-- qualifier="P518", qualifiervalue="Q36870" -- seulement langue maternelle
-- qualifier="P518", qualifiervalue="Q125421" -- seulement langue seconde
-- qualifier="P518", qualifiervalue="Q150352" -- seulement langue étrangère
-- excludequalifier="P1480", excludequalifiervalue="Q18912752,Q18603603"
-- --^ exclut les assertions resp. «controversées» ou «hypothétiques»,
-- -- ce dernier qualificatif indiquant une projection dans le futur
-- sorttype="inverted" -- ou "chronological"
-- --^ tri des valeurs par ordre chrono décroissant ou croissant
-- numval=1 -- au plus une seule valeur
-- formatage:
-- default=0
-- displayformat="raw" -- nombre brut
-- rounding=N-1-math.floor(math.log10(X))
-- --^ arrondit X à N chiffres significatifs (option du [[Module:Conversion]])
-- showunit="short" -- abréger "million" en "M" (option du [[Module:Conversion]])
-- showqualifiers="P518" -- indicateur (langue maternelle/seconde/étrangère)
-- showdate="yes"
-- WIKIDATA CHEATSHEET:
--[[
-- 1: find the entity id (such as "Q150") associated to a given wikipedia page:
local entity_id = mw.wikibase.getEntityIdForTitle("French Language", "enwiki")
-- => returns nil if not found
-- 2: fetch this entity’s data (that’s the costly step):
local entity = mw.wikibase.getEntity(entity_id) -- Wd.getEntity(entity_id) is just a proxy
-- 3: select property P1098 (number of speakers):
local all_claims_about_P1098 = entity.claims.P1098
-- => fails if no such claim
-- shortcut for 2+3:
local all_claims_about_P1098 = mw.wikibase.getAllStatements(entity_id, "p1098")
-- => returns empty array if no such claim
-- 4: filter claims by “preferred” and such:
local filtered_claims = Wd.filterClaims(all_claims_about_P1098, { })
--^ insert additional filtering options between {}
-- => returns nil(!) if there is no claim left
-- also, beware that it modifies the array of claims in place(!)
-- shortcut for 2+3+4, but does not allow additional filtering options:
local best_claims = mw.wikibase.getBestStatements(entity_id, "P1098")
-- 5: format every claim as wikicode text:
for _, claim in pairs(filtered_claims) do
print(Wd.formatStatement(claim, { })) --< insert additional formatting options between {}
end
-- shortcut for 3+4+5: filter, format and concatenate claims,
-- starting from the whole entity’s data:
print(Wd.formatStatements({ entity=entity, property="P1098" }))
--^ insert additional filtering and formatting options between {}
-- => returns nil if no such claim
--]]
-- BASIC LUA SYNTAX REMAINDER:
--[[
-- table (array/dictionary):
local my_dict = {
["key1"] = { "", "x", "y" },
["keya"] = { aa="", bb="z" },
}
-- `mydict.key` is sugar for `mydict["key"]`
-- debugging tip: print the structure of any object
-- (arbitrary nest of Lua “tables“):
print(mw.logObject(my_object))
-- the table datastructure is used to encode both arrays and dictionaries;
-- arrays are indexed by successive integers starting at 1 (!! not 0 !!).
-- get the length of the array portion of table:
-- (i.e. the initial integer-indexed segment of a given table):
print(#my_dict)
-- remove the 1st element of an array (shifts the following elements by 1):
table.remove(args, 1)
-- loop over a table (all keys):
local my_list = {}
for my_key, _ in pairs(my_dict) do
table.insert(my_list, my_dict)
end
-- loop over the array portion of a table:
for i, my_elt in ipairs(my_list) do
print(i, my_elt)
end
-- conditions:
if not args[2] == nil then ... end
if args[2] ~= nil then ... end
-- substring:
my_sub = my_strA:sub(1, -#my_strB-1) -- or rather use mw.ustring.sub() for correct UTF-8 support
--]]