Aller au contenu

Module:Langue

Cette page est protégée.
Une page de Wikipédia, l'encyclopédie libre.

 Documentation[voir] [modifier] [historique] [purger]

Utilisation

Fonctions exportables :

  • codeLangue(frame) : Convertit en code de langue IETF les noms français de langues les plus couramment citées.
  • codeLangue2(frame) : Similaire à codeLangue à une exception près : si le nom de la langue ne se trouve pas dans la liste qui suit, le modèle n'affiche rien.
  • directionLangue(frame) : Convertit en code de direction (rtl ou ltr) les codes ou noms français de langues.
  • lienLangue(frame) : À partir d'un code de langue, retourne un lien avec le nom descriptif de celle-ci et pointant vers son article.
  • nomLangue(frame) : À partir d'un code de langue, retourne le nom descriptif de celle-ci.
  • articleLangue(frame) : À partir d'un code de langue, retourne le titre de l'article concernant celle-ci.
  • lang(frame) : Indique la langue d’un texte dans le code HTML, notamment pour les synthétiseurs vocaux et l’indexation correcte des inclusions de mots en langue différente par les moteurs de recherche.
  • langue(frame) : Similaire à lang mais permet d’employer dans certains cas le nom de la langue en toutes lettres.
  • indicationDeLangue(frame) : Utilisable comme métamodèle pour créer des indications de langue (cf. (fr), (de)etc.).
  • indicationMultilingue(frame) : Similaire à indicationDeLangue à l'exception qu'elle indique plusieurs langues. Sans paramètre, (mul) est indiqué.
  • langueAvecNom(frame) : Indique le nom de la langue avant un court texte dans une langue donnée et met le texte en langue étrangère entre balises xml:lang. Similaire à indicationDeLangue mais avec la détermination automatique du nom et de la direction de la langue. Elle est donc un peu moins rapide.
  • tableauLangues(frame) : Génère un tableau triable de la liste des langues disponibles dans Module:langue/Data.

Modules externes et autres éléments dont ce module a besoin pour fonctionner :

  • Module:Langue/Data : Base de données avec les codes et les noms des langues les plus courantes.

Exemples

Pour des exemples, voir la page de test permettant de tester diverses modifications apportées.

Voir aussi

-- luacheck: globals mw, no max line length

local Langue = { }

local dataLangue = mw.loadData( 'Module:Langue/Data' )

-- attention, plusieurs modules cherchent « class="error" » pour détecter si ce module a retourné une erreur
-- pour trouver ces codes, on peut rechercher dans le namespace Module : insource:Langue insource:/(find|match)\([^)]+error/
local langErrorMess = '<span class="error">langue non reconnue : %s</span>'

-- premierParametre renvoie le premier paramètre de Frame, que celui-ci ait été passé au module par invoke, directement au modèle,
-- ou à l'intérieur d'un module sous forme de string dans un tableau ou directement de string.
-- Si aucun de ces arguments ne contient de chaine, la fonction renvoie nil.
-- Si le deuxième paramètre est true, la chaine est renvoyée trimée et en minuscules.
local function premierParametre( frame, lowerCase )
	local arg
	if type( frame ) == 'table' then
		arg = ( frame.getParent and ( frame.args[1] or frame:getParent().args[1] ) ) or frame[1]
	elseif type( frame ) == 'string' then
		arg = frame
	end
	if type( arg ) ~= 'string' then
		arg = nil
	end
	if arg and lowerCase then
		arg = mw.ustring.lower( mw.text.trim( arg ) )
	end
	return arg
end

-- determinationCode retourne une table contenant le code de langue principal et la liste des subcodes
-- si le code de langue principal n'est pas reconnu, retourne nil.
function Langue.determinationCode( langue )
	if type( langue ) == 'string' and langue ~= '' then
		local tabCode = mw.text.split( langue, '-' )
		local tabLangue = dataLangue[ mw.ustring.lower( tabCode[1] ) ]
		if tabLangue and tabLangue.code then
			tabCode[1] = tabLangue.code
			if tabLangue.invalide then
				tabCode.invalide=true
			end
			return tabCode
		end
	end
