« Unité » : différence entre les versions
De Wikimanche
(Mise à jour module) |
(Mise à jour) |
||
Ligne 18 : | Ligne 18 : | ||
['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' } | ['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' } | ||
local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉', | local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉', | ||
['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ', | ['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ', | ||
['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ', | ['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ', | ||
} | } | ||
local fractionUnicode = { ['½'] = '1/2', ['⅓'] = '1/3', ['⅕'] = '1/5', ['⅙'] = '1/6', ['⅛'] = '1/8', | |||
['⅔'] = '2/3', ['⅖'] = '2/5', ['⅚'] = '5/6', ['⅜'] = '3/8', ['¾'] = '3/4', ['⅗'] = '3/5', | |||
['⅝'] = '5/8', ['⅞'] = '7/8', ['⅘'] = '4/5', ['¼'] = '1/4', ['⅐'] = '1/7', ['⅑'] = '1/9', ['⅒'] = '1/10', ['↉'] = '0/3', | |||
} | |||
local nbsp = '\194\160' -- espace insécable | |||
local nnbsp = '\226\128\175' -- espace fine insécable | |||
--- Copie de Outils.trim acceptant les nombres. | --- Copie de Outils.trim acceptant les nombres. | ||
local function trim( texte ) | local function trim( texte ) | ||
if type( texte ) == 'string' then | if type( texte ) == 'string' then | ||
texte = texte: | -- http://lua-users.org/wiki/StringTrim | ||
texte = texte:match( '^()%s*$' ) and '' or texte:match( '^%s*(.*%S)' ) | |||
if texte ~= '' then | if texte ~= '' then | ||
return texte | return texte | ||
Ligne 34 : | Ligne 41 : | ||
end | end | ||
-- retire les chiffres des strip markers | |||
local function escapeStripMarkers( input ) | |||
return input:gsub( '(UNIQ%-%-%a+%-)(%x%x%x%x%x%x%x%x)(%-QINU)', function ( leading, hexdigits, trailing ) | |||
local escapeddigits = hexdigits:gsub( '%d', { | |||
['0'] = 'g', ['1'] = 'h', ['2'] = 'i', ['3'] = 'j', ['4'] = 'k', | |||
['5'] = 'l', ['6'] = 'm', ['7'] = 'n', ['8'] = 'o', ['9'] = 'p', | |||
} ) | |||
return leading .. escapeddigits .. trailing | |||
end ) | |||
end | |||
-- restaure les strip markers | |||
local function restoreStripMarkers( input ) | |||
return input:gsub( '(UNIQ%-%-%a+%-)(%a%a%a%a%a%a%a%a)(%-QINU)', function ( leading, escapeddigits, trailing ) | |||
local hexdigits = escapeddigits:gsub( '%a', { | |||
['g'] = '0', ['h'] = '1', ['i'] = '2', ['j'] = '3', ['k'] = '4', | |||
['l'] = '5', ['m'] = '6', ['n'] = '7', ['o'] = '8', ['p'] = '9', | |||
} ) | |||
return leading .. hexdigits .. trailing | |||
end ) | |||
end | |||
-- remplacement de certains caractères, pour simplifier les pattern | |||
function p.sanitizeNum( nombre ) | function p.sanitizeNum( nombre ) | ||
if type( nombre ) == 'number' then | if type( nombre ) == 'number' then | ||
Ligne 42 : | Ligne 72 : | ||
end | end | ||
local result = nombre | local result = nombre | ||
-- remplacement des signes moins | -- remplacement des signes moins par un tiret | ||
:gsub( '%−%f[%d]', '-') -- U+2212 | :gsub( '%−%f[%d]', '-') -- U+2212 | ||
:gsub( '−%f[%d]', '-') -- html − | :gsub( '−%f[%d]', '-') -- html − | ||
-- remplacement des espaces insécable par des espace simple | -- remplacement des espaces insécable par des espace simple | ||
:gsub( | :gsub( nbsp, ' ' ) | ||
:gsub( ' ', ' ' ) | :gsub( ' ', ' ' ) | ||
:gsub( '\226\128[\ | :gsub( ' ', ' ' ) | ||
:gsub( nnbsp, ' ' ) | |||
:gsub( ' ', ' ' ) | |||
:gsub( ' ', ' ' ) | |||
:gsub( '\226\128[\132-\138]', ' ' ) -- U+2004 à U+200A | |||
:gsub( ' ', ' ' ) | |||
-- trim | -- trim | ||
:gsub( '^%s*(%S?.-)%s*$', '%1' ) | :gsub( '^%s*(%S?.-)%s*$', '%1' ) | ||
Ligne 72 : | Ligne 106 : | ||
if result == '' then | if result == '' then | ||
return '' | return '' | ||
end | |||
-- si nombre est un chiffre en exposant ou indice comme ², retourne ce chiffre | |||
for i = 0, 9 do | |||
local is = tostring(i) | |||
if result == supUnicode[ is ] or result == subUnicode[ is ] then | |||
return is | |||
end | |||
end | |||
if not result:match( '^%-?[%d., ]*%d$' ) and not result:match( '^%-?[%d., ]*%d ?e[+-]?%d+$' ) then | |||
return nombre | return nombre | ||
end | end | ||
Ligne 94 : | Ligne 136 : | ||
end | end | ||
end | end | ||
return result | return result | ||
end | end | ||
Ligne 127 : | Ligne 169 : | ||
exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−', [''] = '' } ) .. '</sup>' | exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−', [''] = '' } ) .. '</sup>' | ||
end | end | ||
exponent = ' | if num == '1' then | ||
return '10' .. exponent | |||
end | |||
exponent = nbsp .. '×' .. nnbsp .. '10' .. exponent | |||
else | else | ||
exponent = '' | exponent = '' | ||
Ligne 140 : | Ligne 185 : | ||
end | end | ||
local moins, entier, | local moins, entier, deci = num:match( '^(%-?)(%d*)%.?(%d*)$' ) | ||
if not entier then | if not entier then | ||
return num | return num | ||
Ligne 153 : | Ligne 198 : | ||
elseif entier:len() > 3 then | elseif entier:len() > 3 then | ||
local ini = math.fmod( entier:len() - 1, 3 ) + 1 | local ini = math.fmod( entier:len() - 1, 3 ) + 1 | ||
entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', ' | entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', nbsp .. '%1' ) | ||
end | end | ||
if | if deci ~= '' or ( decimals and decimals > 0 ) then | ||
if decimals and decimals > # | if decimals and decimals > #deci then | ||
deci = deci .. string.rep( '0', decimals - #deci ) | |||
end | end | ||
if # | if #deci > 3 then | ||
deci = ',' .. deci:gsub( '(%d%d%d)', '%1' .. nbsp ):gsub( nbsp .. '$', '' ) | |||
else | else | ||
deci = ',' .. deci | |||
end | end | ||
end | end | ||
return moins .. entier .. | return moins .. entier .. deci .. exponent | ||
end | end | ||
Ligne 179 : | Ligne 224 : | ||
function p.formatNombres( nombres, round, decimals ) | function p.formatNombres( nombres, round, decimals ) | ||
if type( nombres ) == 'number' then | if type( nombres ) == 'number' then | ||
return p.formatNum | return p.formatNum{ nombres, round = round, decimals = decimals } | ||
elseif type( nombres ) == 'string' then | elseif type( nombres ) == 'string' then | ||
-- retire les chiffres des strip | -- retire les chiffres des strip markers | ||
nombres = escapeStripMarkers( nombres ) | |||
nombres = nombres | |||
-- formatage proprement dit | |||
-- | |||
nombres = p.sanitizeNum( nombres ) | nombres = p.sanitizeNum( nombres ) | ||
local formatN = function ( n ) | local formatN = function ( n ) | ||
return p.formatNombre( n, round, decimals ) | return p.formatNombre( n, round, decimals ) | ||
end | end | ||
nombres = nombres | if nombres:match('%d%-%d') then | ||
:gsub( '%-?%f[%d.,][%d., ]*% | nombres = nombres:gsub( '%f[%d.,][%d., ]*%d', formatN ) | ||
else | |||
nombres = nombres | |||
:gsub( '%-?%f[%d.,][%d., ]*%d ?e[+-]?%d+', formatN ) | |||
:gsub( '%-?%f[%d.,][%d., ]*%d', formatN ) | |||
end | |||
-- restaure les strip markers | |||
nombres = restoreStripMarkers( nombres ) | |||
return nombres | return nombres | ||
else | else | ||
Ligne 216 : | Ligne 259 : | ||
et = 'et', | et = 'et', | ||
ou = 'ou', | ou = 'ou', | ||
['/'] = '/', | ['/'] = '/', [';'] = '/', | ||
['–'] = '–', ['-'] = '–', -- demi cadratin et tiret | ['//'] = '//', | ||
['–'] = '–', ['—'] = '–', ['-'] = '–', -- demi cadratin, cadratin et tiret | |||
['±'] = '±', ['+-'] = '±', ['+/-'] = '±', | ['±'] = '±', ['+-'] = '±', ['+/-'] = '±', | ||
['+'] = '+', | ['+'] = '+', | ||
Ligne 226 : | Ligne 270 : | ||
-- valeur numérique | -- valeur numérique | ||
local | local cap0, capture = toParse:match( '^(([%d., ]+%f[^d%(])%s*)' ) | ||
local prefix | local prefix | ||
if not match then | if not cap0 then | ||
-- cas d'un nombre entre guillemet, gras, italique... | |||
cap0, capture = toParse:match( '^((["\']+[%d., ]+["\']+)%s*)' ) | |||
end | |||
if not cap0 then | |||
-- cas ou le nombre est remplcé par un ou plusieurs points d'interrogation | -- cas ou le nombre est remplcé par un ou plusieurs points d'interrogation | ||
cap0, prefix = toParse:match( '^((%?+)%s*)' ) | |||
end | end | ||
if not | if not cap0 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) | -- cas ou un mot type "vers", "environ" précède le nombre (mot simple, sans accent pour ne pas complexifier pour des cas minoritaires) | ||
cap0, prefix, capture = toParse:match( '^(([%a ]+[.,]?[: ]* )([+-]? ?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | |||
end | end | ||
if not | if not cap0 then | ||
-- cas ou le nombre est précédé par un signe, un symbole ASCII, ou suivit d'une incerititude entre parenthèse | -- cas ou le nombre est précédé par un signe, un symbole ASCII, ou suivit d'une incerititude entre parenthèse | ||
cap0, prefix, capture = toParse:match( '^(([(<>=~ ]*)([+-]? ?%f[%d.,][%d., ]*%d%(?[%d%.]*%)?)%s*)' ) | |||
end | |||
if not cap0 then | |||
-- cas ou le nombre est précédé par un symbole ≤, ≥, ≈, ≃ et quelque autres | |||
cap0, prefix, capture = toParse:match( '^((\226[\136\137][\131\136\164\165\187\188] ?)([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | |||
end | end | ||
if not | if not cap0 then | ||
-- cas ou le nombre est précédé par un symbole | -- cas ou le nombre est précédé par un symbole ± (\194\177) | ||
cap0, prefix, capture = toParse:match( '^((±) ?(%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | |||
end | end | ||
result = { capture or false, prefix = prefix } | result = { capture or false, prefix = prefix } | ||
if | |||
toParse = toParse:sub( | if cap0 then | ||
toParse = toParse:sub( cap0:len() + 1 ) | |||
-- point de suspensions (ex π = 3.14159...) | -- point de suspensions (ex π = 3.14159...) | ||
cap0 = toParse:match( '^…%s*' ) | |||
if not | if not cap0 then | ||
cap0 = toParse:match( '^%.%.%.%s*' ) | |||
end | end | ||
if | if cap0 then | ||
result[1] = result[1] .. '…' | result[1] = result[1] .. '…' | ||
toParse = toParse:sub( | toParse = toParse:sub( cap0:len() + 1 ) | ||
end | end | ||
if toParse == '' then | |||
return result | |||
end | |||
end | |||
-- fraction | |||
capture = mw.ustring.sub( toParse, 1, 1 ) | |||
if not | if fractionUnicode[ capture ] then | ||
result.fraction = fractionUnicode[ capture ] | |||
toParse = toParse:sub( capture:len() + 1 ):gsub( '^%s*', '' ) | |||
result[1] = result[1] or '' | |||
else | |||
cap0, capture = toParse:match( '^(([%d,]*/%f[%d][%d ]*%d)%s*)' ) | |||
if not cap0 then | |||
-- caractère de fraction ⁄ = \226\129\132 | |||
cap0, capture = toParse:match( '^((%d*⁄%d+)%s*)' ) | |||
if cap0 then | |||
capture = capture:gsub( '⁄', '/' ) | |||
end | |||
end | end | ||
if | if cap0 then | ||
if result[1] and capture:match( '^/' ) then | |||
local n = result[1]:match( ' %d+$' ) or result[1]:match( '^%d+$' ) or '' | local n = result[1]:match( ' %d+$' ) or result[1]:match( '^%d+$' ) or '' | ||
result[1] = result[1]:sub( 1, -1 - #n ) | result[1] = result[1]:sub( 1, -1 - #n ) | ||
Ligne 274 : | Ligne 338 : | ||
result.fraction = capture | result.fraction = capture | ||
end | end | ||
toParse = toParse:sub( | toParse = toParse:sub( cap0:len() + 1 ) | ||
end | end | ||
end | |||
if toParse~= '' and ( result[1] or result.fraction ) then | |||
-- lien avec un deuxième nombre | -- lien avec un deuxième nombre | ||
local | local cap0, conj, num = toParse:match( '^(([etou+/;x*-]+) *(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' ) | ||
if | if not cap0 and toParse:byte() > 127 then | ||
and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then | cap0, conj, num = mw.ustring.match( toParse, '^(([à−×±—–]+) *(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' ) | ||
end | |||
if cap0 and specificArgs[ conj ] | |||
and not ( | |||
specificArgs[ conj ] == '×' | |||
and ( | |||
mw.ustring.match( toParse, '^[×x] ?10 ?e' ) | |||
or mw.ustring.match( toParse, '^[×x] ?10<sup>(%-?%d+)</sup>' ) | |||
) | |||
) | |||
then | |||
result[ specificArgs[ conj ] ] = num | result[ specificArgs[ conj ] ] = num | ||
toParse = toParse:sub( | toParse = toParse:sub( cap0:len() + 1 ) | ||
end | end | ||
if result['+'] or result['×'] then | if result['+'] or result['×'] or result['/'] then | ||
cap0, conj, num = mw.ustring.match( toParse, '^(([/;x*×−-]) *(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) | |||
if | if cap0 then | ||
if specificArgs[ conj ] == '×' then | if specificArgs[ conj ] == '×' then | ||
result['××'] = num | result['××'] = num | ||
elseif specificArgs[ conj ] == '/' then | |||
result['//'] = num | |||
else | else | ||
result['−'] = num | result['−'] = num | ||
end | end | ||
toParse = toParse:sub( | toParse = toParse:sub( cap0:len() + 1 ) | ||
end | end | ||
end | end | ||
Ligne 298 : | Ligne 376 : | ||
-- 10 exposant ( \195\151 = ×, signe multiplié) | -- 10 exposant ( \195\151 = ×, signe multiplié) | ||
cap0, capture = toParse:match( '^(e(%-?%d+)%s*)' ) | |||
if not | if not cap0 then | ||
cap0, capture = toParse:match( '^([x\195]\151? ?10e(%-?%d+)%s*)' ) | |||
end | |||
if not cap0 then | |||
cap0, capture = toParse:match( '^([x\195]\151? ?10<sup>(%-?%d+)</sup>%s*)' ) | |||
end | end | ||
if | if cap0 then | ||
result.e = capture | result.e = capture | ||
toParse = toParse:sub( match:len() + 1 ) | toParse = toParse:sub( cap0:len() + 1 ) | ||
end | |||
if result[1] == '10' and not result.e and not result.fraction then | |||
cap0, capture = toParse:match( '^(<sup>(%-?%d+)</sup>%s*)' ) | |||
if cap0 then | |||
result[1] = false | |||
result.e = capture | |||
toParse = toParse:sub( cap0:len() + 1 ) | |||
end | |||
end | end | ||
if toParse == '' then | |||
return result | |||
end | |||
-- unités | -- unités | ||
if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) | local texteUnit = toParse | ||
toParse = toParse:gsub( '^([^%[<]-)<sup>(%d)</sup>', '%1%2' ) | |||
if Data.unit[ toParse ] | |||
or toParse:match( '%b<>' ) | |||
or toParse:match( 'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU' ) | |||
or mw.ustring.match( toParse, '^%a+$' ) | |||
then | |||
result[ #result + 1] = toParse | |||
toParse = '' | toParse = '' | ||
elseif toParse ~= '' then | elseif toParse:match( '%b<>' ) then | ||
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' ) | |||
end | |||
if toParse ~= '' then | |||
local unit, exp | local unit, exp | ||
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' ) | toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' ) | ||
repeat | repeat | ||
-- unité contenant un lien | -- unité contenant un lien | ||
cap0, unit, exp = mw.ustring.match( toParse, '^((/? ?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' ) | |||
if not | if not cap0 then | ||
-- unité ne contenant pas de lien | -- unité ne contenant pas de lien | ||
cap0, unit, exp = mw.ustring.match( toParse, '^((/? ?[^%s%d/]+) ?(%-?%d*)%s*)' ) | |||
end | end | ||
if not | if not cap0 then | ||
-- l/100 km | -- l/100 km | ||
cap0, unit, exp = mw.ustring.match( toParse, '^((/100 ?[^%s%d/]+) ?(%-?%d*)%s*)' ) | |||
end | end | ||
if | if cap0 then | ||
if unit:match( '%-$' ) and exp ~= '' then | if unit:match( '%-$' ) and exp ~= '' then -- rustine pour quand le "-" se retrouve dans la capture "unit" au lieu de la capture "exp" | ||
unit = unit:gsub( '%-$', '' ) | unit = unit:gsub( '%-$', '' ) | ||
exp = '-' .. exp | exp = '-' .. exp | ||
elseif exp == '-' then | elseif exp == '-' then -- rustine pour quand un "-" a été capturé dans "exp" mais sans qu'il y ait de chiffres après | ||
unit = | unit = cap0 | ||
exp = '' | exp = '' | ||
end | end | ||
if Data.unit[ unit ] or mw.ustring.match( unit, '[%a€£$ | if Data.unit[ unit ] or mw.ustring.match( unit, '[%a€£$¥«»]' ) then | ||
result[ #result + 1] = unit | |||
result[ #result + 1] = exp | |||
toParse = toParse:sub( | toParse = toParse:sub( cap0:len() + 1 ) | ||
else | else | ||
break | break | ||
end | end | ||
end | end | ||
until toParse == '' or not | until toParse == '' or not cap0 | ||
end | end | ||
if toParse == '' then | if toParse == '' then | ||
if #result > 3 then | |||
local estSimpleTexte = true | |||
for r = 2, #result, 2 do | |||
if Data.unit[ result[ r ] ] | |||
or result[ r ]:sub( 1, 1 ) == '/' | |||
or Data.prefix[ result[ r ]:sub( 1, 1 ) ] and Data.unit[ result[ r ]:sub( 2 ) ] | |||
or Data.prefix[ result[ r ]:sub( 1, 2 ) ] and Data.unit[ result[ r ]:sub( 3 ) ] | |||
or result[ r + 1 ] and result[ r + 1 ] ~= '' | |||
then | |||
estSimpleTexte = false | |||
break | |||
end | |||
end | |||
if estSimpleTexte then | |||
result[ 2 ] = texteUnit | |||
for r = #result, 3, -1 do | |||
result[ r ] = nil | |||
end | |||
end | |||
end | |||
if #result > 1 and result[ #result ] == '' then | if #result > 1 and result[ #result ] == '' then | ||
result[ #result ] = nil | result[ #result ] = nil | ||
Ligne 360 : | Ligne 482 : | ||
--- | --- | ||
-- | -- nomUnit 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 | -- si le code de l'unité n'est pas reconnu, retourne false. | ||
function p.nomUnit( unit, exposant ) | function p.nomUnit( unit, exposant ) | ||
unit = trim( unit ) | |||
if not dataSuccess or type( unit ) ~= 'string' then | if not dataSuccess or type( unit ) ~= 'string' then | ||
return | return false | ||
end | end | ||
-- nettoyage des liens et balise HTML | -- nettoyage des liens et balise HTML | ||
unit = unit:gsub( '^/' , '' ) | unit = unit:gsub( '^/' , '' ) | ||
Ligne 375 : | Ligne 497 : | ||
if unit:match( '<' ) then | if unit:match( '<' ) then | ||
unit = unit:gsub( '%b<>', '' ) | unit = unit:gsub( '%b<>', '' ) | ||
end | |||
-- /100 | |||
local divisor = '' | |||
if unit:sub( 1, 2 ) == '10' then | |||
divisor, unit = unit:match( '^(1[0 ]*)(.+)$' ) | |||
local divisorName = { | |||
['10'] = 'dix ', | |||
['100'] = 'cent ', | |||
['1000'] = 'mille ', | |||
['10000'] = 'dix-mille ', | |||
['100000'] = 'cent-mille ', | |||
['1000000'] = 'un million de ', | |||
['1000000000'] = 'un millard de ', | |||
} | |||
divisor = divisorName[ divisor:gsub( ' ', '' ) ] | |||
end | end | ||
Ligne 404 : | Ligne 542 : | ||
-- assemble les deux partie | -- assemble les deux partie | ||
if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then | if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then | ||
return unitPrefix.nom .. unitTab.nom .. exposant | return divisor .. unitPrefix.nom .. unitTab.nom .. exposant | ||
elseif unit:match( '[/%d]' ) then | elseif unit:match( '[/%d]' ) then | ||
-- ce n'est pas du texte simple, on anule l'infobule | -- ce n'est pas du texte simple, on anule l'infobule | ||
return | return false | ||
else | else | ||
return unit .. exposant | return unit .. exposant | ||
Ligne 414 : | Ligne 552 : | ||
function p._unite( args ) | function p._unite( args ) | ||
-- | -- formatage du nombre | ||
local nombre = p. | local nombre = p.formatNombres( args[1], args.arrondi, args['décimales'] ) | ||
if nombre == '' then | if nombre == '' then | ||
nombre = nil | nombre = nil | ||
end | end | ||
local wiki = { | local wiki = {} | ||
-- prefix est un paramètre interne défini par p.parseUnit, utile notamment lorsque {{unité}} est utilisé dans les infobox | |||
if args.prefix then | |||
wiki[ #wiki + 1 ] = args.prefix | |||
end | |||
if nombre then | |||
wiki[ #wiki + 1 ] = nombre | |||
end | |||
-- fraction | -- fraction | ||
local fraction = args.fraction | |||
local nom, den = | if fraction then | ||
fraction = fractionUnicode[ fraction ] or fraction | |||
local nom, den = fraction:match( '^(.-)/(.+)$' ) | |||
if nom then | if nom then | ||
if nom:match( '^[ %dn | if nom:match( '^[%dn]%d?$' ) and den:match( '^[%daeoxhklmnpst]$' ) then | ||
nom = nom:gsub( '[%dn()=+-]', supUnicode ) | nom = nom:gsub( '[%dn()=+-]', supUnicode ) | ||
den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode ) | den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode ) | ||
else | else | ||
nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nom .. '</sup>' | nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' | ||
den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>' | .. p.formatNombres( nom ) | ||
.. '</sup>' | |||
den = '<sub style="font-size: 70%; vertical-align: 0em;">' | |||
.. p.formatNombres( den ) | |||
.. '</sub>' | |||
end | end | ||
fraction = nom .. '⁄' .. den | |||
end | end | ||
if nombre then | if nombre then | ||
wiki[ #wiki + 1 ] = nbsp | |||
end | end | ||
wiki[ #wiki + 1 ] = fraction | |||
end | end | ||
-- à, et, ou, ×, – (tiret cadratin) | -- à, et, ou, ×, – (tiret cadratin) | ||
local specificArgs = { '–', 'à', 'et', 'ou', '/', '×', '××', '±' } | local specificArgs = { '–', 'à', 'et', 'ou', '/', '//', '×', '××', '±' } | ||
for | for i = 1, #specificArgs do | ||
local v = trim( args[ name ] ) | local name = specificArgs[ i ] | ||
local v = args[ name ] and trim( args[ name ] ) | |||
if v then | if v then | ||
v = p.formatNombres( v ) | v = p.formatNombres( v ) | ||
if name == '//' then | |||
name = '/' | |||
elseif name == '××' then | |||
name = '×' | |||
end | |||
if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then | if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then | ||
-- pas d'espace pour le tiret cadratin entre deux nombres positifs | -- pas d'espace pour le tiret cadratin entre deux nombres positifs | ||
wiki[ #wiki + 1 ] = '–' | |||
elseif name == '×' or name == '±' then | elseif name == '×' or name == '±' then | ||
wiki[ #wiki + 1 ] = nbsp .. name .. nbsp | |||
else | else | ||
wiki[ #wiki + 1 ] = nbsp .. name .. ' ' | |||
end | end | ||
wiki[ #wiki + 1 ] = v | |||
end | end | ||
end | end | ||
-- analyse de l'unité pour la conversion (mais ne sera affiché qu'après l'incertitude + et - séparé | -- analyse de l'unité pour la conversion (mais ne sera affiché qu'après l'incertitude + et - séparé) | ||
local i = 1 | local i = 1 | ||
local unit = trim( args[ 2 * i ] ) | local unit = trim( args[ 2 * i ] ) | ||
Ligne 479 : | Ligne 630 : | ||
local expUnit = '' | local expUnit = '' | ||
if exp == '' then | if exp == '' then | ||
local suffix = unit:sub( -2 ) -- yes, it's 2 bytes | |||
if suffix == '²' then | |||
exp = '2' | exp = '2' | ||
unit = unit:sub( 1, -3 ) | unit = unit:sub( 1, -3 ) | ||
elseif | elseif suffix == '³' then | ||
exp = '3' | exp = '3' | ||
unit = unit:sub( 1, -3 ) | unit = unit:sub( 1, -3 ) | ||
Ligne 491 : | Ligne 643 : | ||
end | end | ||
-- gestion de la séparation des unités et des unités en dénominateur | -- gestion de la séparation des unités et des unités en dénominateur | ||
if unit:sub( 1, 1 ) == '/' then | |||
sep = '/' | |||
unit = trim( unit:sub( 2 ) ) or '' | |||
if not par then | |||
par = true | |||
if unit:sub( 1, 2 ) == '10' then | |||
nomUnits[ #nomUnits + 1 ] = 'pour' | |||
else | |||
nomUnits[ #nomUnits + 1 ] = 'par' | |||
end | end | ||
else | else | ||
nomUnits[ #nomUnits + 1 ] = 'et par' | |||
if nomUnits[ #nomUnits - 2 ] == 'et par' then | |||
nomUnits[ #nomUnits - 2 ] = 'par' | |||
end | |||
end | end | ||
elseif units ~= '' then | |||
sep = nbsp | |||
end | end | ||
if exp:match( '^-' ) and not par then | if exp:match( '^-' ) and not par then | ||
par = true | par = true | ||
nomUnits[ #nomUnits + 1 ] = 'par' | |||
end | end | ||
-- remplacement de l'unité par son symbole | -- remplacement de l'unité par son symbole | ||
Ligne 513 : | Ligne 672 : | ||
end | end | ||
units = units .. sep .. unit .. expUnit | units = units .. sep .. unit .. expUnit | ||
local nomUnit = p.nomUnit( unit, exp ) | |||
if nomUnit then | |||
nomUnits[ #nomUnits + 1 ] = nomUnit | |||
else | |||
-- si le code de l'unité n'est pas reconnu, insère false en première position de la table. | |||
table.insert( nomUnits, 1, false ) | |||
end | |||
i = i + 1 | i = i + 1 | ||
unit = trim( args[ 2 * i ] ) | unit = trim( args[ 2 * i ] ) | ||
end | end | ||
local unitFullName = nomUnits[1] and table.concat( nomUnits, ' ' ) or false | |||
-- conversion | -- conversion | ||
local | if unitFullName then | ||
local nameSingular = mw.ustring.gsub( unitFullName, '(%a)s%f[%A]', '%1' ) | |||
local multiple = 1 | |||
local convertTable = Data.convert[ nameSingular ] | |||
if not convertTable and #nameSingular > 5 then | |||
-- gesion des multiples (Kilo, méga, mili...) | |||
local prefix = Data.prefix[ nameSingular:sub( 1, 4 ) ] or Data.prefix[ nameSingular:sub( 1, 5 ) ] | |||
local _, par = nameSingular:find( ' par ' ) | |||
local prefix2 | |||
if par then | |||
prefix2 = Data.prefix[ nameSingular:sub( par + 1, 4 ) ] or Data.prefix[ nameSingular:sub( par + 1, 5 ) ] | |||
end | |||
if prefix and Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ) ] then | |||
convertTable = Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ) ] | |||
multiple = 10 ^ prefix.puissance | |||
elseif prefix2 and Data.convert[ nameSingular:gsub( ' par ' .. prefix2.nom, '' ) ] then | |||
convertTable = Data.convert[ nameSingular:gsub( ' par ' .. prefix2.nom, '' ) ] | |||
multiple = 1 / 10 ^ prefix2.puissance | |||
elseif prefix and prefix2 and Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] then | |||
convertTable = Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] | |||
multiple = 10 ^ prefix.puissance / 10 ^ prefix2.puissance | |||
end | |||
end | end | ||
if | if convertTable then | ||
if type( convertTable[1] ) ~= 'table' then | |||
convertTable = { convertTable } | |||
end | |||
for i = 1, #wiki do | |||
local v = wiki[ i ] | |||
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 | 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 c[1]:sub( 1, 2 ) == '°' then -- yes, it's 2 bytes | |||
sep = '' | |||
end | end | ||
converted[ #converted + 1 ] = nConverted .. sep.. c[1] | |||
end | end | ||
wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>' | |||
end | end | ||
end | end | ||
end | end | ||
Ligne 589 : | Ligne 757 : | ||
approximation = approximation .. '<br> −' .. p.formatNombre( args['−'] ) | approximation = approximation .. '<br> −' .. p.formatNombre( args['−'] ) | ||
end | end | ||
wiki[ #wiki + 1 ] = '<span class="nowrap"><span style="display:inline-block; padding-left:0.2em; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">' | |||
wiki[ #wiki + 1 ] = approximation .. '</span></span>' | |||
end | end | ||
Ligne 600 : | Ligne 768 : | ||
if trim( args['±'] ) and not nombre:match( '^%(' ) then | if trim( args['±'] ) and not nombre:match( '^%(' ) then | ||
table.insert( wiki, 1, '(' ) | table.insert( wiki, 1, '(' ) | ||
wiki[ #wiki + 1 ] = ')' | |||
end | end | ||
wiki[ #wiki + 1 ] = nbsp .. '×' .. nnbsp .. '10<sup>' .. exposant .. '</sup>' | |||
else | else | ||
wiki[ #wiki + 1 ] = '10<sup>' .. exposant .. '</sup>' | |||
end | end | ||
end | end | ||
if units ~= '' then | if units ~= '' then | ||
local sep = | local sep = nbsp | ||
if not ( nombre or args.fraction or exposant ) then | if not ( nombre or args.fraction or exposant ) then | ||
sep = '' | sep = '' | ||
Ligne 618 : | Ligne 786 : | ||
end | end | ||
end | end | ||
-- ajoute une abréviation si le nom de l'unité est différent de l'unité (en | -- ajoute une abréviation si le nom de l'unité est différent de l'unité (en considérant les espaces qui peuvent être devenus insécables) | ||
if | if unitFullName and unitFullName ~= units:gsub( nbsp, ' ' ) then | ||
units = string.format( '<abbr class=abbr title="%s">%s</abbr>', | units = string.format( '<abbr class="abbr" title="%s">%s</abbr>', unitFullName, units ) | ||
end | end | ||
wiki[ #wiki + 1 ] = sep .. units | |||
end | end | ||
Ligne 634 : | Ligne 802 : | ||
if type( frame ) == 'table' then | if type( frame ) == 'table' then | ||
if type( frame.getParent ) == 'function' then | if type( frame.getParent ) == 'function' then | ||
args = frame:getParent().args | args = frame:getParent().args | ||
else | else | ||
args = frame | args = frame | ||
Ligne 640 : | Ligne 808 : | ||
end | end | ||
if args then | if args then | ||
addErrorCat = false | |||
args[1] = trim( args[1] ) or false | args[1] = trim( args[1] ) or false | ||
if args[1] then | local basique = require( 'Module:Yesno' )( args.basique ) | ||
if args[1] and not basique then | |||
if args[1]:match('[^%d,. -]') then | if args[1]:match('[^%d,. -]') then | ||
local tempArgs = p.parseUnit( args[1] ) | local tempArgs = p.parseUnit( args[1] ) | ||
Ligne 651 : | Ligne 821 : | ||
end | end | ||
args[2] = trim( args[2] ) or false | args[2] = trim( args[2] ) or false | ||
if args[2] and not args[3] | if args[2] and not args[3] then | ||
-- cas ou le paramètre 2 contient 'm3' ou 'km2' | |||
local a, d = args[2]:match('^(%a%a?)(%d)$') | |||
if a and Data.unit[a] then | |||
args[2] = a | |||
args[3] = d | |||
end | end | ||
for k, v in pairs( tempArgs ) do | -- cas ou le paramètre 2 contient 'km/s' ou 'm3/h' | ||
if 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 | end | ||
end | end | ||
Ligne 674 : | Ligne 853 : | ||
end | end | ||
local cat = '' | local cat = '' | ||
if addErrorCat then | if addErrorCat and mw.title.getCurrentTitle():inNamespaces( 0, 4, 8, 10, 12, 14, 100, 828 ) then | ||
cat = errorCat | cat = errorCat | ||
mw.log( errorCat ,' → ', args[1], '|', args[2] ) | |||
end | end | ||
return p._unite( args ) .. cat | return ( p._unite( args ) or '' ) .. cat | ||
end | end | ||
end | |||
function p.emulationFormatnum( frame ) | |||
local args = frame:getParent().args | |||
return p.formatNombres( args[1] ) | |||
end | end | ||
return p | return p |
Dernière version du 30 juillet 2023 à 16:18
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'] = 'ₜ',
}
local fractionUnicode = { ['½'] = '1/2', ['⅓'] = '1/3', ['⅕'] = '1/5', ['⅙'] = '1/6', ['⅛'] = '1/8',
['⅔'] = '2/3', ['⅖'] = '2/5', ['⅚'] = '5/6', ['⅜'] = '3/8', ['¾'] = '3/4', ['⅗'] = '3/5',
['⅝'] = '5/8', ['⅞'] = '7/8', ['⅘'] = '4/5', ['¼'] = '1/4', ['⅐'] = '1/7', ['⅑'] = '1/9', ['⅒'] = '1/10', ['↉'] = '0/3',
}
local nbsp = '\194\160' -- espace insécable
local nnbsp = '\226\128\175' -- espace fine insécable
--- Copie de Outils.trim acceptant les nombres.
local function trim( texte )
if type( texte ) == 'string' then
-- http://lua-users.org/wiki/StringTrim
texte = texte:match( '^()%s*$' ) and '' or texte:match( '^%s*(.*%S)' )
if texte ~= '' then
return texte
end
elseif type( texte ) == 'number' then
return tostring( texte )
end
end
-- retire les chiffres des strip markers
local function escapeStripMarkers( input )
return input:gsub( '(UNIQ%-%-%a+%-)(%x%x%x%x%x%x%x%x)(%-QINU)', function ( leading, hexdigits, trailing )
local escapeddigits = hexdigits:gsub( '%d', {
['0'] = 'g', ['1'] = 'h', ['2'] = 'i', ['3'] = 'j', ['4'] = 'k',
['5'] = 'l', ['6'] = 'm', ['7'] = 'n', ['8'] = 'o', ['9'] = 'p',
} )
return leading .. escapeddigits .. trailing
end )
end
-- restaure les strip markers
local function restoreStripMarkers( input )
return input:gsub( '(UNIQ%-%-%a+%-)(%a%a%a%a%a%a%a%a)(%-QINU)', function ( leading, escapeddigits, trailing )
local hexdigits = escapeddigits:gsub( '%a', {
['g'] = '0', ['h'] = '1', ['i'] = '2', ['j'] = '3', ['k'] = '4',
['l'] = '5', ['m'] = '6', ['n'] = '7', ['o'] = '8', ['p'] = '9',
} )
return leading .. hexdigits .. trailing
end )
end
-- remplacement de certains caractères, pour simplifier les pattern
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 par un tiret
:gsub( '%−%f[%d]', '-') -- U+2212
:gsub( '−%f[%d]', '-') -- html −
-- remplacement des espaces insécable par des espace simple
:gsub( nbsp, ' ' )
:gsub( ' ', ' ' )
:gsub( ' ', ' ' )
:gsub( nnbsp, ' ' )
:gsub( ' ', ' ' )
:gsub( ' ', ' ' )
:gsub( '\226\128[\132-\138]', ' ' ) -- U+2004 à U+200A
:gsub( ' ', ' ' )
-- 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 ''
end
-- si nombre est un chiffre en exposant ou indice comme ², retourne ce chiffre
for i = 0, 9 do
local is = tostring(i)
if result == supUnicode[ is ] or result == subUnicode[ is ] then
return is
end
end
if not result:match( '^%-?[%d., ]*%d$' ) and not result:match( '^%-?[%d., ]*%d ?e[+-]?%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
if num == '1' then
return '10' .. exponent
end
exponent = nbsp .. '×' .. nnbsp .. '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, deci = 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)', nbsp .. '%1' )
end
if deci ~= '' or ( decimals and decimals > 0 ) then
if decimals and decimals > #deci then
deci = deci .. string.rep( '0', decimals - #deci )
end
if #deci > 3 then
deci = ',' .. deci:gsub( '(%d%d%d)', '%1' .. nbsp ):gsub( nbsp .. '$', '' )
else
deci = ',' .. deci
end
end
return moins .. entier .. deci .. 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 = round, decimals = decimals }
elseif type( nombres ) == 'string' then
-- retire les chiffres des strip markers
nombres = escapeStripMarkers( nombres )
-- formatage proprement dit
nombres = p.sanitizeNum( nombres )
local formatN = function ( n )
return p.formatNombre( n, round, decimals )
end
if nombres:match('%d%-%d') then
nombres = nombres:gsub( '%f[%d.,][%d., ]*%d', formatN )
else
nombres = nombres
:gsub( '%-?%f[%d.,][%d., ]*%d ?e[+-]?%d+', formatN )
:gsub( '%-?%f[%d.,][%d., ]*%d', formatN )
end
-- restaure les strip markers
nombres = restoreStripMarkers( nombres )
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, cadratin et tiret
['±'] = '±', ['+-'] = '±', ['+/-'] = '±',
['+'] = '+',
['−'] = '−', -- signe moins
['×'] = '×', x = '×', ['*'] = '×',
['××'] = '××', xx = '××', ['**'] = '××',
}
-- valeur numérique
local cap0, capture = toParse:match( '^(([%d., ]+%f[^d%(])%s*)' )
local prefix
if not cap0 then
-- cas d'un nombre entre guillemet, gras, italique...
cap0, capture = toParse:match( '^((["\']+[%d., ]+["\']+)%s*)' )
end
if not cap0 then
-- cas ou le nombre est remplcé par un ou plusieurs points d'interrogation
cap0, prefix = toParse:match( '^((%?+)%s*)' )
end
if not cap0 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)
cap0, prefix, capture = toParse:match( '^(([%a ]+[.,]?[: ]* )([+-]? ?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
end
if not cap0 then
-- cas ou le nombre est précédé par un signe, un symbole ASCII, ou suivit d'une incerititude entre parenthèse
cap0, prefix, capture = toParse:match( '^(([(<>=~ ]*)([+-]? ?%f[%d.,][%d., ]*%d%(?[%d%.]*%)?)%s*)' )
end
if not cap0 then
-- cas ou le nombre est précédé par un symbole ≤, ≥, ≈, ≃ et quelque autres
cap0, prefix, capture = toParse:match( '^((\226[\136\137][\131\136\164\165\187\188] ?)([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
end
if not cap0 then
-- cas ou le nombre est précédé par un symbole ± (\194\177)
cap0, prefix, capture = toParse:match( '^((±) ?(%f[%d.,][%d., ]*%d%f[%D])%s*)' )
end
result = { capture or false, prefix = prefix }
if cap0 then
toParse = toParse:sub( cap0:len() + 1 )
-- point de suspensions (ex π = 3.14159...)
cap0 = toParse:match( '^…%s*' )
if not cap0 then
cap0 = toParse:match( '^%.%.%.%s*' )
end
if cap0 then
result[1] = result[1] .. '…'
toParse = toParse:sub( cap0:len() + 1 )
end
if toParse == '' then
return result
end
end
-- fraction
capture = mw.ustring.sub( toParse, 1, 1 )
if fractionUnicode[ capture ] then
result.fraction = fractionUnicode[ capture ]
toParse = toParse:sub( capture:len() + 1 ):gsub( '^%s*', '' )
result[1] = result[1] or ''
else
cap0, capture = toParse:match( '^(([%d,]*/%f[%d][%d ]*%d)%s*)' )
if not cap0 then
-- caractère de fraction ⁄ = \226\129\132
cap0, capture = toParse:match( '^((%d*⁄%d+)%s*)' )
if cap0 then
capture = capture:gsub( '⁄', '/' )
end
end
if cap0 then
if result[1] and 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( cap0:len() + 1 )
end
end
if toParse~= '' and ( result[1] or result.fraction ) then
-- lien avec un deuxième nombre
local cap0, conj, num = toParse:match( '^(([etou+/;x*-]+) *(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' )
if not cap0 and toParse:byte() > 127 then
cap0, conj, num = mw.ustring.match( toParse, '^(([à−×±—–]+) *(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' )
end
if cap0 and specificArgs[ conj ]
and not (
specificArgs[ conj ] == '×'
and (
mw.ustring.match( toParse, '^[×x] ?10 ?e' )
or mw.ustring.match( toParse, '^[×x] ?10<sup>(%-?%d+)</sup>' )
)
)
then
result[ specificArgs[ conj ] ] = num
toParse = toParse:sub( cap0:len() + 1 )
end
if result['+'] or result['×'] or result['/'] then
cap0, conj, num = mw.ustring.match( toParse, '^(([/;x*×−-]) *(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
if cap0 then
if specificArgs[ conj ] == '×' then
result['××'] = num
elseif specificArgs[ conj ] == '/' then
result['//'] = num
else
result['−'] = num
end
toParse = toParse:sub( cap0:len() + 1 )
end
end
end
-- 10 exposant ( \195\151 = ×, signe multiplié)
cap0, capture = toParse:match( '^(e(%-?%d+)%s*)' )
if not cap0 then
cap0, capture = toParse:match( '^([x\195]\151? ?10e(%-?%d+)%s*)' )
end
if not cap0 then
cap0, capture = toParse:match( '^([x\195]\151? ?10<sup>(%-?%d+)</sup>%s*)' )
end
if cap0 then
result.e = capture
toParse = toParse:sub( cap0:len() + 1 )
end
if result[1] == '10' and not result.e and not result.fraction then
cap0, capture = toParse:match( '^(<sup>(%-?%d+)</sup>%s*)' )
if cap0 then
result[1] = false
result.e = capture
toParse = toParse:sub( cap0:len() + 1 )
end
end
if toParse == '' then
return result
end
-- unités
local texteUnit = toParse
toParse = toParse:gsub( '^([^%[<]-)<sup>(%d)</sup>', '%1%2' )
if Data.unit[ toParse ]
or toParse:match( '%b<>' )
or toParse:match( 'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU' )
or mw.ustring.match( toParse, '^%a+$' )
then
result[ #result + 1] = toParse
toParse = ''
elseif toParse:match( '%b<>' ) then
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
end
if toParse ~= '' then
local unit, exp
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
repeat
-- unité contenant un lien
cap0, unit, exp = mw.ustring.match( toParse, '^((/? ?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' )
if not cap0 then
-- unité ne contenant pas de lien
cap0, unit, exp = mw.ustring.match( toParse, '^((/? ?[^%s%d/]+) ?(%-?%d*)%s*)' )
end
if not cap0 then
-- l/100 km
cap0, unit, exp = mw.ustring.match( toParse, '^((/100 ?[^%s%d/]+) ?(%-?%d*)%s*)' )
end
if cap0 then
if unit:match( '%-$' ) and exp ~= '' then -- rustine pour quand le "-" se retrouve dans la capture "unit" au lieu de la capture "exp"
unit = unit:gsub( '%-$', '' )
exp = '-' .. exp
elseif exp == '-' then -- rustine pour quand un "-" a été capturé dans "exp" mais sans qu'il y ait de chiffres après
unit = cap0
exp = ''
end
if Data.unit[ unit ] or mw.ustring.match( unit, '[%a€£$¥«»]' ) then
result[ #result + 1] = unit
result[ #result + 1] = exp
toParse = toParse:sub( cap0:len() + 1 )
else
break
end
end
until toParse == '' or not cap0
end
if toParse == '' then
if #result > 3 then
local estSimpleTexte = true
for r = 2, #result, 2 do
if Data.unit[ result[ r ] ]
or result[ r ]:sub( 1, 1 ) == '/'
or Data.prefix[ result[ r ]:sub( 1, 1 ) ] and Data.unit[ result[ r ]:sub( 2 ) ]
or Data.prefix[ result[ r ]:sub( 1, 2 ) ] and Data.unit[ result[ r ]:sub( 3 ) ]
or result[ r + 1 ] and result[ r + 1 ] ~= ''
then
estSimpleTexte = false
break
end
end
if estSimpleTexte then
result[ 2 ] = texteUnit
for r = #result, 3, -1 do
result[ r ] = nil
end
end
end
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
---
-- nomUnit 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 false.
function p.nomUnit( unit, exposant )
unit = trim( unit )
if not dataSuccess or type( unit ) ~= 'string' then
return 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
-- /100
local divisor = ''
if unit:sub( 1, 2 ) == '10' then
divisor, unit = unit:match( '^(1[0 ]*)(.+)$' )
local divisorName = {
['10'] = 'dix ',
['100'] = 'cent ',
['1000'] = 'mille ',
['10000'] = 'dix-mille ',
['100000'] = 'cent-mille ',
['1000000'] = 'un million de ',
['1000000000'] = 'un millard de ',
}
divisor = divisorName[ divisor:gsub( ' ', '' ) ]
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 divisor .. unitPrefix.nom .. unitTab.nom .. exposant
elseif unit:match( '[/%d]' ) then
-- ce n'est pas du texte simple, on anule l'infobule
return false
else
return unit .. exposant
end
end
function p._unite( args )
-- formatage du nombre
local nombre = p.formatNombres( args[1], args.arrondi, args['décimales'] )
if nombre == '' then
nombre = nil
end
local wiki = {}
-- prefix est un paramètre interne défini par p.parseUnit, utile notamment lorsque {{unité}} est utilisé dans les infobox
if args.prefix then
wiki[ #wiki + 1 ] = args.prefix
end
if nombre then
wiki[ #wiki + 1 ] = nombre
end
-- fraction
local fraction = args.fraction
if fraction then
fraction = fractionUnicode[ fraction ] or fraction
local nom, den = fraction:match( '^(.-)/(.+)$' )
if nom then
if nom:match( '^[%dn]%d?$' ) 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;">'
.. p.formatNombres( nom )
.. '</sup>'
den = '<sub style="font-size: 70%; vertical-align: 0em;">'
.. p.formatNombres( den )
.. '</sub>'
end
fraction = nom .. '⁄' .. den
end
if nombre then
wiki[ #wiki + 1 ] = nbsp
end
wiki[ #wiki + 1 ] = fraction
end
-- à, et, ou, ×, – (tiret cadratin)
local specificArgs = { '–', 'à', 'et', 'ou', '/', '//', '×', '××', '±' }
for i = 1, #specificArgs do
local name = specificArgs[ i ]
local v = args[ name ] and trim( args[ name ] )
if v then
v = p.formatNombres( v )
if name == '//' then
name = '/'
elseif name == '××' then
name = '×'
end
if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then
-- pas d'espace pour le tiret cadratin entre deux nombres positifs
wiki[ #wiki + 1 ] = '–'
elseif name == '×' or name == '±' then
wiki[ #wiki + 1 ] = nbsp .. name .. nbsp
else
wiki[ #wiki + 1 ] = nbsp .. name .. ' '
end
wiki[ #wiki + 1 ] = 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
local suffix = unit:sub( -2 ) -- yes, it's 2 bytes
if suffix == '²' then
exp = '2'
unit = unit:sub( 1, -3 )
elseif suffix == '³' 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 unit:sub( 1, 1 ) == '/' then
sep = '/'
unit = trim( unit:sub( 2 ) ) or ''
if not par then
par = true
if unit:sub( 1, 2 ) == '10' then
nomUnits[ #nomUnits + 1 ] = 'pour'
else
nomUnits[ #nomUnits + 1 ] = 'par'
end
else
nomUnits[ #nomUnits + 1 ] = 'et par'
if nomUnits[ #nomUnits - 2 ] == 'et par' then
nomUnits[ #nomUnits - 2 ] = 'par'
end
end
elseif units ~= '' then
sep = nbsp
end
if exp:match( '^-' ) and not par then
par = true
nomUnits[ #nomUnits + 1 ] = '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
local nomUnit = p.nomUnit( unit, exp )
if nomUnit then
nomUnits[ #nomUnits + 1 ] = nomUnit
else
-- si le code de l'unité n'est pas reconnu, insère false en première position de la table.
table.insert( nomUnits, 1, false )
end
i = i + 1
unit = trim( args[ 2 * i ] )
end
local unitFullName = nomUnits[1] and table.concat( nomUnits, ' ' ) or false
-- conversion
if unitFullName then
local nameSingular = mw.ustring.gsub( unitFullName, '(%a)s%f[%A]', '%1' )
local multiple = 1
local convertTable = Data.convert[ nameSingular ]
if not convertTable and #nameSingular > 5 then
-- gesion des multiples (Kilo, méga, mili...)
local prefix = Data.prefix[ nameSingular:sub( 1, 4 ) ] or Data.prefix[ nameSingular:sub( 1, 5 ) ]
local _, par = nameSingular:find( ' par ' )
local prefix2
if par then
prefix2 = Data.prefix[ nameSingular:sub( par + 1, 4 ) ] or Data.prefix[ nameSingular:sub( par + 1, 5 ) ]
end
if prefix and Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ) ] then
convertTable = Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ) ]
multiple = 10 ^ prefix.puissance
elseif prefix2 and Data.convert[ nameSingular:gsub( ' par ' .. prefix2.nom, '' ) ] then
convertTable = Data.convert[ nameSingular:gsub( ' par ' .. prefix2.nom, '' ) ]
multiple = 1 / 10 ^ prefix2.puissance
elseif prefix and prefix2 and Data.convert[ nameSingular:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] then
convertTable = Data.convert[ nameSingular: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 = 1, #wiki do
local v = wiki[ i ]
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 c[1]:sub( 1, 2 ) == '°' then -- yes, it's 2 bytes
sep = ''
end
converted[ #converted + 1 ] = nConverted .. sep.. c[1]
end
wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>'
end
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
wiki[ #wiki + 1 ] = '<span class="nowrap"><span style="display:inline-block; padding-left:0.2em; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">'
wiki[ #wiki + 1 ] = 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, '(' )
wiki[ #wiki + 1 ] = ')'
end
wiki[ #wiki + 1 ] = nbsp .. '×' .. nnbsp .. '10<sup>' .. exposant .. '</sup>'
else
wiki[ #wiki + 1 ] = '10<sup>' .. exposant .. '</sup>'
end
end
if units ~= '' then
local sep = nbsp
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 considérant les espaces qui peuvent être devenus insécables)
if unitFullName and unitFullName ~= units:gsub( nbsp, ' ' ) then
units = string.format( '<abbr class="abbr" title="%s">%s</abbr>', unitFullName, units )
end
wiki[ #wiki + 1 ] = 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
addErrorCat = false
args[1] = trim( args[1] ) or false
local basique = require( 'Module:Yesno' )( args.basique )
if args[1] and not basique 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] then
-- cas ou le paramètre 2 contient 'm3' ou 'km2'
local a, d = args[2]:match('^(%a%a?)(%d)$')
if a and Data.unit[a] then
args[2] = a
args[3] = d
end
-- cas ou le paramètre 2 contient 'km/s' ou 'm3/h'
if 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
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 and mw.title.getCurrentTitle():inNamespaces( 0, 4, 8, 10, 12, 14, 100, 828 ) then
cat = errorCat
mw.log( errorCat ,' → ', args[1], '|', args[2] )
end
return ( p._unite( args ) or '' ) .. cat
end
end
function p.emulationFormatnum( frame )
local args = frame:getParent().args
return p.formatNombres( args[1] )
end
return p