
Anwendung
Das Modul wird direkt von der Vorlage {{Kalender}} aufgerufen. Parameterbeschreibung siehe dort. Im Projektnamensraum befindet sich die technische Dokumentation Wikivoyage:Kalender. Das Modul ersetzt die Erweiterung „Calendar-Wikivoyage“.
Beispiel
<< | Juni 2025 | >> | ||||
---|---|---|---|---|---|---|
Mo | Di | Mi | Do | Fr | Sa | So |
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
{{ Kalender | Titellink = Nachrichten:$B $Y | prevLink = [[Nachrichten:$P $Q|<<]] | nextLink = [[Nachrichten:$N $O|>>]] | Hervorhebung = 2 4 6 | 5 = Nachrichten:$B $Y }}
Hervorhebung des aktuellen Tags
Der Parameter showToday
(zeigeHeute
) legt fest, ob und in welcher Weise der aktuelle Tag hervorgehoben wird. Folgende Werte sind möglich:
|showToday=true
– Das Skript nimmt die Hervorhebung selbst vor. Da die Seiten nicht täglich geparst werden, kann sich die Aktualisierung verzögern.|showToday=javascript
– Im Tabellentagtable
wird die Klassevoy-calendar-show-today
eingefügt. Zudem enthält das Tag die Klassevoy-calendar-yyyy-m
, aus der das Jahr und der Monat hervorgehen wie z. B.voy-calendar-2025-6
. Alle Zellen mit den Tagesangaben enthalten eine Klasse in der Formvoy-calendar-d
wie z. B.voy-calendar-23
. Mit Hilfe eines Javascript-Skripts lässt sich die Klassevoy-calendar-today
einfügen, die die Hervorhebung des aktuellen Tags bewirkt.|showToday=false
– Der aktuelle Tag wird nicht hervorgehoben.
Beispiel für ein geeignetes Javascript-Skript:
function addTodayToCalendar() { 'use strict'; var calendars = $( '.voy-calendar-show-today' ); if ( calendars.length ) { let classPrefix = '.voy-calendar-'; let today = new Date(), d = String( today.getDate() ), m = String( today.getMonth() + 1 ), // January is 0 yyyy = today.getFullYear(); let monthFilter = `${classPrefix}${yyyy}-${m}`, daySelector = classPrefix + d; calendars.filter( monthFilter ).each( function() { $( this ).find( daySelector ).addClass( 'voy-calendar-today' ); } ); } } addTodayToCalendar();
Hinweise
- Die obige Dokumentation wurde aus der Seite Modul:Calendar/Doku eingefügt. (bearbeiten | Versionsgeschichte) Die Kategorien für dieses Modul sollten in der Dokumentation eingetragen werden. Die Interwiki-Links sollten auf Wikidata eingepflegt werden.
- Liste der Unterseiten
-- Module:Calendar -- require( 'strict' ) -- module variable and administration local cl = { moduleInterface = { suite = 'Calendar', serial = '2025-06-19', item = 56528415 }, -- miscellaneous i18n messages i18n = { classPrefix = 'voy-calendar-', dayClasses = { 'sundays', 'mondays', 'tuesdays', 'wednesdays', 'thursdays', 'fridays', 'saturdays' }, titleFormat = '%s %i', noMonthOrYear = '[[Category:Kalender: Monat oder Jahr nicht spezifiziert]]', showToday = '[[Category:Kalender: Kalender zeigt aktuellen Tag an]]', unknownParams = '<span class="error">Fehlerhafte(r) Parameter</span>[[Category:Kalender: fehlerhafte Parameter]]', usingCalendar = '[[Category:Seiten, die die Kalender-Vorlage verwenden]]', wrongCharCount = '<span class="error">Fehlerhafte Zeichenanzahl</span>[[Category:Kalender: fehlerhafte Zeichenanzahl]]', wrongHighlight = '<span class="error">Fehlerhafte Hervorhebung</span>[[Category:Kalender: fehlerhafte Hervorhebung]]', wrongLang = '<span class="error">Fehlerhafte Sprache</span>[[Category:Kalender: fehlerhafte Sprache]]', wrongMonth = '<span class="error">Fehlerhafter Monat</span>[[Category:Kalender: fehlerhafter Monat]]', wrongStart = '<span class="error">Fehlerhafter Wochenanfang</span>[[Category:Kalender: fehlerhafter Wochenanfang]]', wrongWidth = '<span class="error">Fehlerhafte Tabellenbreite</span>[[Category:Kalender: fehlerhafte Tabellenbreite]]', wrongYear = '<span class="error">Fehlerhaftes Jahr</span>[[Category:Kalender: fehlerhaftes Jahr]]' }, -- possible argument identifiers -- keep position and tableWidth for backward compatibility params = { align = { 'position', 'align', 'Ausrichtung', 'Lage', default = 'right' }, -- keep position dayCharsCount = { 'dayCharsCount', default = '0' }, generalLinks = { 'generalLinks', 'Tageslinks' }, highlightedDays = { 'highlightedDays', 'Hervorhebung' }, lang = { 'lang', 'Sprache' }, month = { 'month', 'Monat' }, monthCharsCount = { 'monthCharsCount', default = '0' }, nextLink = { 'nextLink' }, offset = { 'offset', 'Versatz', default = '0' }, prevLink = { 'prevLink' }, showToday = { 'showToday', 'zeigeHeute', default = 'true' }, -- alt: 'javascript' start = { 'start', 'Wochenanfang', default = '1' }, -- 0 ... 6 title = { 'title', 'Titel' }, titleLink = { 'titleLink', 'titlelink', 'Titellink' }, width = { 'tableWidth', 'width', 'Breite', 'Tabellenbreite', default = 'default' }, -- keep tableWidth year = { 'year', 'Jahr' } }, -- possible alignment values align = { left = 'left', links = 'left', right = 'right', rechts = 'right', center = 'center', mitte = 'center', zentriert = 'center' }, -- possible width values width = { default = 'default', standard = 'default', none = 'none', no = 'none', n = 'none', keine = 'none', nein = 'none' }, -- possible lang values in addition to language codes -- no means Norwegian (Bokmål or Nynorsk) lang = { content = 'content', standard = 'content', none = 'content', n = 'content', keine = 'content', nein = 'content' }, -- allowed namespaces for categories for Calendar module -- see also: https://de.wikivoyage.org/w/api.php?action=query&meta=siteinfo&siprop=namespaces notAllowedNamespaces = { [ 10 ] = 1, -- Template [ 11 ] = 1, -- Template Talk [ 828 ] = 1, -- Module [ 829 ] = 1 -- Module Talk }, -- wiki language tableLang = mw.getContentLanguage():getCode() } local errorMsgs = {} local categories = cl.i18n.usingCalendar -- using without additional spaces -- add error message to errorMsgs table local function addErrorMsg( msg ) table.insert( errorMsgs, cl.i18n[ msg ] ) end -- get errorMsgs table as string local function getErrorMsgs() local result = table.concat( errorMsgs, ' ' ) if result ~= '' then result = result .. ' ' end return result end -- check if param is set: not nil or empty local function isSet( param ) return param and param ~= '' end -- helper function round local function round( n ) return ( n >= 0 ) and math.floor( n + 0.5 ) or math.ceil( n - 0.5 ) end -- helper function getInteger local function getInteger( s, default ) return math.floor( tonumber( s ) or tonumber( default ) or 0 ) end -- check for possible arguments against list = params table local function checkParams( frameArgs, list ) local args = { numberedKeys = false } local complete = {} -- named arguments for key, value in pairs( list ) do if type( value ) == 'table' then for key2, value2 in ipairs( value ) do complete[ value2 ] = key args[ key ] = args[ key ] or frameArgs[ value2 ] end args[ key ] = args[ key ] or '' elseif value ~= '' then complete[ value ] = key args[ key ] = frameArgs[ value ] or '' else complete[ key ] = key args[ key ] = frameArgs[ key ] or '' end if args[ key ] == '' and type( value ) == 'table' and value.default then args[ key ] = value.default end end local ok = true local intKey for key, value in pairs( frameArgs ) do -- numbered arguments intKey = tonumber( key ) if intKey then -- frameArgs[ key ] cannot be nil if intKey < 1 or intKey > 31 then ok = false else args[ key ] = mw.text.trim( frameArgs[ key ] ) args.numberedKeys = true end end if not complete[ key ] and not intKey then ok = false end end if not ok then addErrorMsg( 'unknownParams' ) end return args end -- date functions local function formatDate( aDate, aFormat ) return mw.getContentLanguage():formatDate( aFormat, aDate, true ) end local function getDayOfWeek( day, month, year ) return formatDate( ( '%i-%i-%i' ):format( year, month, day ), 'w' ) end local function getDaysInMonth( month, year ) local daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } local days = daysInMonth[ month ] -- leap year check if month == 2 and year % 4 == 0 then days = days + 1 if year % 100 == 0 and year % 400 ~= 0 then days = days - 1 end end return days end -- Using Mediawiki:Key local function getMessage( key, length ) local message = mw.message.new( key ) message = message:inLanguage( cl.tableLang ):plain() if isSet( length ) and length > 0 then message = message:sub( 1, length ) end return message end local function getMonthName( month, length ) local monthKeys = { 'january', 'february', 'march', 'april', 'may_long', 'june', 'july', 'august', 'september', 'october', 'november', 'december' } return getMessage( monthKeys[ month ], length ) end local function getMonthNameAbbrev( month, length ) local monthKeys = { 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' } return getMessage( monthKeys[ month ], length ) end local function getDayName( weekday, length ) local dayKeys = { 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' } return getMessage( dayKeys[ weekday ], length ) end local function getDayNameAbbrev( weekday, length ) local dayKeys = { 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' } return getMessage( dayKeys[ weekday ], length ) end local function replace( s, tab ) if not isSet( s ) then return '' end for key, val in pairs( tab ) do s = s:gsub( '%$' .. key, val ) end s = s:gsub( '%$%%', '$' ) -- $% -> $, escape of magic character $ return s end local function getClass( key, prefix ) return ( prefix and prefix or '' ) .. cl.i18n.classPrefix .. key end local function getTitleRow( args ) local colspan = isSet( args.nextLink ) and 6 or 7 local row = mw.html.create( 'tr' ) :addClass( getClass( 'title-row' ) ) if isSet( args.prevLink ) then colspan = colspan - 1 row:node( mw.html.create( 'td' ) :addClass( getClass( 'prev-link' ) ) :wikitext( args.prevLink ) ) end row:node( mw.html.create( 'th' ) :addClass( getClass( 'title' ) ) :attr( 'colspan', colspan ) :wikitext( args.title ) ) if isSet( args.nextLink ) then row:node( mw.html.create( 'td' ) :addClass( getClass( 'next-link' ) ) :wikitext( args.nextLink ) ) end return tostring( row ) end -- header line with abbreviated days local function getWeekdaysRow( args ) local pos local row = mw.html.create( 'tr' ) :addClass( getClass( 'weekdays' ) ) for i = 0, 6, 1 do pos = ( i + args.start ) % 7 + 1 row:node( mw.html.create( 'th' ) :addClass( getClass( cl.i18n.dayClasses[ pos ] ) ) :wikitext( getDayNameAbbrev( pos, args.dayCharsCount ) ) ) end return tostring( row ) end local function addTableWrapper( args, rows ) -- table classes local classes = getClass( '' ):sub(1, -2) .. getClass( ( '%i-%i' ):format( args.year, args.month ), ' ' ) .. ( args.width == 'default' and getClass( 'default-width', ' ' ) or '' ) if args.align == 'left' then classes = classes .. getClass( 'left', ' ' ) elseif args.align == 'center' then classes = classes .. getClass( 'center', ' ' ) else -- default: right classes = classes .. getClass( 'right', ' ' ) end if args.showToday == 'javascript' then classes = classes .. getClass( 'show-today', ' ' ) end if isSet( args.generalLinks ) then classes = classes .. getClass( 'general-day-links', ' ' ) end local table = mw.html.create( 'table' ) :addClass( classes ) :wikitext( '\n' .. rows .. '\n' ) if not cl.width[ args.width ] then table:css( 'width', args.width ) end return tostring( table ) end local function getWikilink( link, display ) return isSet( display ) and ( '[[%s|%s]]' ):format( link, display ) or ( '[[%s]]' ):format( link ) end local function populateTable( args ) -- header lines with title and abbreviated days local rows = { getTitleRow( args ), getWeekdaysRow( args ) } -- adding days: setting offset local firstDayInMonth = getDayOfWeek( 1, args.month, args.year ) firstDayInMonth = firstDayInMonth - args.start if firstDayInMonth < 0 then firstDayInMonth = firstDayInMonth + 7 end -- table with dates considering offset and weekday of month’s first day local dates = { '', '', '', '', '', '', '' } for i = 1, getDaysInMonth( args.month, args.year ), 1 do dates[ i + firstDayInMonth ] = tostring( i ) end local trows = math.ceil( #dates / 7 ) -- count of table rows -- creating html table local classes, day, day2, intDay, link, pos, row -- additional place holders depending on day local function addPairsToTable( day, pos ) args.placeholders.a = getDayNameAbbrev( pos ) args.placeholders.A = getDayName( pos ) args.placeholders.d = ( '%02d' ):format( day ) args.placeholders.D = tonumber( day ) args.placeholders.e = ( '%2d' ):format( day ) end for i = 0, trows - 1, 1 do row = mw.html.create( 'tr' ) :addClass( getClass( 'row ' ) .. getClass( 'row-' .. i ) ) for j = 1, 7, 1 do pos = ( args.start + j - 1 ) % 7 + 1 day = dates[ 7 * i + j ] -- unlinked day number or '' if isSet( day ) then intDay = tonumber( day ) day2 = day -- possibly linked day number -- numbered links or general links if args[ intDay ] then addPairsToTable( day, pos ) link = replace( args[ intDay ], args.placeholders ) day2 = getWikilink( link, day ) elseif isSet( args.generalLinks ) then addPairsToTable( day, pos ) link = replace( args.generalLinks, args.placeholders ) day2 = getWikilink( link, day ) end classes = getClass( cl.i18n.dayClasses[ pos ] ) .. getClass( day, ' ' ) .. ( args.highlightedDays[ day ] and getClass( 'highlighted', ' ' ) or '' ) if args.showToday == 'true' and args.currentDay == intDay then classes = classes .. getClass( 'today', ' ' ) end row:node( mw.html.create( 'td' ) :addClass( classes ) :wikitext( day2 ) ) else row:node( mw.html.create( 'td' ) :wikitext( '' ) ) end end table.insert( rows, tostring( row ) ) end -- adding table-tag wrapper return addTableWrapper( args, table.concat( rows, '\n' ) ) end local function checkParameterValues( args ) args.start = getInteger( args.start ) if args.start < 0 or args.start > 6 then args.start = 1 addErrorMsg( 'wrongStart' ) end args.dayCharsCount = getInteger( args.dayCharsCount ) if args.dayCharsCount < 0 or args.dayCharsCount > 5 then args.dayCharsCount = 0 addErrorMsg( 'wrongCharCount' ) end args.monthCharsCount = getInteger( args.monthCharsCount ) if args.monthCharsCount < 0 or args.monthCharsCount > 10 then args.monthCharsCount = 0 addErrorMsg( 'wrongCharCount' ) end args.offset = getInteger( args.offset ) args.align = cl.align[ args.align:lower() ] or '' if isSet( args.lang ) then args.lang = args.lang:lower() if not cl.lang[ args.lang ] then if isSet( mw.language.fetchLanguageName( args.lang, cl.tableLang ) ) then cl.tableLang = args.lang else addErrorMsg( 'wrongLang' ) end end end args.currentDay = getInteger( os.date( '%d' ) ) -- today args.currentMonth = getInteger( os.date( '%m' ) ) args.currentYear = getInteger( os.date( '%Y' ) ) if not isSet( args.month ) or not isSet( args.year ) then categories = categories .. cl.i18n.noMonthOrYear end args.month = getInteger( args.month, args.currentMonth ) if args.month < 1 or args.month > 12 then args.month = args.currentMonth addErrorMsg( 'wrongMonth' ) end args.year = getInteger( args.year, args.currentYear ) if args.year < 1970 or args.year > 2400 then args.year = args.currentYear addErrorMsg( 'wrongYear' ) end if args.offset ~= 0 then local offsetMonth = args.offset % 12 local offsetYear = round( ( args.offset - offsetMonth ) / 12 ) args.month = args.month + offsetMonth args.year = args.year + offsetYear if args.month > 12 then args.month = args.month - 12 args.year = args.year + 1 end if args.month < 1 then args.month = args.month + 12 args.year = args.year - 1 end end -- don't show current day if month/year is outdated or in future args.showToday = args.showToday:lower() if args.showToday == 'true' or args.showToday == 'javascript' then if args.currentYear ~= tonumber( args.year ) or args.currentMonth ~= tonumber( args.month ) then args.showToday = 'false' else categories = categories .. cl.i18n.showToday end end args.nextMonthYear = args.year args.nextMonth = args.month + 1 if args.nextMonth == 13 then args.nextMonth = 1 args.nextMonthYear = args.nextMonthYear + 1 end args.prevMonthYear = args.year args.prevMonth = args.month - 1 if args.prevMonth == 0 then args.prevMonth = 12 args.prevMonthYear = args.prevMonthYear - 1 end if isSet( args.titleLink ) or isSet( args.prevLink ) or isSet( args.nextLink ) or isSet( args.generalLinks ) or args.numberedKeys then args.placeholders = { b = getMonthNameAbbrev( args.month ), B = getMonthName( args.month ), m = ( '%02d' ):format( args.month ), M = ( '%02d' ):format( args.nextMonth ), n = getMonthNameAbbrev( args.nextMonth ), N = getMonthName( args.nextMonth ), o = tostring( args.nextMonthYear ):sub( 3, 4 ), O = tostring( args.nextMonthYear ), p = getMonthNameAbbrev( args.prevMonth ), P = getMonthName( args.prevMonth ), q = tostring( args.prevMonthYear ):sub( 3, 4 ), Q = tostring( args.prevMonthYear ), R = ( '%02d' ):format( args.prevMonth ), y = tostring( args.year ):sub( 3, 4 ), Y = tostring( args.year ) } args.titleLink = replace( args.titleLink, args.placeholders ) args.prevLink = replace( args.prevLink, args.placeholders ) args.nextLink = replace( args.nextLink, args.placeholders ) end if not isSet( args.title ) then args.title = cl.i18n.titleFormat:format( getMonthName( args.month, args.monthCharsCount ), args.year ) end if isSet( args.titleLink ) then args.title = getWikilink( args.titleLink, args.title ) end local highlighted = {} if isSet( args.highlightedDays ) then args.highlightedDays = args.highlightedDays:gsub( '[,;%s%c%z]+', ' ' ) local s = mw.text.split( args.highlightedDays, ' ', true ) -- plain for i = 1, #s, 1 do s[ i ] = getInteger( mw.text.trim( s[ i ] ) ) if s[ i ] > 0 and s[ i ] < 32 then highlighted[ tostring( s[ i ] ) ] = 1 else addErrorMsg( 'wrongHighlight' ) end end end args.highlightedDays = highlighted if isSet( args.width ) then args.width = args.width:lower() if cl.width[ args.width ] then args.width = cl.width[ args.width ] end if not cl.width[ args.width ] then local unit, n = args.width:gsub( '^[.%d]+([%a%%]+)$', '%1' ) if n > 0 then local widthUnits = { em = 1, ex = 1, pc = 1, pt = 1, px = 1, [ 'in' ] = 1, mm = 1, cm = 1 } if unit ~= '%' and not widthUnits[ unit ] then args.width = 'default' addErrorMsg( 'wrongWidth' ) end else args.width = 'default' addErrorMsg( 'wrongWidth' ) end end end end function cl.calendar( frame ) local args = checkParams( frame:getParent().args, cl.params ) checkParameterValues( args ) return getErrorMsgs() .. populateTable( args ) .. ( cl.notAllowedNamespaces[ mw.title.getCurrentTitle().namespace ] and '' or categories ) end return cl