end

-- Voir Modèle:Code langue
-- Paramètre :
--     1  : nom de langue.
function Langue.codeLangue( frame )
	local arg = premierParametre( frame, true )
	local tabCode = Langue.determinationCode( arg )
	return ( tabCode and table.concat( tabCode, '-' ) ) or arg or ''
end

-- Voir Modèle:Code langue 2
-- Paramètre :
--     1  : nom de langue.
function Langue.codeLangue2( frame )
	local arg = premierParametre( frame, true )
	local tabCode = Langue.determinationCode( arg )
	return ( tabCode and table.concat( tabCode, '-' ) ) or ''
end

-- Voir Modèle:Direction langue
-- Paramètre :
--     1  : nom de langue ou code IETF.
function Langue.directionLangue( frame )
	local arg = premierParametre( frame, true )
	if type( arg ) ~= 'string' or arg == '' then
		return 'ltr'
	end
	-- séparation du code de langue en code principal et les différents subcodes.
	local tabCode = Langue.determinationCode( arg )
	if tabCode then
		-- on essaye de savoir si la direction est de droite à gauche
		local codeScript = tabCode[2]
		if codeScript and string.len( codeScript ) == 4 and dataLangue[ 'rtl script' ] then
			-- il y a un subcode d'écriture, c'est lui qui est pris en compte
			codeScript = string.upper( string.sub( codeScript, 1, 1 ) ) .. string.sub( codeScript, 2 )
			if dataLangue[ 'rtl script' ][ codeScript ] then
				return 'rtl'
			end
		else
			-- il n'y a pas de subcode d'écriture, on prend en compte le code de langue principal.
			local tabLangue = dataLangue[ tabCode[1] ]
			if tabLangue and tabLangue.rtl then
				return 'rtl'
			end
		end
	end

	-- la langue n'est pas écrite de droite à gauche, donc ltr.
	return 'ltr'
end

local function getDataLangue( frame )
	-- Obtenir le code de la langue
	local codeLangue = premierParametre( frame, true )
	-- Si le paramètre est incorrect, retourner un message d'erreur
	if type( codeLangue ) ~= 'string' or codeLangue == '' then
		return nil, '<span class="error">langue non précisée</span>'
	end

	-- Obtenir le tableau contenant les informations sur la langue
	local tabLangue = dataLangue[ codeLangue ]
	-- Si le code étendu n'est pas trouvé, chercher à nouveau avec le code racine
	if not tabLangue then
		tabLangue = dataLangue[ mw.ustring.match( codeLangue, '^(%a-)%-' ) ]
	end
	-- Si on n'a toujours rien trouvé, retourner un message d'erreur
	if not tabLangue then
		return nil, langErrorMess:format( codeLangue )
	end

	return tabLangue
end

-- Fonction fournissant un wikilien vers l'article
-- traitant d'une langue à partir de son code.
-- Voir Modèle:Lien langue
-- Paramètre :
--     1  : code IETF de langue.
function Langue.lienLangue( frame )
	local tabLangue, erreur = getDataLangue( frame )
	if not tabLangue then
		return erreur
	end

	-- Créer un lien pour la langue depuis le tableau
	if type( tabLangue.page ) == 'string' then
		if tabLangue.page ~= '' then
			return '[[' .. tabLangue.page .. '|' .. tabLangue.nom .. ']]'
		else
			return tabLangue.nom
		end
	else
		return '[[' .. tabLangue.nom .. ']]'
	end
end

-- Fonction permettant de récupérer le nom d'une langue à partir de son code.
-- Voir Modèle:Nom langue
-- Paramètre :
--     1  : code IETF de langue.
-- Retourne :
--     Le nom de la langue, un message d'erreur sinon.
function Langue.nomLangue( frame )
	local tabLangue, erreur = getDataLangue( frame )
	if not tabLangue then
		return erreur
	end

	-- Extraire le nom de la langue depuis le tableau
	return tabLangue.nom
