Jump to content

Module:Msg

From translatewiki.net

This module supports a system of templates that render links to messages in message documentation. See Template:Msg-meta for the more detailed explanation.

Uses Module:Msg/styles.css for styling.

Tests

Basic feature support:

  • Wikibase-cancel [not capitalised] (“cancel”)
  • cancel [namespace with lowercase titles] (“Cancel”)
  • Translating:MediaWiki/Basic glossary/publish--term [subpages in title] (“publish”)
  • Word-separator [empty message in Chinese](「」)
  • Sitejspreview [RTL support test] (“<strong>{{GENDER:|זכור שאתה רק עושה|זכרי שאת רק עושה|נא לזכור שזוהי רק}} תצוגה מקדימה של קוד ה־JavaScript הזה. הוא עדיין לא פורסם!</strong>”)

Multi-line messages

  • Movepagetext-noredirectfixer (“Using the form below will rename a page, moving all of its history to the new name. The old title will become a redirect page to the new title. Be sure to check for [[Special:DoubleRedirects|double]] or [[Special:BrokenRedirects|broken redirects]]. You are responsible for making sure that links continue to point where they are supposed to go.
    Note that the page will <strong>not</strong> be moved if there is already a page at the new title, unless it is a redirect and has no past edit history. This means that you can rename a page back to where it was renamed from if you make a mistake, and you cannot overwrite an existing page.
    <strong>Note:</strong> This can be a drastic and unexpected change for a popular page; please be sure you understand the consequences of this before proceeding.
    ”)
  • Abusefilter-warning (“Warning: This action has been automatically identified as harmful. Unconstructive actions will be quickly reverted, and egregious or repeated unconstructive editing will result in your account or IP address being blocked. If you believe this action to be constructive, you may submit it again to confirm it. A brief description of the abuse rule which your action matched is: $1”)

With keywords

  • Rollback-success (“Reverted edits by {{GENDER:$3|$1}}; changed back to last revision by {{GENDER:$4|$2}}.”)
  • Pagecategories (“{{PLURAL:$1|Category|Categories}}”)

Optional messages

Missing/invalid input


--
-- Port [[Template:Msg-meta]] to Lua
--
require( 'strict' )
local p = {}

local getArgs = require( 'Module:Arguments' ).getArgs
local relevantLanguage = require( 'Module:RelevantLanguage' )._main

-- [[Module:Msg/styles.css]]
local stylesPage = 'Module:Msg/styles.css'

-- See [[:Category:Message maintenance]]
local errorCat = '[[Category:Message maintenance/msg-%s|%s]]'

local currentTitle = mw.title.getCurrentTitle()
local nsNumber = currentTitle.namespace

-- Do not add error categories in main/user/thread/module namespaces
local errorCatEnabled = nsNumber ~= 0
	and not currentTitle:inNamespace( 'User' )
	and not currentTitle:inNamespace( 'User talk' )
	and not currentTitle:inNamespace( 'Thread' )
	and not currentTitle:inNamespace( 'Module' )

-- Do not check for existence in main/thread/talk namespaces
local existCheckAllowed = nsNumber ~= 0
	and not currentTitle:inNamespace( 'Thread' )
	and not mw.site.namespaces[ nsNumber ].isTalk

local function isEmpty( str )
	return str == nil or str == ''
end

local function useMwMessage( key, lang, ... )
	if isEmpty( key ) then
		return ''
	end
	if isEmpty( lang ) then
		lang = 'en'
	end
	return mw.message.new( key ):inLanguage( lang ):params( arg ):plain()
end

local function useMwTitle( key, lang, ns )
	local pageName = string.format( '%s/%s', key, lang )
	local success, result = pcall( mw.title.makeTitle, ns, pageName )
	
	if not success then
		return nil
	end
	
	return result
end

local function getErrorCat( meta, key )
	meta = meta or 'meta'
	key = key or '*'
	
	if errorCatEnabled then
		return string.format( errorCat, meta, key )
	end
	
	return ''
end

local function getError( err, meta, str )
	meta = meta or 'meta'
	if not isEmpty( str ) then
		err = string.format( '%s "%s"', err, str )
	end
	
	local errText = string.format(
		'[[Template:Msg-%s|{{msg-%s}}]] error: %s',
		meta,
		meta,
		err
	)
	local result = mw.html.create( 'strong' )
		:addClass( 'error msg-' .. meta )
		:attr( 'lang', 'en' )
		:attr( 'dir', 'ltr' )
		:wikitext( errText )
	return tostring( result ) .. getErrorCat( meta )
end

local function getComment( comment, lang )
	if isEmpty( comment ) then
		return ''
	end
	
	return useMwMessage( 'Word-separator', lang ) .. useMwMessage( 'Brackets', lang, comment )
end

local function getLink( mwTitle, wikilink )
	local linkText = mwTitle.baseText
	if wikilink then
		return string.format(
			'[[:%s|%s]]',
			mwTitle.fullText,
			mwTitle.baseText
		)
	end
	
	return string.format(
		'[%s %s]',
		mwTitle:fullUrl( 'action=edit' ),
		mwTitle.baseText
	)
end

local function getRawText( pageTitle, frame )
	-- frame:preprocess is used here to handle fallbacks correctly
	frame = frame or mw.getCurrentFrame()
	local text = frame:preprocess(
		string.format( '{{msgnw:%s}}', pageTitle )
	)

	-- msgnw: outputs &#91;&#91;:MediaWiki:Example/ru&#93;&#93; code if there is no message
	if mw.ustring.find( text, pageTitle, 1, true ) ~= nil then
		return false
	end
	
	if not isEmpty( text ) then
		-- [[mw:Strip markers]] should be removed, but are outputted in HTML form
		text = text:gsub( '&#39;', '%\'' )
			:gsub( '&#34;', '%"' )
		return mw.text.killMarkers( text )
	end
	
	return text
