This module will sort text in the Han script. It is used to sort Ai-Cham, Southern Amami-Oshima, Central Bai, Panyi Bai, Southern Bai, Biao-Jiao Mien, Biyo, Eastern Min, Jin, Mandarin, Northern Pinghua, Chinese Pidgin English, Puxian Min, Macau Pidgin Portuguese, Southern Pinghua, Huizhou, Central Min, Dungan, Daur, E, Gan, Hakka, Xiang, Japanese, Hachijō, Kikai, Lama Bai, Middle Chinese, Literary Chinese, Middle Vietnamese, Caolan, Northern Min, Translingual, Miyako, Min Nan, Hokkien, Hainanese, Leizhou Min, Teochew, Nùng, Old Chinese, Old Japanese, Oki-No-Erabu, Old Uyghur, Bouyei, Baekje, Rouran, Tuyuhun, Tuoba, Wuhuan, Xianbei, Northern Amami-Oshima, Yaeyama, Okinawan, Sui, Bailang, Toku-No-Shima, Alchuka, Bala, Kyakala, Tày, Jie, Vietnamese, Wu, Waxiang, Classical Tibetan, Middle Mongol, Buyeo, Kunigami, Yonaguni, Yoron, Cantonese, Zhuang, Zauzou, Chinese, Shaozhou Tuhua, Sichuanese, Taishanese, Goguryeo, Zakhring, and Khitan. The module should preferably not be called directly from templates or other modules. To use it from a template, use {{sortkey}}. Within a module, use Module:languages#Language:makeSortKey.

For testcases, see Module:Hani-sortkey/testcases.


makeSortKey(text, lang, sc)
Generates a sortkey for a given piece of text written in the script specified by the code sc, and language specified by the code lang.
When the sort fails, returns nil.

The demonstration functions that generated the content shown below are housed in Module:Hani-sortkey/templates. Modifications to the module can be tested in Module:Hani-sortkey/sandbox. Sortkeys for individual characters are retrieved from one of 178 data modules. Module:Hani-sortkey/data creates documentation for these modules.

Show sortkeys

  • (乙01)
  • 𡆔 (口23)
  • 𡎇 (土09)
  • 阿坝 (阜05土04)

Ideographic description sequences

local export = {}

local m_str_utils = require("Module:string utilities")

local codepoint = m_str_utils.codepoint
local concat = table.concat
local explode_utf8 = m_str_utils.explode_utf8
local insert = table.insert
local u = m_str_utils.char

local m_data = require("Module:Hani-sortkey/data/serialized")
local m_data_core = mw.loadData("Module:Hani-sortkey/data/core")
local cache = {}

	Returns the index in the string where the ideographic description sequence
	(IDS) ends, or the index of the end of the string. Iterates whenever
	another ideographic description character (IDC) is found.
local function findEndOfIDS(text, IDchar, i)
	if not (text and IDchar and i) then
		return nil
	local j = i
	local component = 1
	-- Number of components expected after current IDC.
	local components = m_data_core.ids[IDchar]
	while component <= components do
		j = j + 1
		local char = text[j]
		if not char then
		elseif m_data_core.ids[char] then
			j = findEndOfIDS(text, char, j)
		component = component + 1
		If the expected number of components has been found,
		return the current index in the text.
	if component - components == 1 then
		return j
		return nil

local function unserialize(a, b)
	return m_data_core.radicals[a:byte()] .. ("%02d"):format(b:byte() - 10)

-- The data is stored in [[Module:Hani-sortkey/data]]. This data is not accessed directly (due to the large amount of memory this would consume), but is instead stored in a serialized form as [[Module:Hani-sortkey/data/serialized]]. If the data is changed, the new serialized data can be generated with [[Module:Hani-sortkey/data/serializer]].
function export.getData(char)
	if type(char) == "string" then
		char = codepoint(char)
	elseif type(char) ~= "number" then
		error("getData must operate on a single character or codepoint.")
	local offset, s, f, lookup = 0
	for i = 2, m_data_core.ranges.n, 2 do
		s, f = m_data_core.ranges[i - 1], m_data_core.ranges[i]
		if char > f then
			offset = offset + f - s + 1
		elseif char >= s and char <= f then
			lookup = 2 * (offset + char - s + 1)
			return m_data:sub(lookup - 1, lookup):gsub("(.)(.)", unserialize)
	return u(char)

function export.makeSortKey(text, lang, sc)
	local scripts = {
		Hani = true,
		Hans = true,
		Hant = true,
		Jpan = true,
		Kore = true
	if sc and not scripts[sc] then
		return text:uupper()
	local sort = {}
	text = explode_utf8(text)
	local text_len = #text
	local i, char = 0
	while i < text_len do
		i = i + 1
		char = text[i]
		if m_data_core.preconvert[char] then
			local j = 0
			for c in m_data_core.preconvert[char]:gmatch(".[\128-\191]*") do
				if j == 0 then
					text[i] = c
					insert(text, i + j, c)
				j = j + 1
			char = text[i]
			text_len = #text
			If we encounter an ideographic description character (IDC),
			find out if it begins a valid ideographic description sequence (IDS).
			If the IDS is valid and a sortkey for it is listed in
			[[Module:Hani-sortkey/data/unsupported]], then return
			the sortkey, and move to the next character after the
			Otherwise, insert the IDC into the sortkey and move to the next
			character after the IDC.
			If the IDS is valid and no sortkey for it is found, track it.
		if m_data_core.ids[char] then
			local j = findEndOfIDS(text, char, i)
			local IDS, data
			if j then
				IDS = concat(text, nil, i, j)
				data = m_data_core.unsupported[IDS]
			if not data then
				if IDS then
					mw.log("ideographic description sequence without sortkey: '"
						.. IDS .. "'")
					mw.log("invalid ideographic description sequence at the beginning of '"
						.. text[i] .. "'")
			if IDS and data then
				insert(sort, data)
				i = j
				insert(sort, char)
			if not cache[char] then
				cache[char] = export.getData(char)
			insert(sort, cache[char])
	return concat(sort)

return export