end

-- Fonction permettant de récupérer le nom de l'article
-- portant sur une langue à partir de son code.
-- Voir Modèle:Article langue
-- Paramètre :
--     1  : code IETF de langue.
-- Retourne :
--     Le nom de l'article portant sur la langue
--     si le code est trouvé, un message d'erreur sinon.
function Langue.articleLangue( frame )
	local tabLangue, erreur = getDataLangue( frame )
	if not tabLangue then
		return erreur
	end

	-- Extraire le titre de l'article depuis le tableau
	if type( tabLangue.page ) == 'string' and tabLangue.page ~= '' then
		return tabLangue.page
	else
		return tabLangue.nom
	end
end

-- Voir Modèle:Langue
-- Paramètres :
--    1 : code IETF de langue ;
--    texte ou 2 : texte dans cette langue ;
--    trans : translittération du texte ;
--    dir : direction de la langue (obsolète : peut être en paramètre 1, avec code en 2 et texte en 3).
function Langue.langue( frame )
	local args = ( frame.getParent and frame:getParent().args ) or frame         -- préparation pour appel par modèle ou direct.
	local code = mw.ustring.lower( mw.text.trim( args[1] or '' ) )
	local texte = args.texte or ''
	if texte == '' then
		texte = args[2] or ''
	end
	local dir = args.dir
	local namespaceCategorisation = { [0] = true, [4] = true, [10] = true, [12] = true, [14] = true, [100] = true }
	local categorisation = namespaceCategorisation[ mw.title.getCurrentTitle().namespace ] and not args.nocat

	-- Décalage des paramètres si code contient la direction du texte (obsolète mais toujours possible).
	if code == 'ltr' or code == 'rtl' then
		dir = code
		code = mw.ustring.lower( mw.text.trim( args[2] ) or '' )
		texte = args[3] or ''
	end
	local codeArg = code

	-- sortie immédiate s'il n'y a pas de texte
	if texte == '' then
		if categorisation then
			return '<span class="error">erreur du modèle [[modèle:Langue|{{langue}}]] : texte absent</span>[[Catégorie:Page utilisant un modèle avec une syntaxe erronée|Langue]]'
		else
			return ''
		end
	end

	-- récursion si texte contient des blocs
	if texte:match('\n *\n') or texte:match('\n[*#:]') then
		-- les parenthèses permettent d'éliminer la seconde valeur retournée par gsub (nombre de remplacements)
		return ( texte:gsub(
			'(\n?[*#:]?%s*)([^\n]+)',
			function ( init, ligne )
				return init .. Langue.langue{ code, ligne }
			end
		) )
	end

	-- Si la langue est reconnue, la valeur de la table est prise en compte (permet de corriger les noms de langue en toutes lettres).
	local tabCode = Langue.determinationCode( code )
	local tabLangue
	if tabCode then
		code = table.concat( tabCode, '-' )
		tabLangue = dataLangue[ tabCode[1] ]

		local codeScript = tabCode[2]
		-- Si codeScript est bien un style d'écriture (len = 4) on applique sa direction
		if codeScript and  string.len( codeScript ) == 4 and dataLangue[ 'rtl script' ] then
			-- formatage type Latn correspondant au fromat dans dataLangue[ 'rtl script' ]
			codeScript = string.upper( string.sub(  codeScript, 1, 1 ) ) .. string.sub(  codeScript, 2 )
			tabLangue = { code = tabLangue.code,
				rtl = dataLangue[ 'rtl script' ][ codeScript ],
				invalide = tabLangue.invalide
			}
		end
	end

	-- Préparation du rendu de direction du texte.
	dir = dir and dir:lower()
	if dir ~= 'ltr' and dir ~= 'rtl' then
		dir = ( tabLangue and tabLangue.rtl and 'rtl' )
	end

	-- Compilation du texte à retourner.
	local html = mw.html.create( '' )
	if code == '' then
		html:wikitext( texte )
	else
		local span = mw.html.create( 'span' )
				:addClass( 'lang-' .. code )
				:attr( 'lang', code )
				:attr( 'dir', dir )
				:wikitext( texte )

		if args.class and args.class ~= '' then
			span:addClass( args.class )
		end

		html:node( span )

		-- Translittération.
		if ( args.trans or '' ) ~= '' then
			local trans = args.trans:gsub( "^''%f[^'](.+)%f[']''$", "%1" )
			html:wikitext( " (''" )
				:tag( 'span' )
					:addClass( 'transcription lang-' .. code )
					:attr( 'lang', code .. '-Latn' )
					:attr( 'dir', 'ltr' )
					:wikitext( trans )
					:done()
				:wikitext( "'')" )
		end

	end

	-- Ajout de la catégorie Page avec code de langue invalide si le code langue non reconnu ou invalide.
	if categorisation and ( type( tabLangue ) ~= 'table' or tabCode.invalide ) then
		local erreur = string.format( langErrorMess, codeArg )
		if codeArg == '' then
			erreur = '<span class="error">langue non précisée</span>'
		end
		html:wikitext( '[[Catégorie:Page avec code de langue invalide|Langue]] ' .. erreur )
	end

	return tostring( html )
