Différences entre les pages « Modèle:Titre mis en forme » et « Module:Unité »
De Les Mots de l'agronomie
(Différence entre les pages)
imported>Jacques Ducloy m (1 révision importée) |
imported>Jacques Ducloy m (1 révision importée) |
||
Ligne 1 : | Ligne 1 : | ||
− | + | local p = {} | |
+ | |||
+ | -- local Delink = require( 'Module:Delink' ) -- chargé uniquement si nécessaire | ||
+ | |||
+ | -- Chargement de la base de données des nom d'unités avec gestion d'erreur. | ||
+ | local moduleData = 'Module:Unité/Data' | ||
+ | local dataSuccess, Data = pcall ( mw.loadData, moduleData ) | ||
+ | if dataSuccess and type( Data ) == 'table' then | ||
+ | dataSuccess = type( Data.unit ) == 'table' | ||
+ | and type( Data.prefix ) == 'table' | ||
+ | and type( Data.exposant ) == 'table' | ||
+ | end | ||
+ | |||
+ | local errorCat = '[[Catégorie:Page incorrectement traitée par le Module:Unité]]' | ||
+ | local addErrorCat = false | ||
+ | |||
+ | local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹', | ||
+ | ['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' } | ||
+ | local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉', | ||
+ | ['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎', | ||
+ | ['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ', | ||
+ | ['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ', | ||
+ | } | ||
+ | --- Copie de Outils.trim acceptant les nombres. | ||
+ | local function trim( texte ) | ||
+ | if type( texte ) == 'string' then | ||
+ | texte = texte:gsub( '^%s*(%S?.-)%s*$', '%1' ) | ||
+ | if texte ~= '' then | ||
+ | return texte | ||
+ | end | ||
+ | elseif type( texte ) == 'number' then | ||
+ | return tostring( texte ) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function p.sanitizeNum( nombre ) | ||
+ | if type( nombre ) == 'number' then | ||
+ | return tostring( nombre ) | ||
+ | elseif type( nombre ) == 'string' then | ||
+ | if nombre:match( '^%-?[%d.,]+$' ) then | ||
+ | return nombre | ||
+ | end | ||
+ | local result = nombre | ||
+ | -- remplacement des signes moins ou demi-cadratin par un tiret | ||
+ | :gsub( '%−%f[%d]', '-') -- U+2212 | ||
+ | :gsub( '−%f[%d]', '-') -- html − | ||
+ | :gsub( '\226\128[\146\147]%f[%d]', '-') -- U+2212, U+2213 (tiret numérique et demi-cadratin) | ||
+ | -- remplacement des espaces insécable par des espace simple | ||
+ | :gsub( '\194\160', ' ' ) | ||
+ | :gsub( ' ', ' ' ) | ||
+ | :gsub( '\226\128[\128-\138\175]', ' ' ) -- U+2002 à U+200A et U+202F | ||
+ | -- trim | ||
+ | :gsub( '^%s*(%S?.-)%s*$', '%1' ) | ||
+ | return result | ||
+ | else | ||
+ | return '' | ||
+ | end | ||
+ | end | ||
+ | |||
+ | --- | ||
+ | -- parseNum transforme si possible une chaine formatée en un chaine interprétable par tonumber() | ||
+ | -- retourne une chaine pour éviter les arrondi éventuels de lua. | ||
+ | -- si "nombre" est une chaine non reconnue comme un nombre par la fonction, retourne "nombre". | ||
+ | -- si "nombre" n'est pas un number ou une chaine retourne une chaine vide. | ||
+ | function p.parseNombre( nombre ) | ||
+ | local result | ||
+ | if type( nombre ) == 'number' then | ||
+ | return tostring( nombre ) | ||
+ | else | ||
+ | -- remplacement des signes moins ou demi-cadratin par un tiret | ||
+ | result = p.sanitizeNum( nombre ) | ||
+ | if result == '' then | ||
+ | return '' | ||
+ | elseif not result:match( '^%-?[%d., ]*%d$' ) then | ||
+ | return nombre | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- suppression espaces | ||
+ | result = result:gsub( ' ', '' ) | ||
+ | |||
+ | -- gestion des points et des virgules | ||
+ | if result:match( '[.,]' ) then | ||
+ | if result:match( '%d%.%d%d%d%.%d' ) then | ||
+ | -- type 12.345.678 | ||
+ | result = result:gsub( '%.', '' ):gsub( ',', '.' ) | ||
+ | elseif result:match( '%d,%d%d%d,%d' ) -- type 1,234,567 ou 1.234,567,8 | ||
+ | or result:match( '%d,%d%d%d%.%d' ) -- format anglo-saxon type 1,234.5 | ||
+ | or result:match( '%d%.%d%d%d,%d' ) -- type 1.123,56 (utilisé en exemple pour sépararer les décimales avec l'ancien modèle unité ou formatnum) | ||
+ | then | ||
+ | result = result:gsub( ',', '' ) | ||
+ | else | ||
+ | result = result:gsub( ',', '.' ) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | return result | ||
+ | end | ||
+ | |||
+ | --- | ||
+ | -- _formantNum transforme un nombre ou une chaine représentant un nombre en chaine formatée suivant les conventions du français | ||
+ | -- si le paramètre ne représente pas un nombre lua il est retourné sans modification | ||
+ | -- Le paramètre peut être transmis sous forme de table pour ajouter des options : | ||
+ | -- * round : arrondi à n chiffre après la virgule (peut être négatif) | ||
+ | -- * decimals : nombre de décimales affichées (peut être négatif, dans ce cas équivalent à round) | ||
+ | -- * noHtml : n'utilise pas de balise HTML pour affiché les puissance de 10 (pour pouvoir être utilisé en title) | ||
+ | function p.formatNum( num ) | ||
+ | local params = {} | ||
+ | if type( num ) == 'table' then | ||
+ | params = num | ||
+ | num = params[1] | ||
+ | end | ||
+ | if type( num ) == 'number' then | ||
+ | num = tostring( num ) | ||
+ | elseif type( num ) ~= 'string' or num == '' then | ||
+ | return num | ||
+ | end | ||
+ | |||
+ | -- séparation exposant | ||
+ | local n, exponent = num:match( '^([-%d.]+)[eE]([+-]?%d+)$' ) | ||
+ | if exponent then | ||
+ | num = n | ||
+ | if params.noHtml then | ||
+ | exponent = exponent:gsub('+?%f[%d]0', '' ) | ||
+ | :gsub( '[%d-]', supUnicode ) | ||
+ | else | ||
+ | exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−', [''] = '' } ) .. '</sup>' | ||
+ | end | ||
+ | exponent = ' ×10' .. exponent | ||
+ | else | ||
+ | exponent = '' | ||
+ | end | ||
+ | |||
+ | -- arrondi | ||
+ | local decimals = tonumber( params.decimals ) | ||
+ | local round = tonumber( params.round ) or decimals | ||
+ | if round and tonumber( num ) then | ||
+ | local mult = 10 ^ round | ||
+ | num = tostring( math.floor( num * mult + 0.5 ) / mult ) | ||
+ | end | ||
+ | |||
+ | local moins, entier, fraction = num:match( '^(%-?)(%d*)%.?(%d*)$' ) | ||
+ | if not entier then | ||
+ | return num | ||
+ | end | ||
+ | |||
+ | if moins == '-' then | ||
+ | moins = '−' -- signe moins (U+2212) | ||
+ | end | ||
+ | |||
+ | if entier == '' then | ||
+ | entier = '0' | ||
+ | elseif entier:len() > 3 then | ||
+ | local ini = math.fmod( entier:len() - 1, 3 ) + 1 | ||
+ | entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' ) | ||
+ | end | ||
+ | if fraction ~= '' or ( decimals and decimals > 0 ) then | ||
+ | if decimals and decimals > #fraction then | ||
+ | fraction = fraction .. string.rep( '0', decimals - #fraction ) | ||
+ | end | ||
+ | if #fraction > 4 then | ||
+ | fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' ) | ||
+ | else | ||
+ | fraction = ',' .. fraction | ||
+ | end | ||
+ | end | ||
+ | |||
+ | return moins .. entier .. fraction .. exponent | ||
+ | end | ||
+ | |||
+ | --- | ||
+ | -- formatNombre transforme un nombre formaté ou non en chaine formatée suivant les convention du français. | ||
+ | -- si la chaine n'est pas reconnu comme un nombre, elle n'est pas modifiée. | ||
+ | function p.formatNombre( num, round, decimals ) | ||
+ | return p.formatNum{ p.parseNombre( num ), round = round, decimals = decimals } | ||
+ | end | ||
+ | |||
+ | --- formatNombres transforme tous les nombres d'une chaine en nombre formaté suivant les conventions du français. | ||
+ | function p.formatNombres( nombres, round, decimals ) | ||
+ | if type( nombres ) == 'number' then | ||
+ | return p.formatNum( nombres, round, decimals ) | ||
+ | elseif type( nombres ) == 'string' then | ||
+ | -- retire les chiffres des strip marker | ||
+ | local strip, i = {}, 0 | ||
+ | nombres = nombres:gsub( | ||
+ | 'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU', | ||
+ | function ( marker ) | ||
+ | i = i + 1 | ||
+ | strip[ tostring( i ) ] = marker | ||
+ | return 'UNIQ^' .. i .. '¤QINU' | ||
+ | end | ||
+ | ) | ||
+ | -- formatage proprement dit | ||
+ | nombres = p.sanitizeNum( nombres ) | ||
+ | local formatN = function ( n ) | ||
+ | return p.formatNombre( n, round, decimals ) | ||
+ | end | ||
+ | nombres = nombres | ||
+ | :gsub( '%-?%f[%d.,][%d., ]*%de[+-]?%d+', formatN ) | ||
+ | :gsub( '%-?%f[%d.,][%d., ]*%d', formatN ) | ||
+ | |||
+ | -- réintroduction des strip marker | ||
+ | nombres = nombres:gsub( 'UNIQ^(%d+)¤QINU', strip ) | ||
+ | return nombres | ||
+ | else | ||
+ | return '' | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function p.parseUnit( texte ) | ||
+ | local toParse = p.sanitizeNum( texte ) | ||
+ | if toParse ~= '' then | ||
+ | local result | ||
+ | local specificArgs = { | ||
+ | ['à'] = 'à', | ||
+ | et = 'et', | ||
+ | ou = 'ou', | ||
+ | ['/'] = '/', | ||
+ | ['–'] = '–', ['-'] = '–', -- demi cadratin et tiret | ||
+ | ['±'] = '±', ['+-'] = '±', ['+/-'] = '±', | ||
+ | ['+'] = '+', | ||
+ | ['−'] = '−', -- signe moins | ||
+ | ['×'] = '×', x = '×', ['*'] = '×', | ||
+ | ['××'] = '××', xx = '××', ['**'] = '××', | ||
+ | } | ||
+ | |||
+ | -- valeur numérique | ||
+ | local match, capture = toParse:match( '^(([%d., ]+%f[^d%(])%s*)' ) | ||
+ | local prefix | ||
+ | if not match then | ||
+ | -- cas ou le nombre est remplcé par un ou plusieurs points d'interrogation | ||
+ | match, prefix = toParse:match( '^((%?+)%s*)' ) | ||
+ | end | ||
+ | if not match then | ||
+ | -- cas ou un mot type "vers", "environ" précède le nombre (mot simple, sans accent pour ne pas complexifier pour des cas minoritaires) | ||
+ | match, prefix, capture = toParse:match( '^(([%a]+[.,]?[: ]* )([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | ||
+ | end | ||
+ | if not match then | ||
+ | -- cas ou le nombre est précédé par un signe, un symbole ASCII, ou suivit d'une incerititude entre parenthèse | ||
+ | match, prefix, capture = toParse:match( '^(([(<>=~ ]*)([+-]?%f[%d.,][%d., ]*%d%(?%d*%)?)%s*)' ) | ||
+ | end | ||
+ | if not match then | ||
+ | -- cas ou le nombre est précédé par un symbole ≤, ≥ ou ≈ | ||
+ | match, prefix, capture = toParse:match( '^((\226\137[\164\165\136] ?)([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | ||
+ | end | ||
+ | result = { capture or false, prefix = prefix } | ||
+ | if match then | ||
+ | toParse = toParse:sub( match:len() + 1 ) | ||
+ | |||
+ | -- point de suspensions (ex π = 3.14159...) | ||
+ | match = toParse:match( '^…%s*' ) | ||
+ | if not match then | ||
+ | match, capture = toParse:match( '^%.%.%.%s*' ) | ||
+ | end | ||
+ | if match then | ||
+ | result[1] = result[1] .. '…' | ||
+ | toParse = toParse:sub( match:len() + 1 ) | ||
+ | end | ||
+ | |||
+ | -- fraction | ||
+ | match, capture = toParse:match( '^((%d*/%d+)%s*)' ) | ||
+ | if not match then | ||
+ | match, capture = toParse:match( '^((\194[\188-\190])%s*)' ) -- ¼ à ¾ | ||
+ | end | ||
+ | if not match then | ||
+ | match, capture = toParse:match( '^((\226\133[\144-\158])%s*)' ) -- ⅐ à ⅞ | ||
+ | end | ||
+ | if match then | ||
+ | if capture:match( '^/' ) then | ||
+ | local n = result[1]:match( ' %d+$' ) or result[1]:match( '^%d+$' ) or '' | ||
+ | result[1] = result[1]:sub( 1, -1 - #n ) | ||
+ | result.fraction = n:gsub( '^ ', '' ) .. capture | ||
+ | else | ||
+ | result.fraction = capture | ||
+ | end | ||
+ | toParse = toParse:sub( match:len() + 1 ) | ||
+ | end | ||
+ | |||
+ | -- lien avec un deuxième nombre | ||
+ | local match2, conj, num = mw.ustring.match( toParse, '^(([àetouM+/−x*×±–-]+) ?(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' ) | ||
+ | if match2 and specificArgs[ conj ] | ||
+ | and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then | ||
+ | result[ specificArgs[ conj ] ] = num | ||
+ | toParse = toParse:sub( match2:len() + 1 ) | ||
+ | end | ||
+ | if result['+'] or result['×'] then | ||
+ | match2, conj, num = mw.ustring.match( toParse, '^(([x*×−-]) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | ||
+ | if match2 then | ||
+ | if specificArgs[ conj ] == '×' then | ||
+ | result['××'] = num | ||
+ | else | ||
+ | result['−'] = num | ||
+ | end | ||
+ | toParse = toParse:sub( match2:len() + 1 ) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- 10 exposant ( \195\151 = ×, signe multiplié) | ||
+ | match, capture = toParse:match( '^(%s*e(%-?%d+)%s*)' ) | ||
+ | if not match then | ||
+ | match, capture = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+)%s*)' ) | ||
+ | end | ||
+ | if match then | ||
+ | result.e = capture | ||
+ | toParse = toParse:sub( match:len() + 1 ) | ||
+ | end | ||
+ | |||
+ | -- unités | ||
+ | if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then | ||
+ | table.insert( result, toParse ) | ||
+ | toParse = '' | ||
+ | elseif toParse ~= '' then | ||
+ | local unit, exp | ||
+ | toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' ) | ||
+ | repeat | ||
+ | -- unité contenant un lien | ||
+ | match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' ) | ||
+ | if not match then | ||
+ | -- unité ne contenant pas de lien | ||
+ | match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' ) | ||
+ | end | ||
+ | if not match then | ||
+ | -- l/100 km | ||
+ | match, unit, exp = mw.ustring.match( toParse, '^((/100 ?[^%s%d/]+) ?(%-?%d*)%s*)' ) | ||
+ | end | ||
+ | if match then | ||
+ | if unit:match( '%-$' ) and exp ~= '' then | ||
+ | unit = unit:gsub( '%-$', '' ) | ||
+ | exp = '-' .. exp | ||
+ | elseif exp == '-' then | ||
+ | unit = match | ||
+ | exp = '' | ||
+ | end | ||
+ | if Data.unit[ unit ] or mw.ustring.match( unit, '[%a€£$«»]' ) then | ||
+ | table.insert( result, unit ) | ||
+ | table.insert( result, exp ) | ||
+ | toParse = toParse:sub( match:len() + 1 ) | ||
+ | else | ||
+ | break | ||
+ | end | ||
+ | end | ||
+ | until toParse == '' or not match | ||
+ | end | ||
+ | |||
+ | if toParse == '' then | ||
+ | if #result > 1 and result[ #result ] == '' then | ||
+ | result[ #result ] = nil | ||
+ | end | ||
+ | return result | ||
+ | else | ||
+ | -- une partie de la chaine n'a pas pu être décodée, on retourne la chaine originale | ||
+ | addErrorCat = true | ||
+ | return { texte } | ||
+ | end | ||
+ | else | ||
+ | return { } | ||
+ | end | ||
+ | end | ||
+ | |||
+ | --- | ||
+ | -- nomUtnit retourne le nom français du code d'une unité et de son exposant. | ||
+ | -- si le code de l'unité n'est pas reconnu retourne 1 et false, de façon à ajouter false en première position d'une table. | ||
+ | function p.nomUnit( unit, exposant ) | ||
+ | if not dataSuccess or type( unit ) ~= 'string' then | ||
+ | return 1, false | ||
+ | end | ||
+ | |||
+ | -- nettoyage des liens et balise HTML | ||
+ | unit = unit:gsub( '^/' , '' ) | ||
+ | if unit:match( '%[' ) then | ||
+ | local Delink = require( 'Module:Delink' ) | ||
+ | unit = Delink._delink{ unit } | ||
+ | end | ||
+ | if unit:match( '<' ) then | ||
+ | unit = unit:gsub( '%b<>', '' ) | ||
+ | end | ||
+ | |||
+ | -- récupère le nom de l'unité | ||
+ | local unitTab = Data.unit[ unit ] | ||
+ | local unitPrefix = { nom = '' } | ||
+ | if not unitTab then | ||
+ | unitTab = Data.unit[ unit:sub( 2 ) ] | ||
+ | unitPrefix = Data.prefix[ unit:sub( 1, 1 ) ] | ||
+ | if not ( unitTab and unitPrefix ) then | ||
+ | -- pour µ, Ki, Mi, Gi... qui sont codé sur deux octets | ||
+ | unitTab = Data.unit[ unit:sub( 3 ) ] | ||
+ | unitPrefix = Data.prefix[ unit:sub( 1, 2 ) ] | ||
+ | if not ( unitTab and unitPrefix ) then | ||
+ | unitTab = false | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- récupère le nom de l'exposant | ||
+ | if trim( exposant ) then | ||
+ | local exp = tonumber( exposant ) | ||
+ | exp = exp and Data.exposant[ math.abs( exp ) ] | ||
+ | exposant = exp or ' puissance ' .. exposant | ||
+ | else | ||
+ | exposant = '' | ||
+ | end | ||
+ | |||
+ | -- assemble les deux partie | ||
+ | if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then | ||
+ | return unitPrefix.nom .. unitTab.nom .. exposant | ||
+ | elseif unit:match( '[/%d]' ) then | ||
+ | -- ce n'est pas du texte simple, on anule l'infobule | ||
+ | return 1, false | ||
+ | else | ||
+ | return unit .. exposant | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function p._unite( args ) | ||
+ | -- remplacement de certains caractères, pour simplifier les pattern | ||
+ | local nombre = p.sanitizeNum( args[1] ) | ||
+ | if nombre == '' then | ||
+ | nombre = nil | ||
+ | else | ||
+ | -- formatage du nombre | ||
+ | nombre = p.formatNombres( nombre, args.arrondi, args['décimales'] ) | ||
+ | end | ||
+ | |||
+ | local wiki = { args.prefix or '', nombre } -- prefix est un paramètre interne défini par p.parseUnit, utile notamment lorsque {{unité}} est utilisé dans les infobox | ||
+ | |||
+ | -- fraction | ||
+ | if args.fraction then | ||
+ | local nom, den = args.fraction:match( '^(.-)/(.+)$' ) | ||
+ | if nom then | ||
+ | if nom:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then | ||
+ | nom = nom:gsub( '[%dn()=+-]', supUnicode ) | ||
+ | den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode ) | ||
+ | else | ||
+ | nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nom .. '</sup>' | ||
+ | den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>' | ||
+ | end | ||
+ | args.fraction = nom .. '⁄' .. den | ||
+ | end | ||
+ | |||
+ | if nombre then | ||
+ | table.insert( wiki, '\194\160' ) | ||
+ | end | ||
+ | table.insert( wiki, args.fraction ) | ||
+ | end | ||
+ | |||
+ | -- à, et, ou, ×, – (tiret cadratin) | ||
+ | local specificArgs = { '–', 'à', 'et', 'ou', '/', '×', '××', '±' } | ||
+ | for _, name in ipairs( specificArgs ) do | ||
+ | local v = trim( args[ name ] ) | ||
+ | if v then | ||
+ | v = p.formatNombres( v ) | ||
+ | if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then | ||
+ | -- pas d'espace pour le tiret cadratin entre deux nombres positifs | ||
+ | table.insert( wiki, '–' ) | ||
+ | elseif name == '/' then | ||
+ | -- espace demi-cadratin (U+2002 ou  ) avec / | ||
+ | table.insert( wiki, ' / ' ) | ||
+ | elseif name == '××' then | ||
+ | table.insert( wiki, '\194\160×\194\160' ) | ||
+ | elseif name == '×' or name == '±' then | ||
+ | table.insert( wiki, '\194\160' .. name .. '\194\160' ) | ||
+ | else | ||
+ | table.insert( wiki, ' ' .. name .. ' ' ) | ||
+ | end | ||
+ | table.insert( wiki, v ) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- analyse de l'unité pour la conversion (mais ne sera affiché qu'après l'incertitude + et - séparé | ||
+ | local i = 1 | ||
+ | local unit = trim( args[ 2 * i ] ) | ||
+ | local units = '' | ||
+ | local nomUnits, par = {}, false | ||
+ | while unit do | ||
+ | local exp = p.parseNombre( args[ 2 * i + 1 ] ) | ||
+ | local sep = '' | ||
+ | -- gestion des exposants | ||
+ | local expUnit = '' | ||
+ | if exp == '' then | ||
+ | if unit:sub( -2 ) == '²' then | ||
+ | exp = '2' | ||
+ | unit = unit:sub( 1, -3 ) | ||
+ | elseif unit:sub( -2 ) == '³' then | ||
+ | exp = '3' | ||
+ | unit = unit:sub( 1, -3 ) | ||
+ | end | ||
+ | end | ||
+ | if #exp > 0 then | ||
+ | expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>' -- remplace le tiret par un vrai signe moins | ||
+ | end | ||
+ | -- gestion de la séparation des unités et des unités en dénominateur | ||
+ | if units ~= '' then | ||
+ | if unit:sub( 1, 1 ) == '/' then | ||
+ | sep = '/' | ||
+ | unit = unit:sub( 2 ) | ||
+ | if not par then | ||
+ | par = true | ||
+ | table.insert( nomUnits, 'par' ) | ||
+ | end | ||
+ | else | ||
+ | sep = '\194\160' -- point médian désactivé : '⋅\194\160' | ||
+ | end | ||
+ | end | ||
+ | if exp:match( '^-' ) and not par then | ||
+ | par = true | ||
+ | table.insert( nomUnits, 'par' ) | ||
+ | end | ||
+ | -- remplacement de l'unité par son symbole | ||
+ | if Data.unit[ unit ] then | ||
+ | -- unit = Data.unit[ unit ].symbole | ||
+ | -- désactivé car ne gère pas les multiple tel mL | ||
+ | end | ||
+ | units = units .. sep .. unit .. expUnit | ||
+ | table.insert( nomUnits, p.nomUnit( unit, exp ) ) | ||
+ | i = i + 1 | ||
+ | unit = trim( args[ 2 * i ] ) | ||
+ | end | ||
+ | |||
+ | -- conversion | ||
+ | local unitNameString = nomUnits[1] and table.concat( nomUnits, ' ' ) or '' | ||
+ | unitNameString = mw.ustring.gsub( unitNameString, '(%a)s%f[%A]', '%1' ) | ||
+ | local multiple = 1 | ||
+ | local convertTable = Data.convert[ unitNameString ] | ||
+ | if not convertTable and #unitNameString > 5 then | ||
+ | -- gesion des multiples (Kilo, méga, mili...) | ||
+ | local prefix = Data.prefix[ unitNameString:sub( 1, 4 ) ] or Data.prefix[ unitNameString:sub( 1, 5 ) ] | ||
+ | local _, par = unitNameString:find( ' par ' ) | ||
+ | local prefix2 | ||
+ | if par then | ||
+ | prefix2 = Data.prefix[ unitNameString:sub( par + 1, 4 ) ] or Data.prefix[ unitNameString:sub( par + 1, 5 ) ] | ||
+ | end | ||
+ | if prefix and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ] then | ||
+ | convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ] | ||
+ | multiple = 10 ^ prefix.puissance | ||
+ | elseif prefix2 and Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ] then | ||
+ | convertTable = Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ] | ||
+ | multiple = 1 / 10 ^ prefix2.puissance | ||
+ | elseif prefix and prefix2 and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] then | ||
+ | convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] | ||
+ | multiple = 10 ^ prefix.puissance / 10 ^ prefix2.puissance | ||
+ | end | ||
+ | end | ||
+ | if convertTable then | ||
+ | if type( convertTable[1] ) ~= 'table' then | ||
+ | convertTable = { convertTable } | ||
+ | end | ||
+ | for i, v in ipairs( wiki ) do | ||
+ | local n = tonumber( p.parseNombre( v ) ) | ||
+ | if n then | ||
+ | n = n * 10 ^ ( tonumber( p.parseNombre( args.e ) ) or 0 ) | ||
+ | local converted = {} | ||
+ | for _, c in ipairs( convertTable ) do | ||
+ | local nConverted = n | ||
+ | if c.inverse then | ||
+ | nConverted = 1 / n | ||
+ | end | ||
+ | if c.M then | ||
+ | -- M = masse molaire | ||
+ | local M = tonumber( args.M ) | ||
+ | if not M then | ||
+ | break | ||
+ | end | ||
+ | if c.M == '*' then | ||
+ | nConverted = nConverted * M | ||
+ | elseif c.M == '/' then | ||
+ | nConverted = nConverted / M | ||
+ | end | ||
+ | end | ||
+ | nConverted = nConverted * multiple * c[2] + ( c[3] or 0 ) | ||
+ | -- format | ||
+ | nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true } | ||
+ | local sep = ' ' | ||
+ | if mw.ustring.sub( c[1], 1, 1 ) == '°' then | ||
+ | sep = '' | ||
+ | end | ||
+ | mw.log( c[1], mw.ustring.codepoint( sep, 1, 1)) | ||
+ | table.insert( converted, nConverted .. sep.. c[1] ) | ||
+ | end | ||
+ | wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>' | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- incertitude avec + et − séparés | ||
+ | if trim( args['+'] ) then | ||
+ | local approximation = '+' .. p.formatNombre( args['+'] ) .. '' | ||
+ | if trim( args['−'] ) then | ||
+ | approximation = approximation .. '<br> −' .. p.formatNombre( args['−'] ) | ||
+ | end | ||
+ | table.insert( wiki, '<span class="nowrap"><span style="display:inline-block; padding-left:0.2em; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">' ) | ||
+ | table.insert( wiki, approximation .. '</span></span>' ) | ||
+ | end | ||
+ | |||
+ | -- puissance de 10 | ||
+ | local exposant = trim( args.e ) | ||
+ | if exposant then | ||
+ | exposant = p.formatNombre( exposant ) | ||
+ | if nombre then | ||
+ | if trim( args['±'] ) and not nombre:match( '^%(' ) then | ||
+ | table.insert( wiki, 1, '(' ) | ||
+ | table.insert( wiki, ')' ) | ||
+ | end | ||
+ | table.insert( wiki, '\194\160× 10<sup>' .. exposant .. '</sup>' ) | ||
+ | else | ||
+ | table.insert( wiki, '10<sup>' .. exposant .. '</sup>' ) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | if units ~= '' then | ||
+ | local sep = '\194\160' | ||
+ | if not ( nombre or args.fraction or exposant ) then | ||
+ | sep = '' | ||
+ | else | ||
+ | local symbole = Data.unit[ units ] and Data.unit[ units ].symbole | ||
+ | if symbole == '°' or symbole == '′' or symbole == '″' then | ||
+ | sep = '' | ||
+ | end | ||
+ | end | ||
+ | -- ajoute une abréviation si le nom de l'unité est différent de l'unité (en retirant les espaces qui peuvent être devenus insécables) | ||
+ | if nomUnits[1] and mw.ustring.gsub( table.concat( nomUnits ), '[ \194\160]', '' ) ~= mw.ustring.gsub( units, '[ \194\160]', '' ) then | ||
+ | units = string.format( '<abbr class=abbr title="%s">%s</abbr>', table.concat( nomUnits, ' ' ), units ) | ||
+ | end | ||
+ | table.insert( wiki, sep .. units ) | ||
+ | end | ||
+ | |||
+ | if #wiki > 0 then | ||
+ | return table.concat( wiki ) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function p.unite( frame ) | ||
+ | local args | ||
+ | if type( frame ) == 'table' then | ||
+ | if type( frame.getParent ) == 'function' then | ||
+ | args = frame:getParent().args; | ||
+ | else | ||
+ | args = frame | ||
+ | end | ||
+ | end | ||
+ | if args then | ||
+ | args[1] = trim( args[1] ) or false | ||
+ | if args[1] then | ||
+ | if args[1]:match('[^%d,. -]') then | ||
+ | local tempArgs = p.parseUnit( args[1] ) | ||
+ | if not ( args[2] and tempArgs[2] ) then | ||
+ | for k, v in pairs( tempArgs ) do | ||
+ | args[k] = v | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | args[2] = trim( args[2] ) or false | ||
+ | if args[2] and not args[3] and args[2]:match('/') then | ||
+ | local tempArgs = p.parseUnit( args[2] ) | ||
+ | args[2] = false | ||
+ | if tempArgs[1] ~= false then | ||
+ | table.insert( tempArgs, 1, false ) | ||
+ | end | ||
+ | for k, v in pairs( tempArgs ) do | ||
+ | if args[k] and v then | ||
+ | addErrorCat = true | ||
+ | end | ||
+ | args[k] = args[k] or v | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | -- args alias | ||
+ | args['×'] = args['×'] or args['x'] -- lettre x → signe multiplié | ||
+ | args['±'] = args['±'] or args['+-'] or args['+/-'] | ||
+ | if args['+'] then | ||
+ | args['−'] = args['−'] or args['-'] -- tiret → signe moins | ||
+ | else | ||
+ | args['–'] = args['–'] or args['-'] -- tiret → demi-cadratin | ||
+ | end | ||
+ | local cat = '' | ||
+ | if addErrorCat then | ||
+ | cat = errorCat | ||
+ | end | ||
+ | return p._unite( args ) .. cat | ||
+ | end | ||
+ | end | ||
+ | |||
+ | return p |
Version actuelle datée du 28 décembre 2020 à 11:53
La documentation pour ce module peut être créée à Module:Unité/doc
local p = {}
-- local Delink = require( 'Module:Delink' ) -- chargé uniquement si nécessaire
-- Chargement de la base de données des nom d'unités avec gestion d'erreur.
local moduleData = 'Module:Unité/Data'
local dataSuccess, Data = pcall ( mw.loadData, moduleData )
if dataSuccess and type( Data ) == 'table' then
dataSuccess = type( Data.unit ) == 'table'
and type( Data.prefix ) == 'table'
and type( Data.exposant ) == 'table'
end
local errorCat = '[[Catégorie:Page incorrectement traitée par le Module:Unité]]'
local addErrorCat = false
local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹',
['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' }
local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉',
['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎',
['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ',
['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ',
}
--- Copie de Outils.trim acceptant les nombres.
local function trim( texte )
if type( texte ) == 'string' then
texte = texte:gsub( '^%s*(%S?.-)%s*$', '%1' )
if texte ~= '' then
return texte
end
elseif type( texte ) == 'number' then
return tostring( texte )
end
end
function p.sanitizeNum( nombre )
if type( nombre ) == 'number' then
return tostring( nombre )
elseif type( nombre ) == 'string' then
if nombre:match( '^%-?[%d.,]+$' ) then
return nombre
end
local result = nombre
-- remplacement des signes moins ou demi-cadratin par un tiret
:gsub( '%−%f[%d]', '-') -- U+2212
:gsub( '−%f[%d]', '-') -- html −
:gsub( '\226\128[\146\147]%f[%d]', '-') -- U+2212, U+2213 (tiret numérique et demi-cadratin)
-- remplacement des espaces insécable par des espace simple
:gsub( '\194\160', ' ' )
:gsub( ' ', ' ' )
:gsub( '\226\128[\128-\138\175]', ' ' ) -- U+2002 à U+200A et U+202F
-- trim
:gsub( '^%s*(%S?.-)%s*$', '%1' )
return result
else
return ''
end
end
---
-- parseNum transforme si possible une chaine formatée en un chaine interprétable par tonumber()
-- retourne une chaine pour éviter les arrondi éventuels de lua.
-- si "nombre" est une chaine non reconnue comme un nombre par la fonction, retourne "nombre".
-- si "nombre" n'est pas un number ou une chaine retourne une chaine vide.
function p.parseNombre( nombre )
local result
if type( nombre ) == 'number' then
return tostring( nombre )
else
-- remplacement des signes moins ou demi-cadratin par un tiret
result = p.sanitizeNum( nombre )
if result == '' then
return ''
elseif not result:match( '^%-?[%d., ]*%d$' ) then
return nombre
end
end
-- suppression espaces
result = result:gsub( ' ', '' )
-- gestion des points et des virgules
if result:match( '[.,]' ) then
if result:match( '%d%.%d%d%d%.%d' ) then
-- type 12.345.678
result = result:gsub( '%.', '' ):gsub( ',', '.' )
elseif result:match( '%d,%d%d%d,%d' ) -- type 1,234,567 ou 1.234,567,8
or result:match( '%d,%d%d%d%.%d' ) -- format anglo-saxon type 1,234.5
or result:match( '%d%.%d%d%d,%d' ) -- type 1.123,56 (utilisé en exemple pour sépararer les décimales avec l'ancien modèle unité ou formatnum)
then
result = result:gsub( ',', '' )
else
result = result:gsub( ',', '.' )
end
end
return result
end
---
-- _formantNum transforme un nombre ou une chaine représentant un nombre en chaine formatée suivant les conventions du français
-- si le paramètre ne représente pas un nombre lua il est retourné sans modification
-- Le paramètre peut être transmis sous forme de table pour ajouter des options :
-- * round : arrondi à n chiffre après la virgule (peut être négatif)
-- * decimals : nombre de décimales affichées (peut être négatif, dans ce cas équivalent à round)
-- * noHtml : n'utilise pas de balise HTML pour affiché les puissance de 10 (pour pouvoir être utilisé en title)
function p.formatNum( num )
local params = {}
if type( num ) == 'table' then
params = num
num = params[1]
end
if type( num ) == 'number' then
num = tostring( num )
elseif type( num ) ~= 'string' or num == '' then
return num
end
-- séparation exposant
local n, exponent = num:match( '^([-%d.]+)[eE]([+-]?%d+)$' )
if exponent then
num = n
if params.noHtml then
exponent = exponent:gsub('+?%f[%d]0', '' )
:gsub( '[%d-]', supUnicode )
else
exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−', [''] = '' } ) .. '</sup>'
end
exponent = ' ×10' .. exponent
else
exponent = ''
end
-- arrondi
local decimals = tonumber( params.decimals )
local round = tonumber( params.round ) or decimals
if round and tonumber( num ) then
local mult = 10 ^ round
num = tostring( math.floor( num * mult + 0.5 ) / mult )
end
local moins, entier, fraction = num:match( '^(%-?)(%d*)%.?(%d*)$' )
if not entier then
return num
end
if moins == '-' then
moins = '−' -- signe moins (U+2212)
end
if entier == '' then
entier = '0'
elseif entier:len() > 3 then
local ini = math.fmod( entier:len() - 1, 3 ) + 1
entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' )
end
if fraction ~= '' or ( decimals and decimals > 0 ) then
if decimals and decimals > #fraction then
fraction = fraction .. string.rep( '0', decimals - #fraction )
end
if #fraction > 4 then
fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' )
else
fraction = ',' .. fraction
end
end
return moins .. entier .. fraction .. exponent
end
---
-- formatNombre transforme un nombre formaté ou non en chaine formatée suivant les convention du français.
-- si la chaine n'est pas reconnu comme un nombre, elle n'est pas modifiée.
function p.formatNombre( num, round, decimals )
return p.formatNum{ p.parseNombre( num ), round = round, decimals = decimals }
end
--- formatNombres transforme tous les nombres d'une chaine en nombre formaté suivant les conventions du français.
function p.formatNombres( nombres, round, decimals )
if type( nombres ) == 'number' then
return p.formatNum( nombres, round, decimals )
elseif type( nombres ) == 'string' then
-- retire les chiffres des strip marker
local strip, i = {}, 0
nombres = nombres:gsub(
'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU',
function ( marker )
i = i + 1
strip[ tostring( i ) ] = marker
return 'UNIQ^' .. i .. '¤QINU'
end
)
-- formatage proprement dit
nombres = p.sanitizeNum( nombres )
local formatN = function ( n )
return p.formatNombre( n, round, decimals )
end
nombres = nombres
:gsub( '%-?%f[%d.,][%d., ]*%de[+-]?%d+', formatN )
:gsub( '%-?%f[%d.,][%d., ]*%d', formatN )
-- réintroduction des strip marker
nombres = nombres:gsub( 'UNIQ^(%d+)¤QINU', strip )
return nombres
else
return ''
end
end
function p.parseUnit( texte )
local toParse = p.sanitizeNum( texte )
if toParse ~= '' then
local result
local specificArgs = {
['à'] = 'à',
et = 'et',
ou = 'ou',
['/'] = '/',
['–'] = '–', ['-'] = '–', -- demi cadratin et tiret
['±'] = '±', ['+-'] = '±', ['+/-'] = '±',
['+'] = '+',
['−'] = '−', -- signe moins
['×'] = '×', x = '×', ['*'] = '×',
['××'] = '××', xx = '××', ['**'] = '××',
}
-- valeur numérique
local match, capture = toParse:match( '^(([%d., ]+%f[^d%(])%s*)' )
local prefix
if not match then
-- cas ou le nombre est remplcé par un ou plusieurs points d'interrogation
match, prefix = toParse:match( '^((%?+)%s*)' )
end
if not match then
-- cas ou un mot type "vers", "environ" précède le nombre (mot simple, sans accent pour ne pas complexifier pour des cas minoritaires)
match, prefix, capture = toParse:match( '^(([%a]+[.,]?[: ]* )([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
end
if not match then
-- cas ou le nombre est précédé par un signe, un symbole ASCII, ou suivit d'une incerititude entre parenthèse
match, prefix, capture = toParse:match( '^(([(<>=~ ]*)([+-]?%f[%d.,][%d., ]*%d%(?%d*%)?)%s*)' )
end
if not match then
-- cas ou le nombre est précédé par un symbole ≤, ≥ ou ≈
match, prefix, capture = toParse:match( '^((\226\137[\164\165\136] ?)([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
end
result = { capture or false, prefix = prefix }
if match then
toParse = toParse:sub( match:len() + 1 )
-- point de suspensions (ex π = 3.14159...)
match = toParse:match( '^…%s*' )
if not match then
match, capture = toParse:match( '^%.%.%.%s*' )
end
if match then
result[1] = result[1] .. '…'
toParse = toParse:sub( match:len() + 1 )
end
-- fraction
match, capture = toParse:match( '^((%d*/%d+)%s*)' )
if not match then
match, capture = toParse:match( '^((\194[\188-\190])%s*)' ) -- ¼ à ¾
end
if not match then
match, capture = toParse:match( '^((\226\133[\144-\158])%s*)' ) -- ⅐ à ⅞
end
if match then
if capture:match( '^/' ) then
local n = result[1]:match( ' %d+$' ) or result[1]:match( '^%d+$' ) or ''
result[1] = result[1]:sub( 1, -1 - #n )
result.fraction = n:gsub( '^ ', '' ) .. capture
else
result.fraction = capture
end
toParse = toParse:sub( match:len() + 1 )
end
-- lien avec un deuxième nombre
local match2, conj, num = mw.ustring.match( toParse, '^(([àetouM+/−x*×±–-]+) ?(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' )
if match2 and specificArgs[ conj ]
and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then
result[ specificArgs[ conj ] ] = num
toParse = toParse:sub( match2:len() + 1 )
end
if result['+'] or result['×'] then
match2, conj, num = mw.ustring.match( toParse, '^(([x*×−-]) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
if match2 then
if specificArgs[ conj ] == '×' then
result['××'] = num
else
result['−'] = num
end
toParse = toParse:sub( match2:len() + 1 )
end
end
end
-- 10 exposant ( \195\151 = ×, signe multiplié)
match, capture = toParse:match( '^(%s*e(%-?%d+)%s*)' )
if not match then
match, capture = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+)%s*)' )
end
if match then
result.e = capture
toParse = toParse:sub( match:len() + 1 )
end
-- unités
if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then
table.insert( result, toParse )
toParse = ''
elseif toParse ~= '' then
local unit, exp
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
repeat
-- unité contenant un lien
match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' )
if not match then
-- unité ne contenant pas de lien
match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' )
end
if not match then
-- l/100 km
match, unit, exp = mw.ustring.match( toParse, '^((/100 ?[^%s%d/]+) ?(%-?%d*)%s*)' )
end
if match then
if unit:match( '%-$' ) and exp ~= '' then
unit = unit:gsub( '%-$', '' )
exp = '-' .. exp
elseif exp == '-' then
unit = match
exp = ''
end
if Data.unit[ unit ] or mw.ustring.match( unit, '[%a€£$«»]' ) then
table.insert( result, unit )
table.insert( result, exp )
toParse = toParse:sub( match:len() + 1 )
else
break
end
end
until toParse == '' or not match
end
if toParse == '' then
if #result > 1 and result[ #result ] == '' then
result[ #result ] = nil
end
return result
else
-- une partie de la chaine n'a pas pu être décodée, on retourne la chaine originale
addErrorCat = true
return { texte }
end
else
return { }
end
end
---
-- nomUtnit retourne le nom français du code d'une unité et de son exposant.
-- si le code de l'unité n'est pas reconnu retourne 1 et false, de façon à ajouter false en première position d'une table.
function p.nomUnit( unit, exposant )
if not dataSuccess or type( unit ) ~= 'string' then
return 1, false
end
-- nettoyage des liens et balise HTML
unit = unit:gsub( '^/' , '' )
if unit:match( '%[' ) then
local Delink = require( 'Module:Delink' )
unit = Delink._delink{ unit }
end
if unit:match( '<' ) then
unit = unit:gsub( '%b<>', '' )
end
-- récupère le nom de l'unité
local unitTab = Data.unit[ unit ]
local unitPrefix = { nom = '' }
if not unitTab then
unitTab = Data.unit[ unit:sub( 2 ) ]
unitPrefix = Data.prefix[ unit:sub( 1, 1 ) ]
if not ( unitTab and unitPrefix ) then
-- pour µ, Ki, Mi, Gi... qui sont codé sur deux octets
unitTab = Data.unit[ unit:sub( 3 ) ]
unitPrefix = Data.prefix[ unit:sub( 1, 2 ) ]
if not ( unitTab and unitPrefix ) then
unitTab = false
end
end
end
-- récupère le nom de l'exposant
if trim( exposant ) then
local exp = tonumber( exposant )
exp = exp and Data.exposant[ math.abs( exp ) ]
exposant = exp or ' puissance ' .. exposant
else
exposant = ''
end
-- assemble les deux partie
if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then
return unitPrefix.nom .. unitTab.nom .. exposant
elseif unit:match( '[/%d]' ) then
-- ce n'est pas du texte simple, on anule l'infobule
return 1, false
else
return unit .. exposant
end
end
function p._unite( args )
-- remplacement de certains caractères, pour simplifier les pattern
local nombre = p.sanitizeNum( args[1] )
if nombre == '' then
nombre = nil
else
-- formatage du nombre
nombre = p.formatNombres( nombre, args.arrondi, args['décimales'] )
end
local wiki = { args.prefix or '', nombre } -- prefix est un paramètre interne défini par p.parseUnit, utile notamment lorsque {{unité}} est utilisé dans les infobox
-- fraction
if args.fraction then
local nom, den = args.fraction:match( '^(.-)/(.+)$' )
if nom then
if nom:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then
nom = nom:gsub( '[%dn()=+-]', supUnicode )
den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode )
else
nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nom .. '</sup>'
den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>'
end
args.fraction = nom .. '⁄' .. den
end
if nombre then
table.insert( wiki, '\194\160' )
end
table.insert( wiki, args.fraction )
end
-- à, et, ou, ×, – (tiret cadratin)
local specificArgs = { '–', 'à', 'et', 'ou', '/', '×', '××', '±' }
for _, name in ipairs( specificArgs ) do
local v = trim( args[ name ] )
if v then
v = p.formatNombres( v )
if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then
-- pas d'espace pour le tiret cadratin entre deux nombres positifs
table.insert( wiki, '–' )
elseif name == '/' then
-- espace demi-cadratin (U+2002 ou  ) avec /
table.insert( wiki, ' / ' )
elseif name == '××' then
table.insert( wiki, '\194\160×\194\160' )
elseif name == '×' or name == '±' then
table.insert( wiki, '\194\160' .. name .. '\194\160' )
else
table.insert( wiki, ' ' .. name .. ' ' )
end
table.insert( wiki, v )
end
end
-- analyse de l'unité pour la conversion (mais ne sera affiché qu'après l'incertitude + et - séparé
local i = 1
local unit = trim( args[ 2 * i ] )
local units = ''
local nomUnits, par = {}, false
while unit do
local exp = p.parseNombre( args[ 2 * i + 1 ] )
local sep = ''
-- gestion des exposants
local expUnit = ''
if exp == '' then
if unit:sub( -2 ) == '²' then
exp = '2'
unit = unit:sub( 1, -3 )
elseif unit:sub( -2 ) == '³' then
exp = '3'
unit = unit:sub( 1, -3 )
end
end
if #exp > 0 then
expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>' -- remplace le tiret par un vrai signe moins
end
-- gestion de la séparation des unités et des unités en dénominateur
if units ~= '' then
if unit:sub( 1, 1 ) == '/' then
sep = '/'
unit = unit:sub( 2 )
if not par then
par = true
table.insert( nomUnits, 'par' )
end
else
sep = '\194\160' -- point médian désactivé : '⋅\194\160'
end
end
if exp:match( '^-' ) and not par then
par = true
table.insert( nomUnits, 'par' )
end
-- remplacement de l'unité par son symbole
if Data.unit[ unit ] then
-- unit = Data.unit[ unit ].symbole
-- désactivé car ne gère pas les multiple tel mL
end
units = units .. sep .. unit .. expUnit
table.insert( nomUnits, p.nomUnit( unit, exp ) )
i = i + 1
unit = trim( args[ 2 * i ] )
end
-- conversion
local unitNameString = nomUnits[1] and table.concat( nomUnits, ' ' ) or ''
unitNameString = mw.ustring.gsub( unitNameString, '(%a)s%f[%A]', '%1' )
local multiple = 1
local convertTable = Data.convert[ unitNameString ]
if not convertTable and #unitNameString > 5 then
-- gesion des multiples (Kilo, méga, mili...)
local prefix = Data.prefix[ unitNameString:sub( 1, 4 ) ] or Data.prefix[ unitNameString:sub( 1, 5 ) ]
local _, par = unitNameString:find( ' par ' )
local prefix2
if par then
prefix2 = Data.prefix[ unitNameString:sub( par + 1, 4 ) ] or Data.prefix[ unitNameString:sub( par + 1, 5 ) ]
end
if prefix and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ] then
convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ]
multiple = 10 ^ prefix.puissance
elseif prefix2 and Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ] then
convertTable = Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ]
multiple = 1 / 10 ^ prefix2.puissance
elseif prefix and prefix2 and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] then
convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ]
multiple = 10 ^ prefix.puissance / 10 ^ prefix2.puissance
end
end
if convertTable then
if type( convertTable[1] ) ~= 'table' then
convertTable = { convertTable }
end
for i, v in ipairs( wiki ) do
local n = tonumber( p.parseNombre( v ) )
if n then
n = n * 10 ^ ( tonumber( p.parseNombre( args.e ) ) or 0 )
local converted = {}
for _, c in ipairs( convertTable ) do
local nConverted = n
if c.inverse then
nConverted = 1 / n
end
if c.M then
-- M = masse molaire
local M = tonumber( args.M )
if not M then
break
end
if c.M == '*' then
nConverted = nConverted * M
elseif c.M == '/' then
nConverted = nConverted / M
end
end
nConverted = nConverted * multiple * c[2] + ( c[3] or 0 )
-- format
nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true }
local sep = ' '
if mw.ustring.sub( c[1], 1, 1 ) == '°' then
sep = ''
end
mw.log( c[1], mw.ustring.codepoint( sep, 1, 1))
table.insert( converted, nConverted .. sep.. c[1] )
end
wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>'
end
end
end
-- incertitude avec + et − séparés
if trim( args['+'] ) then
local approximation = '+' .. p.formatNombre( args['+'] ) .. ''
if trim( args['−'] ) then
approximation = approximation .. '<br> −' .. p.formatNombre( args['−'] )
end
table.insert( wiki, '<span class="nowrap"><span style="display:inline-block; padding-left:0.2em; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">' )
table.insert( wiki, approximation .. '</span></span>' )
end
-- puissance de 10
local exposant = trim( args.e )
if exposant then
exposant = p.formatNombre( exposant )
if nombre then
if trim( args['±'] ) and not nombre:match( '^%(' ) then
table.insert( wiki, 1, '(' )
table.insert( wiki, ')' )
end
table.insert( wiki, '\194\160× 10<sup>' .. exposant .. '</sup>' )
else
table.insert( wiki, '10<sup>' .. exposant .. '</sup>' )
end
end
if units ~= '' then
local sep = '\194\160'
if not ( nombre or args.fraction or exposant ) then
sep = ''
else
local symbole = Data.unit[ units ] and Data.unit[ units ].symbole
if symbole == '°' or symbole == '′' or symbole == '″' then
sep = ''
end
end
-- ajoute une abréviation si le nom de l'unité est différent de l'unité (en retirant les espaces qui peuvent être devenus insécables)
if nomUnits[1] and mw.ustring.gsub( table.concat( nomUnits ), '[ \194\160]', '' ) ~= mw.ustring.gsub( units, '[ \194\160]', '' ) then
units = string.format( '<abbr class=abbr title="%s">%s</abbr>', table.concat( nomUnits, ' ' ), units )
end
table.insert( wiki, sep .. units )
end
if #wiki > 0 then
return table.concat( wiki )
end
end
function p.unite( frame )
local args
if type( frame ) == 'table' then
if type( frame.getParent ) == 'function' then
args = frame:getParent().args;
else
args = frame
end
end
if args then
args[1] = trim( args[1] ) or false
if args[1] then
if args[1]:match('[^%d,. -]') then
local tempArgs = p.parseUnit( args[1] )
if not ( args[2] and tempArgs[2] ) then
for k, v in pairs( tempArgs ) do
args[k] = v
end
end
end
args[2] = trim( args[2] ) or false
if args[2] and not args[3] and args[2]:match('/') then
local tempArgs = p.parseUnit( args[2] )
args[2] = false
if tempArgs[1] ~= false then
table.insert( tempArgs, 1, false )
end
for k, v in pairs( tempArgs ) do
if args[k] and v then
addErrorCat = true
end
args[k] = args[k] or v
end
end
end
-- args alias
args['×'] = args['×'] or args['x'] -- lettre x → signe multiplié
args['±'] = args['±'] or args['+-'] or args['+/-']
if args['+'] then
args['−'] = args['−'] or args['-'] -- tiret → signe moins
else
args['–'] = args['–'] or args['-'] -- tiret → demi-cadratin
end
local cat = ''
if addErrorCat then
cat = errorCat
end
return p._unite( args ) .. cat
end
end
return p