end

-- Accepts full page title
local function getText( pageTitle, lang, frame )
	local text = getRawText( pageTitle, frame )

	if text == false then
		return false
	end
	
	local result = ''
	if text == '' then
		-- Empty messages get [[MediaWiki:Historyempty]]
		result = useMwMessage( 'Historyempty', lang )
	else
		text = mw.text.trim( text )
		
		-- Replace line breaks to render multi-line messages
		text = mw.ustring.gsub( text, '\n&#10;', '<br>' )
		text = mw.ustring.gsub( text, '\n', ' ' )
		
		result = useMwMessage( 'Quotes', lang, string.format( '<code>%s</code>', text ) )
	end

	return useMwMessage( 'Word-separator', lang ) .. useMwMessage( 'Parentheses', lang, result )
end

function p._main( key, ns, comment, lang, config )
	local title = useMwTitle( key, lang, ns )
	local class = not isEmpty( config.meta ) and 'msg-' .. config.meta or ''
	local frame = config.frame or mw.getCurrentFrame()
	
	-- Return an error for false language codes
	if not mw.language.isSupportedLanguage( lang ) then
		return getError( 'Invalid language code', config.meta, lang )
	end
	
	-- Return an error if the key starts with namespace name
	local titlePrefix = mw.ustring.sub( mw.ustring.lower( key ), 1, mw.ustring.len( ns ) + 1 )
	if titlePrefix == mw.ustring.lower( ns ) .. ':' then
		return getError( 'Message key starts with namespace:', config.meta, string.format( '%s:%s', ns, key ) )
	end
	
	-- Check if link is valid after title is known for sure
	if title == nil then
		return getError( 'Invalid title', config.meta, string.format( '%s:%s', ns, key ) )
	end
	
	local mwLang = mw.language.new( lang )
	local result = mw.html.create( 'span' )
		:addClass( 'plainlinks ts-msg ' .. class )
	
	-- Optionally check for existence of English message
	local doesNotExist = false
	if config.checkIfExists then
		-- Check existence by checking message text output to avoid expensive #ifexist checks
		-- Less performant, but does the job well enough unless something changes
		local origTitle = useMwTitle( key, 'en', ns )
		doesNotExist = origTitle == nil or getRawText( origTitle.fullText, frame ) == false
		
		if origTitle ~= nil and doesNotExist then
			-- Missing links link to English version for log access
			title = origTitle
			
			result:addClass( 'ts-msg-missing' )
			result:wikitext( getErrorCat( config.meta, key ) )
			
			mw.addWarning(
				'[[Module:Msg]]: ' ..
				useMwMessage( 'Red-link-title', lang, string.format( '[[%s]]', title.fullText ) )
			)
		end
	end
	
	local link = mw.html.create( 'var' )
		:attr( 'lang', 'zxx' )
		:attr( 'dir', 'ltr' )
	local linkText = getLink( title, doesNotExist )
	comment = getComment( comment )
	
	if not config.noText then
		local text = getText( title.fullText, lang, frame )
		
		-- Display English fallback message if possible
		if text == false then
			linkText = getLink( title, true )
			
			if not doesNotExist then
				local origTitle = useMwTitle( key, 'en', ns )
				text = getText( origTitle.fullText, 'en', frame )
			end
			
			if text ~= false then
				mwLang = mw.language.new( 'en' )
				result:addClass( 'ts-msg-untranslated' )
			end
		end
		
		-- Say ‘page does not exist’ if there is no English message
		link:wikitext( linkText )
		if text == false then
			text = tostring( link ) .. comment
			result:wikitext( useMwMessage( 'Red-link-title', lang, text ) )
		else
			result:node( link )
				:wikitext( comment )
				:wikitext( text )
		end
	else
		link:wikitext( linkText )
		result:node( link )
			:wikitext( comment )
	end
	
	-- Add lang/dir tags here since they can change to English
	result:attr( 'lang', mwLang:toBcp47Code() )
		:attr( 'dir', mwLang:getDir() )
	
	-- Only tag in content namespaces
	if config.isError and existCheckAllowed then
		result:wikitext( getErrorCat( config.meta, key ) )
	end
	
	local templatestyles = frame:extensionTag{
		name = 'templatestyles',
		args = { src = stylesPage },
	}
	return templatestyles .. tostring( result )
end

function p.main( frame )
	local args = getArgs( frame )
	local key = args[ 1 ]
	local comment = args[ 2 ]
	local ns = args[ 'namespace' ]
	local lang = args[ 'lang' ]
	local config = {
		frame = frame,
		meta = args[ 'meta' ],
		checkIfExists = existCheckAllowed and isEmpty( args[ 'exist' ] ),
		noText = not isEmpty( args[ 'notext' ] ),
		-- Should only be an option in [[Template:Msg]]
		isError = not isEmpty( args[ 'error' ] ),
	}
	
	if isEmpty( key ) or key:find( '{{{' ) == 1 then
		return getError( 'No message key passed', config.meta )
	end
	
	-- Use relevant language depending on the namespace/subpage name
	if isEmpty( lang ) then
		lang = relevantLanguage( frame )
	end
	
	return p._main( key, ns, comment, lang, config )
end

return p