end

-- Alias nom de fonction
Langue.lang = Langue.langue

-- Voir Modèle:Indication de langue
-- Paramètres :
--    1 : nom de langue ;
--    2 : code IETF ;
--    texte : texte dans cette langue ;
--    dir : direction de la langue.
function Langue.indicationDeLangue( frame )
	local args = ( frame.getParent and frame:getParent().args ) or frame
	local nomLangue = mw.text.trim( args[1] or '' )
	local code = mw.text.trim( args[2] or '' )
	local texte = args.texte
	local dir = args.dir

	-- Cas où le premier et/ou le deuxième paramètre est vide.
	if code == '' and nomLangue == '' then
		return texte
	elseif nomLangue == '' then
		nomLangue = dataLangue[ mw.ustring.lower( code ) ]
		nomLangue = (nomLangue and nomLangue.nom or '???')
	elseif code == '' then
		code = dataLangue[ nomLangue ]
		code = ( code and code.code or '' )
		if code == '' then
			return texte
		end
	end
	-- Gestion du texte.
	if texte and texte ~= '' then
		texte = '\194\160' .. Langue.lang{ code, dir = dir, texte = texte }
	else
		texte = ''
	end
	-- Compilation de l'indicateur de langue et du texte.
	local html = mw.html.create()
	html:tag( 'abbr' )
			:addClass( 'abbr' )
			:addClass( 'indicateur-langue' )
			:attr( 'title', 'Langue : ' .. nomLangue )
			:wikitext( '(' .. code .. ')' )
			:done()
		:wikitext( texte )

	return tostring( html )
end

