模組:Namespace pagename
外观
| 此模块已被全保護。僅限管理員編輯。 |
| 此模块被引用於約190,000個頁面,佔全部頁面的2%。 為了避免造成大規模的影響,所有對此模块的編輯應先於沙盒或測試樣例上測試。实施修改前,建议先在討論頁發起討論。 模板引用數量會自動更新。 |
本模組根據頁面所在的命名空間與頁面的實際內容,綜合判斷並輸出該頁面的類型,例如「模板文件」、「用戶沙盒」、「模組重新導向」。命名空間名稱取自 Module:Namespace,頁面類型的名稱與判定規則在 Module:Namespace pagename/data 維護。
包裝模板為 Template:Namespace pagename(捷徑 {{NSPN}}),測試樣例見 Template:Namespace pagename/testcases。
用法
Wikitext
main
{{#invoke:Namespace pagename|main|語言|頁面名稱或命名空間}}
| 位置參數 | 具名參數 | 說明 |
|---|---|---|
|1= |
|lang= |
語言代碼,如 zh-hant、zh-hans、zh-tw、en。省略或留空時取使用者的介面語言,無法對應的語言代碼會回退為英文。
|
|2= |
|pageOrNs= |
要判斷的頁面名稱或命名空間。省略或留空時使用目前頁面。 |
| - | |forcePage= |
強制把第二參數解釋成頁面名稱,不嘗試解析成命名空間。 |
| - | |skipCheckPageType= |
不判斷頁面類型,只輸出「命名空間名稱+預設字尾」,效果形同第二參數傳的是命名空間。 |
| - | |skipCheckContent= |
不讀取頁面內容來判斷類型。title:getContent() 會計入解析器的昂貴函數配額,大量呼叫時可用此參數節省開銷。
|
三個開關參數經 Module:Yesno 解讀,填 yes、1、true 等皆可。
- 第二參數會優先解析成命名空間:純數字且該編號存在時視為命名空間編號,否則
{{ns:}}能解析的名稱(含結尾冒號,如Template:)也視為命名空間。因此要指向名為3或維基百科的條目,請改傳:3、:維基百科,或加上|forcePage=yes。 - 傳入命名空間時沒有頁面可供檢查,輸出一律是「命名空間名稱+預設字尾」。
- 無法解析成合法頁面名稱時輸出紅字錯誤訊息,不會拋出 Lua 錯誤。
- 命名空間名稱取自 Module:Namespace 的
_namespaceName(不是-display),因此4輸出「專案」而非「維基百科」、8輸出「介面」而非「MediaWiki」。
傳入命名空間的範例:
{{#invoke:Namespace pagename|main|zh-hant|0}} <!-- 條目 -->
{{#invoke:Namespace pagename|main|zh-hant|1}} <!-- 討論頁 -->
{{#invoke:Namespace pagename|main|zh-hant|2}} <!-- 用戶頁面 -->
{{#invoke:Namespace pagename|main|zh-hans|2}} <!-- 用户页 -->
{{#invoke:Namespace pagename|main|zh-hant|4}} <!-- 專案頁面 -->
{{#invoke:Namespace pagename|main|zh-hant|8}} <!-- 介面頁 -->
{{#invoke:Namespace pagename|main|zh-hant|12}} <!-- 幫助頁 -->
{{#invoke:Namespace pagename|main|zh-hant|10}} <!-- 模板 -->
{{#invoke:Namespace pagename|main|zh-hant|828}} <!-- 模組 -->
{{#invoke:Namespace pagename|main|zh-hant|-1}} <!-- 特殊頁面 -->
{{#invoke:Namespace pagename|main|zh-hant|Template}} <!-- 模板 -->
{{#invoke:Namespace pagename|main|zh-hant|模板}} <!-- 模板 -->
{{#invoke:Namespace pagename|main|en|2}} <!-- User page -->
{{#invoke:Namespace pagename|main|en|828}} <!-- Module -->
傳入頁面名稱的範例:
{{#invoke:Namespace pagename|main|zh-hant|Template:Namespace pagename}} <!-- 模板 -->
{{#invoke:Namespace pagename|main|zh-hant|Template:Namespace pagename/doc}} <!-- 模板文件 -->
{{#invoke:Namespace pagename|main|zh-hant|Module:Namespace/testcases}} <!-- 模組測試樣例 -->
{{#invoke:Namespace pagename|main|zh-hant|Module:Namespace/sandbox}} <!-- 模組沙盒 -->
{{#invoke:Namespace pagename|main|zh-hant|Template:NSPN}} <!-- 模板重新導向 -->
{{#invoke:Namespace pagename|main|zh-hant|Template:沙盒}} <!-- 模板沙盒 -->
{{#invoke:Namespace pagename|main|zh-hant|Category:沙盒}} <!-- 分類沙盒 -->
{{#invoke:Namespace pagename|main|zh-hant|User:Example/沙盒}} <!-- 用戶沙盒 -->
{{#invoke:Namespace pagename|main|zh-hant|User:Example/common.css}} <!-- 用戶樣式 -->
{{#invoke:Namespace pagename|main|zh-hant|User:Example/common.js}} <!-- 用戶工具 -->
{{#invoke:Namespace pagename|main|zh-hant|Module:Sandbox/test.json}} <!-- 模組數據 -->
{{#invoke:Namespace pagename|main|zh-hant|Template:Namespace pagename/temp}} <!-- 模板臨時頁 -->
{{#invoke:Namespace pagename|main|zh-hant|Template:Editnotices/Page/Foo}} <!-- 編輯提示 -->
{{#invoke:Namespace pagename|main|zh-hant|User:Example/Editnotice}} <!-- 編輯提示 -->
{{#invoke:Namespace pagename|main|zh-hant|User:Example/Emailnotice}} <!-- Example的電郵提示(含用戶連結) -->
{{#invoke:Namespace pagename|main|zh-hant|LTA:Foo}} <!-- 持續出沒的破壞者捷徑 -->
{{#invoke:Namespace pagename|main|zh-hant|Talk:LTA:Foo}} <!-- 持續出沒的破壞者討論捷徑頁 -->
{{#invoke:Namespace pagename|main|zh-hant|en:Wikipedia:Templates}} <!-- 跨語言連結 -->
開關參數與特殊情形的範例:
{{#invoke:Namespace pagename|main|zh-hant|維基百科}} <!-- 專案頁面(被當成命名空間名稱) -->
{{#invoke:Namespace pagename|main|zh-hant|維基百科|forcePage=yes}} <!-- 條目 -->
{{#invoke:Namespace pagename|main|zh-hant|:3}} <!-- 條目 -->
{{#invoke:Namespace pagename|main|zh-hant|3|forcePage=yes}} <!-- 條目 -->
{{#invoke:Namespace pagename|main|zh-hant|9999}} <!-- 條目(無此命名空間,當成頁面名稱) -->
{{#invoke:Namespace pagename|main|zh-hant|Template:Namespace pagename/doc|skipCheckPageType=yes}} <!-- 模板 -->
{{#invoke:Namespace pagename|main|zh-hant|Help:Foo/doc}} <!-- 幫助頁(幫助命名空間不檢測頁面類型) -->
{{#invoke:Namespace pagename|main|zh-hant|Draft:Foo/sandbox}} <!-- 草稿(草稿命名空間不檢測沙盒) -->
{{#invoke:Namespace pagename|main|zh-hant|<<<}} <!-- 紅字錯誤:頁面名稱「<<<」無效。 -->
Lua
以下範例中隱含
local mNSPN = require('Module:Namespace pagename')
_namespace_pagename
-- @param string|nil [lang]
-- @param string|number|nil [pageOrNs]
-- - string:頁面(註:此函數不檢查是否有效)或(偽)命名空間的名字
-- - number:命名空間數字
-- @param table|nil [cfg]
-- @param boolean|nil [cfg.forcePage] 讓模組強行把輸入解釋成頁面
-- @param boolean|nil [cfg.skipCheckPageType] 抑制檢測頁面類型,效果形同 pageOrNs 不是傳頁面
-- @param boolean|nil [cfg.skipCheckContent] 抑制使用頁面內容檢測頁面類型
-- @return string
mNSPN._namespace_pagename(pageOrNs, lang, cfg)輸入無效時返回 Module:Error 產生的錯誤字串,不會拋出錯誤。
範例:
mNSPN._namespace_pagename('zh-hant', 828) -- "模組"
mNSPN._namespace_pagename('zh-hant', 'Module:Namespace/doc') -- "模組文件"
mNSPN._namespace_pagename('zh-hant', 'Template:NSPN') -- "模板重新導向"
mNSPN._namespace_pagename('zh-hant', 'LTA:Foo') -- "持續出沒的破壞者捷徑"
mNSPN._namespace_pagename('zh-hant') -- 目前頁面的類型
-- 不讀取頁面內容,只看命名空間、內容模型與子頁面名稱
mNSPN._namespace_pagename('zh-hant', 'Template:Foo/bar', { skipCheckContent = true })
-- 強制當成頁面名稱
mNSPN._namespace_pagename('zh-hant', '維基百科', { forcePage = true }) -- "條目"
判定順序
了解輸出為何如此,通常只需要照下列順序往下讀,第一個命中的規則就是結果。括號內是 Module:Namespace pagename/data 中對應的設定項。
- 解析輸入。未指定或為空字串時取目前頁面;否則除非指定
forcePage,先嘗試解析成命名空間,失敗才當成頁面名稱。頁面位於條目或討論命名空間時,再交給 Module:Namespace 檢查是否為偽命名空間。 - 跨語言連結(
title.isExternal)→ 輸出「跨語言連結」。 - 偽命名空間→ 交給 Module:Namespace 輸出捷徑名稱,不再往下判斷。
- 取得命名空間名稱(Module:Namespace)。
- 若輸入是命名空間而非頁面,或指定了
skipCheckPageType→ 輸出「命名空間名稱+預設字尾」(DefaultSuffixList),結束。 - 固定沙盒頁名(
SandboxPageNames,以及User:下的×××/沙盒)→ 沙盒。此步在命名空間白名單之前,因此分類、檔案等命名空間的固定沙盒也認得。 - 編輯提示/電郵提示(
NoticeRules)→ 直接取代整個輸出,不加命名空間名稱。 - 模組討論頁的
/testcases子頁→ 測試樣例。模組討論命名空間不在下一步的白名單內,因此這項特例排在白名單檢查之前。 - 命名空間不在
EnablePageTypeNamespaces→ 輸出「命名空間名稱+預設字尾」,結束。目前只有用戶、專案、檔案、模板、草稿、模組六個命名空間會繼續往下判斷,所以Help:Foo/doc只會輸出「幫助頁」。 - 內容模型(
ContentModelMap)→ 樣式/工具/數據,即 CSS、JavaScript、JSON 頁面。 - 重新導向(
title.isRedirect)→ 重新導向。 - 子頁面名稱(
SubpageRules)→ 文件/沙盒/測試樣例/臨時頁/說明頁。子頁面名稱必須列在該類型的names之中,適用的命名空間則由AllowTemplateType、AllowTempType,或該類型自訂的matcher決定。 - 頁面內容特徵(
ContentRules)→ 文件/沙盒/測試樣例,依頁面是否含有{{Documentation subpage}}等標記判斷;指定skipCheckContent時略過此步。 - 以上皆未命中 → 輸出「命名空間名稱+預設字尾」。
子頁面名稱與頁面內容都沒有命中時,輸出只有「命名空間名稱+預設字尾」,例如 Template:Foo/bar 輸出「模板」。
參見
- Module:Namespace pagename/data-頁面類型名稱與判定規則
- Module:Namespace-命名空間名稱與偽命名空間判斷
- Module:Namespace/data-命名空間名稱的資料
- Template:Namespace pagename、Template:NSPN-包裝模板
- Template:Namespace pagename/testcases-測試樣例
-- =====================================================================
-- 根据页面命名空间和实际内容综合判断该页面类型
--
-- 原作者:SunAfterRain
--
-- 维护说明:
--
-- 测试用例见 [[Template:Namespace pagename/testcases]]
--
-- 1. 命名空间(含伪空间)ID、英文名与简繁中文名在[[Module:Namespace/data]]维护。如增加新命名空间注意同时修改[[Template:NS1]]
-- 2. 页面具体内容(例如重定向、沙盒等)名称、判定规则在[[Module:Namespace pagename/data]]维护。
-- 3. 判断逻辑在本页维护。
-- =====================================================================
require('strict')
local yesno = require('Module:Yesno')
local mNS = require('Module:Namespace')
local mError = require('Module:Error')
local data = mw.loadData('Module:Namespace pagename/data')
local lan = mNS._lan
local NS = data.NS
local p = {}
-- ---------------------------------------------------------------------
-- 辅助函数
-- ---------------------------------------------------------------------
local function msg(group, key, lang)
-- 包裹 lan(data[group][key], lang)
return lan(data[tostring(group)][tostring(key)] or {}, lang)
end
local function inArray(item, arr)
for i, v in ipairs(arr) do
if item == v then
return v
end
end
return nil
end
local function getDefaultSuffix(nsid, lang)
local pageConv = data.NamespaceSuffixConfig.DefaultSuffixList[nsid]
if pageConv == false then
return ''
end
return lan(pageConv, lang)
end
local function lowerFirst(value)
-- 將字串第一個文字換成小寫
return mw.ustring.lower(mw.ustring.sub(value, 1, 1)) .. mw.ustring.sub(value, 2)
end
local function makeError(errorMessage)
return mError.error{'[[Module:Namespace pagename]]錯誤:' .. errorMessage}
end
-- ---------------------------------------------------------------------
-- 页面类型匹配规则
-- ---------------------------------------------------------------------
-- 是否为沙盒
local function isSandbox(title, nsid)
local nsSandbox = data.SandboxPageNames[nsid]
if nsSandbox and nsSandbox[title.text] then
return true
end
-- 用户页沙盒
if nsid == NS.USER and mw.ustring.match(title.text, '^[^/]+/沙盒$') then
return true
end
return false
end
-- 根据data中配置的子页面名称(SubpageRules)匹配页面类型
local function matchSubpageType(title, nsid)
local subpageName = lowerFirst(title.subpageText)
local isSubpage = title.text ~= title.rootText
for pageType, rule in pairs(data.SubpageRules) do
-- 先判斷這條規則在本命名空間、本頁面是否適用
local ok
if rule.matcher then
-- 自定义 matcher 取代默认的命名空间白名单与子页面判断
ok = true
if rule.matcher.allowNs then
ok = inArray(nsid, rule.matcher.allowNs) ~= nil
end
if rule.matcher.subpageOnly then
ok = ok and isSubpage
end
else
-- 默认子页面匹配:必须是子页面,且符合命名空间白名单
local allowList = (pageType == 'temp')
and data.NamespaceSuffixConfig.AllowTempType
or data.NamespaceSuffixConfig.AllowTemplateType
ok = isSubpage and inArray(nsid, allowList) ~= nil
end
-- 不論有沒有 matcher,rule.names 存在時都必須命中,
-- 否則設了 matcher 的規則會變成該命名空間下所有子頁面的萬用規則
if ok and (not rule.names or inArray(subpageName, rule.names)) then
return pageType
end
end
return nil
end
-- 根据data中配置的页面内容特征(ContentRules)匹配类型
local function matchContentType(content, nsid)
local isAllowTemplateType = inArray(nsid, data.NamespaceSuffixConfig.AllowTemplateType)
for pageType, patterns in pairs(data.ContentRules) do
-- doc 与 testcases 仅在 AllowTemplateType 指定的命名空间下检测
local allowed = (pageType == 'sandbox') or isAllowTemplateType
if allowed then
for _, pat in ipairs(patterns) do
if type(pat) == 'function' then
if pat(nsid, content) then return pageType end
elseif type(pat) == 'string' then
if mw.ustring.match(content, pat) then return pageType end
end
end
end
end
return nil
end
-- 检查提示类特殊页面(Editnotice / Emailnotice 等)
local function matchNotice(title, nsid)
for pageType, rule in pairs(data.NoticeRules) do
if rule.matcher then
local nsRule = rule.matcher[nsid]
if nsRule then
local ok = true
if nsRule.rootText then
ok = ok and title.rootText == nsRule.rootText
end
if nsRule.subpageText then
ok = ok and title.subpageText == nsRule.subpageText
end
if nsRule.onlyFirstLevel then
ok = ok and title.rootText == title.baseText
end
if ok then
return pageType
end
end
end
end
return nil
end
-- ---------------------------------------------------------------------
-- 核心解析逻辑
-- ---------------------------------------------------------------------
-- @param {string} [lang]
-- @param {string | number} [pageOrNs]
-- - string: 頁面(註:此函數不檢查是否有效) or (偽)命名空間的名字
-- - number: 命名空間數字
-- @param {boolean} [cfg.forcePage] 讓模組強行把輸入解釋成頁面
-- @param {boolean} [cfg.skipCheckPageType] 抑制檢測頁面類型,效果形同 pageOrNs 不是傳頁面
-- @param {boolean} [cfg.skipCheckContent] 抑制使用頁面內容檢測頁面類型
function p._namespace_pagename(lang, pageOrNs, cfg)
cfg = cfg or {}
local success, title, PNS, PNSIsTalk, nsid
--
-- 一、处理标题
--
if not pageOrNs or pageOrNs == '' then
-- 如果未指定頁面及命名空間參數,以當前頁面讀取
title = mw.title.getCurrentTitle()
nsid = title.namespace
else
local ns
if not cfg.forcePage then
if pageOrNs:match('^-?%d+$') and mw.site.namespaces[tonumber(pageOrNs)] then
-- 如果用户指定一个数字,优先视为命名空间数字,无相应数字时才视为条目名称
ns = mw.site.namespaces[tonumber(pageOrNs)]
else
-- 再判断是否为命名空间名称
ns = mw.site.namespaces[pageOrNs]
if not ns and pageOrNs:match(':$') then
-- 允許 <ns>:
ns = mw.site.namespaces[pageOrNs:sub(1, -2)]
end
end
end
-- 如果为命名空间名称,按命名空间处理,否则按页面标题处理
if ns then -- pageOrNs 是{{ns:}}能解析的
-- 請注意:
-- 如果你想指向一個名為 [[3]] 的條目,你應該
-- 1. 傳參 :3
-- 2. 指定 forcePage=true
-- 以免落到這裡發生錯誤
-- 想指定名為 [[維基百科]] 的條目同理
nsid = ns.id
title = nil
else
success, title = pcall(mw.title.new, pageOrNs)
if not success or not title or title.prefixedText == '' then
-- 不是一个合法页面名称,报错
return makeError(string.format(msg('Error', 'pg-error', lang), pageOrNs))
else
-- 其他正常標題或偽命名空間,無效的命名空間也被當條目處理
nsid = title.namespace
if nsid == 0 or nsid == 1 then
PNS, PNSIsTalk = mNS._isPseudoNamespace(pageOrNs, true)
end
end
end
end
-- 1.1 外部链接与伪命名空间,提前返回
if title and title.isExternal then
return msg('Type', 'external', lang)
elseif PNS then
-- 偽命名空間
-- 丟給[[Module:Namespace]]處理
return mNS._pseudoNamespaceDisplay(lang, PNS, PNSIsTalk)
end
-- 1.2 命名空间名称
local namespace = mNS._namespaceName(lang, nsid)
-- 如果不需要检测页面类型,或者没有加载页面内容,则直接结束
if cfg.skipCheckPageType or not title then
return namespace .. getDefaultSuffix(nsid, lang)
end
--
-- 二、判断頁面内容類型
--
local pageType
-- 特殊处理:固定沙盒
if isSandbox(title, nsid) then
return namespace .. msg('Type', 'sandbox', lang)
end
-- 特殊处理:编辑提示/邮件提示页面
pageType = matchNotice(title, nsid)
if pageType ~= nil then
local text = msg('Type', pageType, lang)
local userLink = '[[User:' .. title.rootText .. '|' .. title.rootText .. ']]'
text = string.gsub(text, '%%u', function() return userLink end)
return text
end
-- 特殊处理:(特例處理)模組討論頁當測試樣例展示效果
-- 模組討論命名空間不在 EnablePageTypeNamespaces 内,必须在 2.1 之前处理
if nsid == NS.MODULE_TALK and lowerFirst(title.subpageText) == 'testcases' then
return namespace .. msg('Type', 'testcases', lang)
end
-- 2.1 判断是否应该继续检测页面内容类型
local isCheckPageType = inArray(nsid, data.NamespaceSuffixConfig.EnablePageTypeNamespaces)
if not isCheckPageType then
return namespace .. getDefaultSuffix(nsid, lang)
end
-- 2.2 根据内容模型判断,如非标准wikitext则直接结束
local modelType = data.ContentModelMap[title.contentModel]
if modelType then
return namespace .. msg('Type', modelType, lang)
end
-- 2.3 是否为重定向
if title.isRedirect then
return namespace .. msg('Type', 'redirect', lang)
end
-- 2.4 根据子页面名称判断
-- 追加檢測文檔、沙盒、測試樣例的命名空間
-- 追加檢測臨時頁面的命名空間
-- matchSubpageType已对该命名空间该不该追加作出判断
if pageType == nil then
pageType = matchSubpageType(title, nsid)
-- 立即结束,减少不必要嵌入
if pageType ~= nil then
return namespace .. msg('Type', pageType, lang)
end
end
-- 2.5 根据页面内容判断
if not cfg.skipCheckContent then
local content = title:getContent() or ''
pageType = matchContentType(content, nsid)
end
if pageType then
return namespace .. msg('Type', pageType, lang)
end
return (namespace .. getDefaultSuffix(nsid, lang) or '')
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame, {
valueFunc = function (key, value)
if key == 2 then
return type(value) == 'string' and mw.text.trim(value) or value
elseif value then
value = mw.text.trim(value)
if value ~= '' then
return value
end
end
return nil
end
})
local lang = args[1] or args['1'] or args['lang'] or nil
local pageOrNs = args[2] or args['2'] or args['pageOrNs'] or nil
local forcePage = yesno(args['forcePage']) or false
local skipCheckPageType = yesno(args['skipCheckPageType']) or false
local skipCheckContent = yesno(args['skipCheckContent']) or false
return p._namespace_pagename(lang, pageOrNs, {
forcePage = forcePage,
skipCheckPageType = skipCheckPageType,
skipCheckContent = skipCheckContent,
})
end
return p