Moduł:statystyka

Z Wikisłownika – wolnego słownika wielojęzycznego
[edytuj] [odśwież] Dokumentacja modułu

Użycie

Moduł obsługujący rankingi na stronie Wikisłownik:Statystyka oraz szablony liczników. Dane pochodzą ze zrzutów bazy danych ukazujących się dwa razy w miesiącu, analizowanych przez bota i kompilowanych w formacie JSON dla wykorzystania przez niniejszy moduł. Zestawienie uwzględnia wyłącznie wprowadzone języki (tj. z indeksem) z niezerową liczbą haseł w momencie wykonania zrzutu, natomiast zliczane hasła powinny być dostosowane do szablonu.

Schemat JSON

Schemat danych JSON:

  • currentDate, previousDate – odpowiednio data obecnego i poprzedniego zrzutu w formacie yyyyMMdd
  • overall – statystyki ogólne dla wszystkich języków
  • overallDiff – statystyki ogólne, porównanie względem poprzedniego zrzutu
  • languages – statystyki dla poszczególnych języków, kluczem jest nazwa krótka języka

Bot zbiera następujące informacje (dla każdego języka oraz w podsumowaniu ogólnym):

  • entries – całkowita liczba haseł
  • canonical – liczba haseł zawierających definicje
  • nonCanonical – liczba haseł zawierających formy fleksyjne
  • definitions – liczba definicji
  • withFiles – liczba haseł zawierających co najmniej jedną ilustrację pod nagłówkiem sekcji
  • withAudio – liczba haseł zawierających co najmniej jedno nagranie wymowy w polu „wymowa”
  • withReferences – liczba haseł zawierających co najmniej jeden przypis w formie <ref> gdziekolwiek w haśle oraz <references> w polu „źródła”
  • combinedLength – łączna suma długości haseł w znakach (zob. objaśnienie w WS:STAT#Długość haseł)

Jeżeli wykryje różnice względem poprzedniego zrzutu, odzwierciedla je w odpowiednich właściwościach o takiej samej nazwie z dodaniem przyrostka „Diff”. Przykładowo definitionsDiff ukazuje liczbę znaczeń dodanych lub usuniętych od poprzedniego zrzutu. Jeżeli ta liczba jest równa zeru, właściwość zostaje pominięta.

Opis funkcji

Sposób wywołania: {{#invoke:statystyka|<funkcja>|<parametry>}}.

mainRanking

Generuje tabelę z zestawieniem języków z największą liczbą haseł (WS:STAT#Języki). Dodatkowe kolumny: liczba znaczeń.

Parametry:

  • 1 (opcjonalny): liczba języków do wyświetlenia; jeżeli jest pusty, wyświetla wszystkie języki oraz podsumowanie w stopce tabeli

Przykłady:

  • {{#invoke:statystyka|mainRanking|50}} – tabela zestawiająca 50 języków z największą liczbą haseł
  • {{#invoke:statystyka|mainRanking}} – tabela zestawiająca wszystkie języki w projekce z podsumowaniem

combinedLengthRanking

Generuje tabelę z zestawieniem języków z największą sumą długości haseł (WS:STAT#Długość haseł). Dodatkowe kolumny: różnica sumy długości względem poprzedniego zrzutu, średnia długość hasła, różnica średniej długości hasła, liczba haseł.

Parametry:

  • 1 (opcjonalny): liczba języków do wyświetlenia; jeżeli jest pusty, wyświetla wszystkie języki oraz podsumowanie w stopce tabeli
  • 2 (opcjonalny): główny klucz sortowania (zob. #Schemat JSON); jeżeli dodano przyrostek Rate, kluczem będzie wynik dzielenia wskazanej właściwości (bez przyrostka) przez liczbę haseł; domyślnie suma długości haseł
  • 3 (opcjonalny): filtr minimalnej liczby haseł; domyślnie zero

Przykłady:

  • {{#invoke:statystyka|combinedLengthRanking}} – tabela zestawiająca wszystkie języki w projekcie z podsumowaniem
  • {{#invoke:statystyka|combinedLengthRanking|50}} – tabela zestawiająca 50 języków z największą sumą długości haseł
  • {{#invoke:statystyka|combinedLengthRanking|50|definitions}} – tabela zestawiająca 50 języków z największą sumą długości haseł, posortowana wg liczby znaczeń w kolejności malejącej
  • {{#invoke:statystyka|combinedLengthRanking|50|combinedLengthRate}} – tabela zestawiająca 50 języków z największą sumą długości haseł, posortowana wg średniej długości haseł w kolejności malejącej
  • {{#invoke:statystyka|combinedLengthRanking|50|combinedLengthRate|500}} – tabela zestawiająca 50 języków z największą sumą długości haseł, posortowana wg średniej długości haseł w kolejności malejącej, z ograniczeniem do języków liczących 500 haseł lub więcej

definitionsRanking

Generuje tabelę z zestawieniem języków z największą liczbą znaczeń (WS:STAT#Znaczenia). Dodatkowe kolumny: różnica liczby znaczeń względem poprzedniego zrzutu, średnia znaczeń na hasło, różnica średniej znaczeń, liczba haseł.

Parametry:

  • 1 (opcjonalny): liczba języków do wyświetlenia; jeżeli jest pusty, wyświetla wszystkie języki oraz podsumowanie w stopce tabeli
  • 2 (opcjonalny): główny klucz sortowania (zob. #Schemat JSON); jeżeli dodano przyrostek Rate, kluczem będzie wynik dzielenia wskazanej właściwości (bez przyrostka) przez liczbę haseł; domyślnie liczba znaczeń
  • 3 (opcjonalny): filtr minimalnej liczby haseł; domyślnie zero

Przykłady:

  • {{#invoke:statystyka|definitionsRanking}} – tabela zestawiająca wszystkie języki w projekcie z podsumowaniem
  • {{#invoke:statystyka|definitionsRanking|50}} – tabela zestawiająca 50 języków z największą liczbą znaczeń
  • {{#invoke:statystyka|definitionsRanking|50|combinedLength}} – tabela zestawiająca 50 języków z największą liczbą znaczeń, posortowana wg sumy długości haseł w kolejności malejącej
  • {{#invoke:statystyka|definitionsRanking|50|definitionsRate}} – tabela zestawiająca 50 języków z największą liczbą znaczeń, posortowana wg średniej liczby znaczeń na hasło w kolejności malejącej
  • {{#invoke:statystyka|definitionsRanking|50|definitionsRate|500}} – tabela zestawiająca 50 języków z największą liczbą znaczeń, posortowana wg średniej liczby znaczeń na hasło w kolejności malejącej, z ograniczeniem do języków liczących 500 haseł lub więcej

miscellaneaRanking

Generuje tabelę z zestawieniem haseł z ilustracjami, z nagraniem wymowy oraz ze źródłem dla języków z największą liczbą haseł (WS:STAT#Multimedia i źródła). Dodatkowe kolumny: stosunek haseł ze wskazaną właściwością do całkowitej liczby haseł, różnica względem poprzedniego zrzutu.

Parametry:

  • 1 (opcjonalny): liczba języków do wyświetlenia; jeżeli jest pusty, wyświetla wszystkie języki oraz podsumowanie w stopce tabeli
  • 2 (opcjonalny): główny klucz sortowania (zob. #Schemat JSON); jeżeli dodano przyrostek Rate, kluczem będzie wynik dzielenia wskazanej właściwości (bez przyrostka) przez liczbę haseł; domyślnie liczba haseł
  • 3 (opcjonalny): filtr minimalnej liczby haseł; domyślnie zero

Przykłady:

  • {{#invoke:statystyka|miscellaneaRanking}} – tabela zestawiająca wszystkie języki w projekcie z podsumowaniem
  • {{#invoke:statystyka|miscellaneaRanking|50}} – tabela zestawiająca 50 języków z największą liczbą haseł
  • {{#invoke:statystyka|miscellaneaRanking|50|definitions}} – tabela zestawiająca 50 języków z największą liczbą haseł, posortowana wg średniej liczby znaczeń w kolejności malejącej
  • {{#invoke:statystyka|miscellaneaRanking|50|definitionsRate}} – tabela zestawiająca 50 języków z największą liczbą haseł, posortowana wg średniej liczby znaczeń w kolejności malejącej
  • {{#invoke:statystyka|miscellaneaRanking|50|definitionsRate|500}} – tabela zestawiająca 50 języków z największą liczbą haseł, posortowana wg liczby znaczeń w kolejności malejącej, z ograniczeniem do języków liczących 500 haseł lub więcej

languageIndex

Generuje indeks języków na stronie głównej.

Parametry:

  • 1 (wymagany): lista najniższych niepodzielnych progów w kolejności rosnącej, oddzielonych przecinkiem; ostatnia wartość wyznacza wielokrotność kolejnych progów

Przykłady:

  • {{#invoke:statystyka|languageIndex|10000}} – grupuje języki w odstępach co 10 000 haseł
  • {{#invoke:statystyka|languageIndex|1000,2000,5000}} – generuje grupę języków z tysiącem haseł lub więcej, z dwoma tysiącami, z pięcioma tysiącami, a wyżej z wielokrotnością 5000 (ponad 10 000 haseł, ponad 15 000, ponad 20 000 itd.)

queryStorage

Wyłuskuje wartość pożądanej właściwości z tablicy danych JSON.

Parametry: zobacz #Schemat JSON.

Przykłady:

  • {{#invoke:statystyka|queryStorage|currentDate}} – data ostatniego zrzutu
  • {{#invoke:statystyka|queryStorage|overall|withReferences}} – suma wszystkich haseł z przypisami w projekcie
  • {{#invoke:statystyka|queryStorage|languages|hiszpański|withReferences}} – licznik hiszpańskich haseł z przypisami

canonicalCounter

Licznik haseł z definicją. Zwraca rozmiar kategorii [1] (indeks), jeżeli nie znaleziono wskazanego języka w tablicy danych JSON.

Parametry:

  • 1 (opcjonalny): krótka nazwa języka; jeżeli jest pusty, zwraca sumę liczników haseł dla wszystkich języków

Przykłady:

  • {{#invoke:statystyka|canonicalCounter|hiszpański}} – licznik hiszpańskich haseł z definicją
  • {{#invoke:statystyka|canonicalCounter}} – suma wszystkich haseł z definicją w projekcie

flexiveFormCounter

Licznik haseł bez definicji (jedyne znaczenia to formy fleksyjne). Zwraca rozmiar kategorii [1] (formy fleksyjne), jeżeli nie znaleziono wskazanego języka w tablicy danych JSON.

Parametry:

  • 1 (opcjonalny): krótka nazwa języka; jeżeli jest pusty, zwraca sumę liczników haseł dla wszystkich języków

Przykłady:

  • {{#invoke:statystyka|flexiveFormCounter|hiszpański}} – licznik hiszpańskich haseł bez definicji
  • {{#invoke:statystyka|flexiveFormCounter}} – suma wszystkich haseł bez definicji w projekcie

definitionCounter

Licznik znaczeń. Zwraca 0, jeżeli nie znaleziono wskazanego języka w tablicy danych JSON.

Parametry:

  • 1 (opcjonalny): krótka nazwa języka; jeżeli jest pusty, zwraca sumę liczników definicji dla wszystkich języków

Przykłady:

  • {{#invoke:statystyka|definitionCounter|hiszpański}} – licznik znaczeń w hiszpańskich hasłach
  • {{#invoke:statystyka|definitionCounter}} – suma znaczeń we wszystkich hasłach w projekcie

Zobacz też

local data = mw.loadJsonData( 'Moduł:statystyka/dane.json' )

local shortLangs = mw.loadJsonData( 'MediaWiki:Gadget-langdata-short-langs.json' )
local shortLangKeys = {}

for i, lang in ipairs( shortLangs ) do
	shortLangKeys[ lang ] = true
end

local language = mw.language.new( 'pl' )

local polishCollation = {
	[ 'ą' ] = 'a',
	[ 'ć' ] = 'c',
	[ 'ę' ] = 'e',
	[ 'ł' ] = 'l',
	[ 'ń' ] = 'n',
	[ 'ó' ] = 'o',
	[ 'ś' ] = 's',
	[ 'ź' ] = 'z',
	[ 'ż' ] = 'z'
}

function makeSortKey( str )
	local out = ''
	
	for i = 1, mw.ustring.len( str ) do
		local character = mw.ustring.sub( str, i, i )
		out = out .. ( polishCollation[ character ] or '' ) .. character
	end
	
	return out
end

function makeDiff( value, fmt )
	local out = tonumber( string.format( fmt or '%s', value ) )
	
	if out == 0 then
		return ''
	end
	
	out = language:formatNum( out )
	
	if value > 0 then
		return '+' .. out
	else
		return out
	end
end

function addStyles( frame )
	return frame:extensionTag( 'templatestyles', '', { src = 'Wikisłownik:Statystyka/styles.css' } )
end

local p = {}

p.mainRanking = function ( frame )
	local limit = frame.args[ 1 ]
	
	local header = mw.html.create( 'tr' )
		:tag( 'th' ):wikitext( 'język' ):done()
		:tag( 'th' ):addClass( 'unsortable' ):css( 'border-right-style', 'hidden' ):done()
		:tag( 'th' ):addClass( 'unsortable' ):css( 'width', '50%' )
			:wikitext( 'licznik&nbsp;haseł' ):done()
		:tag( 'th' ):css( 'border-left-style' , 'hidden' ):done()
		:tag( 'th' ):wikitext( 'licznik&nbsp;znaczeń' ):done()
	
	local rows = {}
	
	for lang, stats in pairs( data.languages ) do
		local row = {
			key = stats.canonical,
			key2 = stats.definitions,
			key3 = makeSortKey( lang ),
			category = mw.html.create( 'td' ),
			percentage = mw.html.create( 'td' ),
			bar = mw.html.create( 'td' ),
			canonicalCount = mw.html.create( 'td' ),
			definitionsCount = mw.html.create( 'td' ),
		}
		
		local fullName = shortLangKeys[ lang ] and lang or ( 'język ' .. lang )
		
		row.category:wikitext(
			'[[:Kategoria:' .. lang .. ' (indeks)|' .. lang .. ']] ' ..
			'([[:Kategoria:' .. language:ucfirst( fullName ) .. '|⌘]])'
		)
		
		local percentage = 100 * stats.canonical / data.overall.canonical
		
		row.percentage:tag( 'small' ):wikitext( string.format( '%.3f', percentage ) .. '%' )
		row.canonicalCount:wikitext( language:formatNum( stats.canonical ) )
		row.definitionsCount:wikitext( language:formatNum( stats.definitions ) )
		
		table.insert( rows, row )
	end
	
	local rowSorter = function ( a, b )
		if a.key == b.key then
			if a.key2 == b.key2 then
				return a.key3 < b.key3
			else
				return a.key2 > b.key2
			end
		else
			return a.key > b.key
		end	
	end
	
	table.sort( rows, rowSorter )
	
	local contents = mw.html.create( '' )
	
	if limit then
		limit = math.min( limit, #rows )
	else
		limit = #rows
	end
	
	for i = 1, limit do
		local row = rows[ i ]
		local percent = 100 * row.key / rows[ 1 ].key
		
		contents:tag( 'tr' )
			:node( row.category )
			:node( row.percentage )
			:node( row.bar:tag( 'div' )
				:addClass( 'progress-bar' ):css( 'width', percent .. '%' ):done()
			)
			:node( row.canonicalCount )
			:node( row.definitionsCount )
	end
	
	local html = mw.html.create( 'table' )
		:addClass( 'wikitable sortable autonumber ws-stats' )
		:node( header )
		:node( contents )
	
	if limit == #rows then
		local footer = mw.html.create( 'tr' ):addClass( 'sortbottom' )
			:tag( 'td' ):done()
			:tag( 'td' ):tag( 'small' )
				:wikitext( '100%' )
				:done():done()
			:tag( 'td' ):done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.canonical ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.definitions ) )
				:done():done()
		
		html:node( footer )
	end
	
	return addStyles( frame ) .. tostring( html )
end

p.combinedLengthRanking = function ( frame )
	local limit = frame.args[ 1 ]
	local mainSortKey = frame.args[ 2 ] or 'combinedLength'
	local threshold = tonumber( frame.args[ 3 ] ) or 0
	local subtable = {}
	local sortByRate = mainSortKey:sub( -4 ) == 'Rate'
	
	if sortByRate then
		mainSortKey = mainSortKey:sub( 1, -5 )
	end
	
	if not data.overall[ mainSortKey ] then
		error( 'niedostępna właściwość w kluczu sortowania: ' .. mainSortKey )
	end
	
	for language, stats in pairs( data.languages ) do
		if stats.canonical >= threshold then
			table.insert( subtable, {
				lang = language,
				key = sortByRate and stats[ mainSortKey ] / stats.canonical or stats[ mainSortKey ],
				key2 = makeSortKey( language )
			} )
		end
	end
	
	local entrySorter = function ( a, b )
		if a.key == b.key then
			return a.key2 < b.key2
		else
			return a.key > b.key
		end
	end
	
	table.sort( subtable, entrySorter )
	
	local header = mw.html.create( 'tr' )
		:tag( 'th' ):wikitext( 'język' ):done()
		:tag( 'th' ):wikitext( 'suma długości' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'średnia długość' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'hasła' ):done()
	
	local contents = mw.html.create( '' )
	
	if limit then
		limit = math.min( limit, #subtable )
	else
		limit = #subtable
	end
	
	for i = 1, limit do
		local lang = subtable[ i ].lang
		local stats = data.languages[ lang ]
		local mean = stats.combinedLength / stats.canonical
		local prevLength = stats.combinedLength - ( stats.combinedLengthDiff or 0 )
		local prevCanonical = stats.canonical - ( stats.canonicalDiff or 0 )
		local prevMean = prevLength / prevCanonical
		local fullName = shortLangKeys[ lang ] and lang or ( 'język ' .. lang )
		
		contents:tag( 'tr' )
			:tag( 'td' ):wikitext(
					'[[:Kategoria:' .. lang .. ' (indeks)|' .. lang .. ']] ' ..
					'([[:Kategoria:' .. language:ucfirst( fullName ) .. '|⌘]])'
				):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.combinedLength ) ):done()
			:tag( 'td' ):wikitext( makeDiff( stats.combinedLengthDiff or 0 ) ):done()
			:tag( 'td' ):wikitext(
					language:formatNum( tonumber( string.format( '%.1f', mean ) ) )
				):done()
			:tag( 'td' ):wikitext( makeDiff( mean - prevMean, '%.1f' ) ):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.canonical ) ):done()
	end
	
	local html = mw.html.create( 'table' )
		:addClass( 'wikitable sortable autonumber ws-stats' )
		:node( header )
		:node( contents )
	
	if limit == #subtable then
		local mean = data.overall.combinedLength / data.overall.canonical
		local prevLength = data.overall.combinedLength - ( data.overallDiff.combinedLength or 0 )
		local prevCanonical = data.overall.canonical - ( data.overallDiff.canonical or 0 )
		local prevMean = prevLength / prevCanonical
		
		local footer = mw.html.create( 'tr' ):addClass( 'sortbottom' )
			:tag( 'td' ):done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.combinedLength ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( data.overallDiff.combinedLength ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( tonumber( string.format( '%.1f', mean ) ) ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( mean - prevMean, '%.1f' ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.canonical ) )
				:done():done()
		
		html:node( footer )
	end
	
	return addStyles( frame ) .. tostring( html )
end

p.definitionsRanking = function ( frame )
	local limit = frame.args[ 1 ]
	local mainSortKey = frame.args[ 2 ] or 'definitions'
	local threshold = tonumber( frame.args[ 3 ] ) or 0
	local subtable = {}
	local sortByRate = mainSortKey:sub( -4 ) == 'Rate'
	
	if sortByRate then
		mainSortKey = mainSortKey:sub( 1, -5 )
	end
	
	if not data.overall[ mainSortKey ] then
		error( 'niedostępna właściwość w kluczu sortowania: ' .. mainSortKey )
	end
	
	for language, stats in pairs( data.languages ) do
		if stats.canonical >= threshold then
			table.insert( subtable, {
				lang = language,
				key = sortByRate and stats[ mainSortKey ] / stats.canonical or stats[ mainSortKey ],
				key2 = makeSortKey( language )
			} )
		end
	end
	
	local entrySorter = function ( a, b )
		if a.key == b.key then
			return a.key2 < b.key2
		else
			return a.key > b.key
		end
	end
	
	table.sort( subtable, entrySorter )
	
	local header = mw.html.create( 'tr' )
		:tag( 'th' ):wikitext( 'język' ):done()
		:tag( 'th' ):wikitext( 'znaczenia' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'średnia znaczeń' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'hasła' ):done()
	
	local contents = mw.html.create( '' )
	
	if limit then
		limit = math.min( limit, #subtable )
	else
		limit = #subtable
	end
	
	for i = 1, limit do
		local lang = subtable[ i ].lang
		local stats = data.languages[ lang ]
		local mean = stats.definitions / stats.canonical
		local prevDefinitions = stats.definitions - ( stats.definitionsDiff or 0 )
		local prevCanonical = stats.canonical - ( stats.canonicalDiff or 0 )
		local prevMean = prevDefinitions / prevCanonical
		local fullName = shortLangKeys[ lang ] and lang or ( 'język ' .. lang )
		
		contents:tag( 'tr' )
			:tag( 'td' ):wikitext(
					'[[:Kategoria:' .. lang .. ' (indeks)|' .. lang .. ']] ' ..
					'([[:Kategoria:' .. language:ucfirst( fullName ) .. '|⌘]])'
				):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.definitions ) ):done()
			:tag( 'td' ):wikitext( makeDiff( stats.definitionsDiff or 0 ) ):done()
			:tag( 'td' ):wikitext(
					language:formatNum( tonumber( string.format( '%.4f', mean ) ) )
				):done()
			:tag( 'td' ):wikitext( makeDiff( mean - prevMean, '%.4f' ) ):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.canonical ) ):done()
	end
	
	local html = mw.html.create( 'table' )
		:addClass( 'wikitable sortable autonumber ws-stats' )
		:node( header )
		:node( contents )
	
	if limit == #subtable then
		local mean = data.overall.definitions / data.overall.canonical
		local prevDefinitions = data.overall.definitions - ( data.overallDiff.definitions or 0 )
		local prevCanonical = data.overall.canonical - ( data.overallDiff.canonical or 0 )
		local prevMean = prevDefinitions / prevCanonical
		
		local footer = mw.html.create( 'tr' ):addClass( 'sortbottom' )
			:tag( 'td' ):done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.definitions ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( data.overallDiff.definitions ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( tonumber( string.format( '%.4f', mean ) ) ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( mean - prevMean, '%.4f' ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.canonical ) )
				:done():done()
		
		html:node( footer )
	end
	
	return addStyles( frame ) .. tostring( html )
end

p.miscellaneaRanking = function ( frame )
	local limit = frame.args[ 1 ]
	local mainSortKey = frame.args[ 2 ] or 'canonical'
	local threshold = tonumber( frame.args[ 3 ] ) or 0
	local subtable = {}
	local sortByRate = mainSortKey:sub( -4 ) == 'Rate'
	
	if sortByRate then
		mainSortKey = mainSortKey:sub( 1, -5 )
	end
	
	if not data.overall[ mainSortKey ] then
		error( 'niedostępna właściwość w kluczu sortowania: ' .. mainSortKey )
	end
	
	for language, stats in pairs( data.languages ) do
		if stats.canonical >= threshold then
			table.insert( subtable, {
				lang = language,
				key = sortByRate and stats[ mainSortKey ] / stats.canonical or stats[ mainSortKey ],
				key2 = makeSortKey( language )
			} )
		end
	end
	
	local entrySorter = function ( a, b )
		if a.key == b.key then
			return a.key2 < b.key2
		else
			return a.key > b.key
		end
	end
	
	table.sort( subtable, entrySorter )
	
	local header = mw.html.create( 'tr' )
		:tag( 'th' ):wikitext( 'język' ):done()
		:tag( 'th' ):wikitext( 'z ilustracją' ):done()
		:tag( 'th' ):wikitext( '%' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'z nagraniem' ):done()
		:tag( 'th' ):wikitext( '%' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'ze źródłem' ):done()
		:tag( 'th' ):wikitext( '%' ):done()
		:tag( 'th' ):wikitext( '+/-' ):done()
		:tag( 'th' ):wikitext( 'hasła' ):done()
	
	local contents = mw.html.create( '' )
	
	if limit then
		limit = math.min( limit, #subtable )
	else
		limit = #subtable
	end
	
	for i = 1, limit do
		local lang = subtable[ i ].lang
		local stats = data.languages[ lang ]
		local filesRatio = 100 * stats.withFiles / stats.canonical
		local audioRatio = 100 * stats.withAudio / stats.canonical
		local refsRatio = 100 * stats.withReferences / stats.canonical
		local fullName = shortLangKeys[ lang ] and lang or ( 'język ' .. lang )
		
		contents:tag( 'tr' )
			:tag( 'td' ):wikitext(
					'[[:Kategoria:' .. lang .. ' (indeks)|' .. lang .. ']] ' ..
					'([[:Kategoria:' .. language:ucfirst( fullName ) .. '|⌘]])'
				):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.withFiles ) ):done()
			:tag( 'td' ):wikitext(
					language:formatNum( tonumber( string.format( '%.1f', filesRatio ) ) ) .. '%'
				):done()
			:tag( 'td' ):wikitext( makeDiff( stats.withFilesDiff or 0 ) ):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.withAudio ) ):done()
			:tag( 'td' ):wikitext(
					language:formatNum( tonumber( string.format( '%.1f', audioRatio ) ) ) .. '%'
				):done()
			:tag( 'td' ):wikitext( makeDiff( stats.withAudioDiff or 0 ) ):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.withReferences ) ):done()
			:tag( 'td' ):wikitext(
					language:formatNum( tonumber( string.format( '%.1f', refsRatio ) ) ) .. '%'
				):done()
			:tag( 'td' ):wikitext( makeDiff( stats.withReferencesDiff or 0 ) ):done()
			:tag( 'td' ):wikitext( language:formatNum( stats.canonical ) ):done()
	end
	
	local html = mw.html.create( 'table' )
		:addClass( 'wikitable sortable autonumber ws-stats' )
		:node( header )
		:node( contents )
	
	if limit == #subtable then
		local filesRatio = 100 * data.overall.withFiles / data.overall.canonical
		local audioRatio = 100 * data.overall.withAudio / data.overall.canonical
		local refsRatio = 100 * data.overall.withReferences / data.overall.canonical
		
		local footer = mw.html.create( 'tr' ):addClass( 'sortbottom' )
			:tag( 'td' ):done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.withFiles ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( tonumber( string.format( '%.1f', filesRatio ) ) ) .. '%' )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( data.overallDiff.withFiles ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.withAudio ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( tonumber( string.format( '%.1f', audioRatio ) ) ) .. '%' )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( data.overallDiff.withAudio ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.withReferences ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( tonumber( string.format( '%.1f', refsRatio ) ) ) .. '%' )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( makeDiff( data.overallDiff.withReferences ) )
				:done():done()
			:tag( 'td' ):tag( 'b' )
				:wikitext( language:formatNum( data.overall.canonical ) )
				:done():done()
		
		html:node( footer )
	end
	
	return addStyles( frame ) .. tostring( html )
end

p.languageIndex = function( frame )
	local dictionaries = mw.loadJsonData( 'MediaWiki:Gadget-langdata-dictionaries.json' )
	
	local thresholdsStr = frame.args[ 1 ] or ''
	local thresholds = {}
	
	for value in mw.text.gsplit( thresholdsStr, ',', true ) do
		local parsed = tonumber( mw.text.trim( value ) )
		
		if not parsed then
			error( 'nieprawidłowa wartość: ' .. value )
		end
		
		table.insert( thresholds, parsed )
	end
	
	if #thresholds == 0 then
		error( 'wskaż granice jako pierwszy parametr, np. "500,1000,2000"' )
	end
	
	local makeThreshold = function ( n )
		if n >= thresholds[ #thresholds ] then
			local mult = math.floor( n / thresholds[ #thresholds ] )
			return thresholds[ #thresholds ] * mult
		end
		
		for i = #thresholds - 1, 1, -1 do
			if n >= thresholds[ i ] then
				return thresholds[ i ]
			end
		end
		
		return thresholds[ 1 ]
	end
	
	local sections = {}
	
	for language, stats in pairs( data.languages ) do
		if stats.canonical >= thresholds[ 1 ] then
			local threshold = makeThreshold( stats.canonical )
			
			if not sections[ threshold ] then
				sections[ threshold ] = {}
			end
			
			table.insert( sections[ threshold ], {
				lang = language,
				key = makeSortKey( language )
			} )
		end
	end
	
	local orderedSectionKeys = {}
	
	for threshold, _ in pairs( sections ) do
		table.insert( orderedSectionKeys, threshold )
	end
	
	table.sort( orderedSectionKeys, function ( a, b ) return a > b end )
	
	local out = {}
	
	for i, threshold in ipairs( orderedSectionKeys ) do
		local s = 'ponad ' .. language:formatNum( threshold ) .. ' haseł: '
		local linked = {}
		
		table.sort( sections[ threshold ], function ( a, b ) return a.key < b.key end )
		
		for _, v in ipairs( sections[ threshold ] ) do
			if dictionaries[ v.lang ] then
				table.insert( linked, '[[' .. dictionaries[ v.lang ] .. '|' .. v.lang .. ']]' )
			else
				local fullName = shortLangKeys[ v.lang ] and v.lang or ( 'język ' .. v.lang )
				table.insert( linked, '[[:Kategoria:' .. language:ucfirst( fullName ) .. '|' .. v.lang .. ']]' )
			end
		end
		
		s = s .. table.concat( linked, '&nbsp;• ' )
		table.insert( out, s )
	end
	
	return table.concat( out, '\n\n' )
end

p.queryStorage = function ( frame )
	local property = data
	
	for i, v in ipairs( frame.args ) do
		if type( property ) == 'table' and property[ v ] then
			property = property[ v ]
		else
			return 0
		end
	end
	
	return property
end

p.canonicalCounter = function ( frame )
	local lang = frame.args[ 1 ] or frame:getParent().args[ 1 ]
	
	if lang then
		if data.languages[ lang ] then
			return data.languages[ lang ].canonical
		else
			-- expensive!
			return mw.site.stats.pagesInCategory( lang .. ' (indeks)', 'pages' )
		end
	else
		return data.overall.canonical
	end
end

p.flexiveFormCounter = function ( frame )
	local lang = frame.args[ 1 ] or frame:getParent().args[ 1 ]
	
	if lang then
		if data.languages[ lang ] then
			return data.languages[ lang ].entries - data.languages[ lang ].canonical
		else
			-- expensive!
			return mw.site.stats.pagesInCategory( lang .. ' (formy fleksyjne)', 'pages' )
		end
	else
		return data.overall.entries - data.overall.canonical
	end
end

p.definitionCounter = function ( frame )
	local lang = frame.args[ 1 ] or frame:getParent().args[ 1 ]
	
	if lang then
		if data.languages[ lang ] then
			return data.languages[ lang ].definitions
		else
			return 0
		end
	else
		return data.overall.definitions
	end
end

return p