-- Voir Modèle:Mul
-- Paramètres : codes IETF ou noms de langue, en nombre indéfini (string ou nil uniquement).
function Langue.indicationMultilingue( frame )
	local args = (frame.getParent and frame:getParent().args) or frame
	local listeNom = { }
	local listeCode = { }

	-- Valeur par défaut du premier paramètre = 'mul'.
	local code = mw.text.trim( args[1] or '' )
	if code == '' then
		code = 'mul'
	end
	local maxLang = tonumber(args.maxLang) or nil

	if not args[2] and not dataLangue[ mw.ustring.lower( code ) ] then
		local split = mw.text.split( code, '[+,;:/ .]+' )
		if #split > 1 then
			split.maxLang = maxLang
			return Langue.indicationMultilingue( split )
		end
	end
	-- Ajout des noms et codes de langue de chaque paramètre dans listeNom et ListeCode.
	local i = 1
	repeat
		code = mw.ustring.lower( code )
		local tabLangue = dataLangue[ code ]
		if not tabLangue then
			code = mw.text.split( code, '-' )[1]
			tabLangue = dataLangue[ code ]
		end
		if type( tabLangue ) == 'table' then
			table.insert( listeNom, tabLangue.nom )
			table.insert( listeCode, tabLangue.code )
		else
			table.insert( listeNom, '???' )
			local erreur = string.format( langErrorMess, code )
			table.insert( listeCode, erreur )
		end
		i = i + 1
		code = mw.text.trim( args[i] or '' )
	until code == ''

	-- Préparation et renvoi du texte.
	local n = #listeCode
	if n == 0 then
		return ''
	end
	if maxLang and n > maxLang then
		listeCode = {'mul'}
	end
	local plusieurs = ( n > 1 )

	local html = mw.html.create( 'abbr' )
		:addClass( 'abbr' )
		:addClass( 'indicateur-langue' )
		:attr( 'title', 'Langue' .. ( plusieurs and 's' or '' ) .. ' : ' .. mw.text.listToText( listeNom ) )
		:wikitext( '(' .. table.concat( listeCode, '\194\160+\194\160' ) .. ')' )

	return tostring( html )
end

-- Voir Modèle:Langue avec nom
-- Paramètres :
--    1 : code IETF de langue ;
--    texte ou 2 : texte dans cette langue ;
--    trans : translittération du texte ;
--    dir : direction de la langue.
function Langue.langueAvecNom( frame )
	local args = ( frame.getParent and frame:getParent().args ) or frame
	local code = mw.ustring.lower( mw.text.trim( args [1] or '') )
	local texte = args.texte or args[2] or ''
	local trans = args.trans
	local dir = args.dir

	-- Détection de la direction du texte.
	if code == 'ltr' or code == 'rtl' then
		dir = code
		code = mw.ustring.lower( mw.text.trim( args[2] ) )
		texte = args[3] or ''
	end

	-- Définition du nom de la langue en français.
	local nom = Langue.lienLangue{ code }

	if texte ~= '' then
		texte = ' ' .. Langue.lang{ code, dir = dir, texte = texte, trans = trans }
	end

	return nom .. ' :' .. texte
end

