Module:Age: Difference between revisions

    (update from sandbox: implement show=M (minutes) and show=s (seconds))
    (update from Module:Age/sandbox; this aids localization; used at bnwiki + bswiki)
    Line 3: Line 3:
    local mtext = {
    local mtext = {
    -- Message and other text that should be localized.
    -- Message and other text that should be localized.
    -- Also need to localize text in table names in function dateDifference.
    ['mt-bad-param1'] =            'Invalid parameter $1',
    ['mt-bad-param1'] =            'Invalid parameter $1',
    ['mt-bad-param2'] =            'Parameter $1=$2 is invalid',
    ['mt-bad-param2'] =            'Parameter $1=$2 is invalid',
    Line 25: Line 26:
    ['mt-template-bad-name'] =      'The specified template name is not valid',
    ['mt-template-bad-name'] =      'The specified template name is not valid',
    ['mt-template-x'] =            'The template invoking this must have "|template=x" where x is the wanted operation',
    ['mt-template-x'] =            'The template invoking this must have "|template=x" where x is the wanted operation',
    ['txt-age'] =                  '(age ',
    ['txt-aged'] =                  ' (aged ',
    ['txt-and'] =                  ' and ',
    ['txt-and'] =                  ' and ',
    ['txt-or'] =                    ' or ',
    ['txt-category'] =              'Category:Age error',
    ['txt-comma-and'] =            ', and ',
    ['txt-comma-and'] =            ', and ',
    ['txt-error'] =                'Error: ',
    ['txt-error'] =                'Error: ',
    ['txt-or'] =                   ' or ',
    ['txt-format-default'] =        'mf',  -- 'df' (day first = dmy) or 'mf' (month first = mdy)
    ['txt-module-convertnumeric'] = 'Module:ConvertNumeric',
    ['txt-module-date'] =          'Module:Date',
    ['txt-sandbox'] =              'sandbox',
    ['txt-bda'] = '<span style="display:none"> (<span class="bday">$1</span>) </span>$2<span class="noprint ForceAgeToShow"> (age&nbsp;$3)</span>',
    ['txt-dda'] = '$2<span style="display:none">($1)</span> (aged&nbsp;$3)',
    ['txt-bda-disp'] = 'disp_raw',  -- disp_raw → age is a number only; disp_age → age is a number and unit (normally years but months or days if very young)
    ['txt-dda-disp'] = 'disp_raw',
    ['txt-dmy'] = '%-d %B %-Y',
    ['txt-mdy'] = '%B %-d, %-Y',
    }
     
    local isWarning = {
    ['mt-bad-param1'] = true,
    }
    }


    Line 50: Line 64:
    -- Return objects exported from the date module or its sandbox.
    -- Return objects exported from the date module or its sandbox.
    if not _Date then
    if not _Date then
    local sandbox = frame:getTitle():find('sandbox', 1, true) and '/sandbox' or ''
    local sandbox = frame:getTitle():find(mtext['txt-sandbox'], 1, true) and ('/' .. mtext['txt-sandbox']) or ''
    local datemod = require('Module:Date' .. sandbox)
    local datemod = require(mtext['txt-module-date'] .. sandbox)
    local realDate = datemod._Date
    local realDate = datemod._Date
    _currentDate = datemod._current
    _currentDate = datemod._current
    Line 102: Line 116:
    end
    end
    return text
    return text
    end
    local function dateFormat(args)
    -- Return string for wanted date format.
    local default = mtext['txt-format-default']
    local other = default == 'df' and 'mf' or 'df'
    local wanted = stripToNil(args[other]) and other or default
    return wanted == 'df' and mtext['txt-dmy'] or mtext['txt-mdy']
    end
    local function substituteParameters(text, ...)
    -- Return text after substituting any given parameters for $1, $2, etc.
    return mw.message.newRawMessage(text, ...):plain()
    end
    end


    Line 111: Line 138:
    end
    end


    local function message(msg, id)
    local function message(msg, ...)
    -- Return formatted message text for an error or warning.
    -- Return formatted message text for an error or warning.
    local function getText(msg)
    local function getText(msg)
    return mtext[msg] or error('Bug: message "' .. tostring(msg) .. '" not defined')
    return mtext[msg] or error('Bug: message "' .. tostring(msg) .. '" not defined')
    end
    local text
    if type(msg) == 'table' then
    text = getText(msg[1])
    local rep = {}
    for i, v in ipairs(msg) do
    if i > 1 then
    rep['$' .. (i - 1)] = v
    end
    end
    text = text:gsub('$%d+', rep)
    else
    text = getText(msg)
    end
    end
    local categories = {
    local categories = {
    error = '[[Category:Age error]]',
    error = mtext['txt-category'],
    warning = '[[Category:Age error]]',  -- same as error until determine whether 'Age warning' would be worthwhile
    warning = mtext['txt-category'],
    }
    }
    local a, b, category
    local a, b, k, category
    if id == 'warning' then
    local text = substituteParameters(getText(msg), ...)
    if isWarning[msg] then
    a = '<sup>[<i>'
    a = '<sup>[<i>'
    b = '</i>]</sup>'
    b = '</i>]</sup>'
    k = 'warning'
    else
    else
    a = '<strong class="error">' .. getText('txt-error')
    a = '<strong class="error">' .. getText('txt-error')
    b = '</strong>'
    b = '</strong>'
    k = 'error'
    end
    end
    if mw.title.getCurrentTitle():inNamespaces(0) then
    if mw.title.getCurrentTitle():inNamespaces(0) then
    -- Category only in namespaces: 0=article.
    -- Category only in namespaces: 0=article.
    category = categories[id or 'error']
    category = '[[' .. categories[k] .. ']]'
    end
    end
    return
    return
    Line 178: Line 195:
    -- i == 1 for the first number which can optionally start with an uppercase letter.
    -- i == 1 for the first number which can optionally start with an uppercase letter.
    number = tostring(number)
    number = tostring(number)
    return require('Module:ConvertNumeric').spell_number(
    return require(mtext['txt-module-convertnumeric']).spell_number(
    number,
    number,
    nil,                      -- fraction numerator
    nil,                      -- fraction numerator
    Line 230: Line 247:
    result = '<span data-sort-value="_SORTKEY_♠"></span>'
    result = '<span data-sort-value="_SORTKEY_♠"></span>'
    end
    end
    return result:gsub('_SORTKEY_', sortKey)
    return (result:gsub('_SORTKEY_', sortKey))
    end
    end
    end
    end
    Line 333: Line 350:
    date = date + item
    date = date + item
    if not date then
    if not date then
    return message({ 'mt-cannot-add', item })
    return message('mt-cannot-add', item)
    end
    end
    end
    end
    Line 390: Line 407:
    local name = names[components[i]]
    local name = names[components[i]]
    if name then
    if name then
    local plural = names.plural
    if type(name) == 'table' then
    if not plural or (islist and v[2] or v) == 1 then
    name = mw.getContentLanguage():plural(islist and v[2] or v, name)
    plural = ''
    end
    end
    text:add(vstr .. sep .. name .. plural)
    text:add(vstr .. sep .. name)
    else
    else
    text:add(vstr)
    text:add(vstr)
    Line 442: Line 458:
    -- which have been validated.
    -- which have been validated.
    local names = {
    local names = {
    -- Each name is:
    -- * a string if no plural form of the name is used; or
    -- * a table of strings, one of which is selected using the rules at
    --  https://translatewiki.net/wiki/Plural/Mediawiki_plural_rules
    abbr_off = {
    abbr_off = {
    plural = 's',
    sep = '&nbsp;',
    sep = '&nbsp;',
    y = 'year',
    y = {'year', 'years'},
    m = 'month',
    m = {'month', 'months'},
    w = 'week',
    w = {'week', 'weeks'},
    d = 'day',
    d = {'day', 'days'},
    H = 'hour',
    H = {'hour', 'hours'},
    M = 'minute',
    M = {'minute', 'minutes'},
    S = 'second',
    S = {'second', 'seconds'},
    },
    },
    abbr_on = {
    abbr_on = {
    Line 463: Line 482:
    },
    },
    abbr_infant = {      -- for {{age for infant}}
    abbr_infant = {      -- for {{age for infant}}
    plural = 's',
    sep = '&nbsp;',
    sep = '&nbsp;',
    y = 'yr',
    y = {'yr', 'yrs'},
    m = 'mo',
    m = {'mo', 'mos'},
    w = 'wk',
    w = {'wk', 'wks'},
    d = 'day',
    d = {'day', 'days'},
    H = 'hr',
    H = {'hr', 'hrs'},
    M = 'min',
    M = {'min', 'mins'},
    S = 'sec',
    S = {'sec', 'secs'},
    },
    },
    abbr_raw = {},
    abbr_raw = {},
    Line 552: Line 570:
    (textOptions.suffix or '')
    (textOptions.suffix or '')
    end
    end
    return message({ 'mt-bad-show', show.id })
    return message('mt-bad-show', show.id)
    end
    end


    Line 863: Line 881:
    return message('mt-invalid-bd-age')
    return message('mt-invalid-bd-age')
    end
    end
    local disp, show = 'disp_raw', 'y'
    local disp = mtext['txt-bda-disp']
    local show = 'y'
    if diff.years < 2 then
    if diff.years < 2 then
    disp = 'disp_age'
    disp = 'disp_age'
    Line 872: Line 891:
    end
    end
    end
    end
    local df = stripToNil(args.df)  -- day first (dmy); default is month first (mdy)
    local result = substituteParameters(
    local result = '(<span class="bday">%-Y-%m-%d</span>) </span>' ..
    mtext['txt-bda'],
    (df and '%-d %B %-Y' or '%B %-d, %-Y')
    date:text('%-Y-%m-%d'),
    result = from_en('<span style="display:none"> ' ..
    from_en(date:text(dateFormat(args))),
    date:text(result) ..
    from_en(dateDifference({
    '<span class="noprint ForceAgeToShow"> ' ..
    mtext['txt-age'] ..
    dateDifference({
    diff = diff,
    diff = diff,
    show = show,
    show = show,
    Line 885: Line 901:
    disp = disp,
    disp = disp,
    sep = 'sep_space',
    sep = 'sep_space',
    }) ..
    }))
    ')</span>')
    )
    local warnings = tonumber(frame.args.warnings)
    local warnings = tonumber(frame.args.warnings)
    if warnings and warnings > 0 then
    if warnings and warnings > 0 then
    Line 915: Line 931:
    end
    end
    if invalid then
    if invalid then
    result = result .. message({ 'mt-bad-param1', invalid }, 'warning')
    result = result .. message('mt-bad-param1', invalid)
    end
    end
    end
    end
    Line 953: Line 969:
    return message('mt-invalid-dates-age')
    return message('mt-invalid-dates-age')
    end
    end
    local df = stripToNil(args.df)  -- day first (dmy); default is month first (mdy)
    local fmt_date, fmt_ymd
    local result
    if date1.day then  -- y, m, d known
    if date1.day then  -- y, m, d known
    result = (df and
    fmt_date = dateFormat(args)
    '%-d %B %-Y' or
    fmt_ymd = '%-Y-%m-%d'
    '%B %-d, %-Y') ..
    '<span style="display:none">(%-Y-%m-%d)</span>'
    elseif date1.month then  -- y, m known; d unknown
    elseif date1.month then  -- y, m known; d unknown
    result =
    fmt_date = '%B %-Y'
    '%B %-Y' ..
    fmt_ymd = '%-Y-%m-00'
    '<span style="display:none">(%-Y-%m-00)</span>'
    else  -- y known; m, d unknown
    else  -- y known; m, d unknown
    result =
    fmt_date = '%-Y'
    '%-Y' ..
    fmt_ymd = '%-Y-00-00'
    '<span style="display:none">(%-Y-00-00)</span>'
    end
    end
    result = from_en(date1:text(result) ..
    local result = substituteParameters(
    mtext['txt-aged'] ..
    mtext['txt-dda'],
    dateDifference({
    date1:text(fmt_ymd),
    from_en(date1:text(fmt_date)),
    from_en(dateDifference({
    diff = diff,
    diff = diff,
    show = 'y',
    show = 'y',
    abbr = 'abbr_off',
    abbr = 'abbr_off',
    disp = 'disp_raw',
    disp = mtext['txt-dda-disp'],
    range = 'dash',
    range = 'dash',
    sep = 'sep_space',
    sep = 'sep_space',
    }) ..
    }))
    ')')
    )
    local warnings = tonumber(frame.args.warnings)
    local warnings = tonumber(frame.args.warnings)
    if warnings and warnings > 0 then
    if warnings and warnings > 0 then
    Line 1,002: Line 1,015:
    end
    end
    if invalid then
    if invalid then
    result = result .. message({ 'mt-bad-param1', invalid }, 'warning')
    result = result .. message('mt-bad-param1', invalid)
    end
    end
    end
    end
    Line 1,074: Line 1,087:
    parm = translate[parm]
    parm = translate[parm]
    if parm == nil then  -- test for nil because false is a valid setting
    if parm == nil then  -- test for nil because false is a valid setting
    return message({ 'mt-bad-param2', argname, args[argname] })
    return message('mt-bad-param2', argname, args[argname])
    end
    end
    parms[argname] = parm
    parms[argname] = parm
    Line 1,085: Line 1,098:
    if show then
    if show then
    if show.id ~= round then
    if show.id ~= round then
    return message({ 'mt-conflicting-show', args.show, args.round })
    return message('mt-conflicting-show', args.show, args.round)
    end
    end
    else
    else