-- nonLatin détermine si la chaine fournie contient des caractères qui ne sont pas latins (étendu, unicode < U+0370)
function Langue.nonLatin( frame )
	local texte = premierParametre( frame )

	-- pour convertir les code points Unicode en UTF-8 : https://r12a.github.io/app-conversion/
	-- (dans le dropdown "Treat bare numbers as", penser à sélectionner "Hex code points" ou "Dec code points")

	-- voir [[mw:Extension:Scribunto/Lua reference manual#Patterns]] :
	-- les fonctions string.* ne permettent pas le caractère \0 dans les patterns, il faudrait donc faire [%z\001-\127], par exemple
	-- les fonctions mw.ustring.* semblent ne pas avoir cette limitation, mais par précaution faisons comme si elles l'avaient aussi

	-- avec Lua 5.2, on pourrait saisir les octets en format hexadécimal : [^%z\x01-\xCD\xAF...]

	-- avec Lua 5.3, on pourrait saisir directement les code points Unicode, que Lua convertirait en UTF-8 : [^%z\u{1}-\u{36F}...]

	-- la concaténation du pattern est effectuée à chaque appel de la fonction,
	-- mais elle est extrêmement performante (c'est optimisé en faisant une seule opcode concat, et il y a le string interning)
	-- on pourrait mettre en variable locale dans le module et réutiliser, mais il n'y a même pas de différence mesurable

	local pattern = '[^'
		.. '%z'                        -- U+0000 (UTF-8 00), cas particulier qu'il faut représenter avec %z
		.. '\001-\205\175'             -- U+0001 (UTF-8 01) à U+036F (UTF-8 CDAF)
		.. '\225\180\128-\225\187\191' -- suppléments phonétique, diacritiques et latin : U+1D00 (UTF-8 E1B480) à U+1EFF (UTF-8 E1BBBF)
		.. '\226\128\128-\226\177\191' -- espace, indices, monnaies et symboles divers : U+2000 (UTF-8 E28080) à U+2C7F (UTF-8 E2B1BF)
		.. '\234\156\160-\234\159\191' -- latin étendu D : U+A720 (UTF-8 EA9CA0) à U+A7FF (UTF-8 EA9FBF)
		.. ']'

	-- après essais, mw.ustring.match() est *très légèrement* plus performant que mw.ustring.find(), mais ça reste le même ordre de grandeur

	return mw.ustring.match( texte, pattern ) and true or false
end

-- erreurModuleData affiche un message d'erreur si le Module:Langue/Data n'a pas été chargé correctement,
-- pour la page de discussion de la base de données et ceux qui veulent surveiller cette page.
function Langue.erreurModuleData()
	if type( dataLangue ) ~= 'table' then
		local message = [[<strong class="error">Le chargement du module Langue/Data génère une erreur.</strong> <br>

<span class="error">Cette erreur doit être corrigée au plus vite car des milliers de pages ne s'affichent pas correctement.</span>
]]
		return message
	end
end

-- tableauLangues génère un tableau triable de la liste des langues disponible dans Module:langue/Data.
function Langue.tableauLangues()
	local tableau = { }
	local entete = [[{| class="wikitable alternance sortable"
|-
!scope=col|Alias
!scope=col|Code IETF
!scope=col|Nom principal
!scope=col|Titre de l'article<br>(si différent du nom)
!scope=col|Lien vers l'article
!scope=col|RTL
!scope=col|Invalide]]

	local ligneTab, ligneSrt
	for k, v in pairs( dataLangue ) do
		if v.code then
			ligneTab = {
				k,
				v.code,
				v.nom,
				v.page or '',
				Langue.lienLangue(v.code),
				v.rtl and '[[Fichier:Yes check.svg|15px|oui|lien=]]' or '',
				v.invalide and '[[Fichier:Yes check.svg|15px|oui|lien=]]' or '',
			}
			ligneSrt = table.concat( ligneTab, '\n|' )
			table.insert( tableau, ligneSrt )
		end
	end
	table.sort( tableau )
	table.insert( tableau, 1, entete )
	table.insert( tableau, '}' )

	return table.concat( tableau, '\n|-\n|' )
end

-- listeCodeAlias génère une liste ; les langues sont de la forme : * code : alias1, alias2
function Langue.listeCodeAlias()
	local languesTab, listeCodesAlias = { }, { }
	local code, alias, codeAlias
	for k, v in pairs( dataLangue ) do
		-- on construit un table avec pour indices les codes de langue, et pour valeurs une table avec la liste des alias
		code = v.code
		if code and not v.invalide then
			languesTab[code] = languesTab[code] or { }
			if k ~= mw.ustring.lower( code ) then
				table.insert( languesTab[code], k )
			end
		end
	end
	for k, v in pairs( languesTab ) do
		-- transformation en un table séquence, avec pour valeur une chaine correspondant à une ligne de la liste
		alias = table.concat( v, ', ' )
		if alias == '' then
			codeAlias = '* <code>' .. k .. '</code>'
		else
			codeAlias = '* <code>' .. k .. '</code> : ' .. alias
		end
		table.insert( listeCodesAlias, codeAlias )
	end
	table.sort( listeCodesAlias )

	return table.concat( listeCodesAlias, '\n' )
end

-- listeAliasCode génère une liste ; les langues sont de la forme :  * alias : code
function Langue.listeAliasCode()
	local languesTab = { }
	local code
	for k, v in pairs( dataLangue ) do
		-- on construit un table avec pour indices les codes de langue, et pour valeurs une table avec la liste des alias
		code = v.code
		if code and k ~= code and not v.invalide then
			table.insert( languesTab, '* ' .. k .. ' = ' .. code )
		end
	end
	table.sort( languesTab )

	return table.concat( languesTab, '\n' )
end

return Langue