Difference between revisions of "Module:Backpack item"

From Team Fortress Wiki
Jump to: navigation, search
Line 1: Line 1:
-- TODO: Missing features:
+
--[[
-- Stat clocks: https://wiki.teamfortress.com/wiki/Civilian_Grade_Stat_Clock
+
These are the order of parameters in different languages.
-- Halloween spells: https://www.youtube.com/watch?v=kA7K4jNnMmQ
+
When the template is evaluated, the english text here will be replaced with language-appropriate strings.
-- War paint prefix
+
Please note the missing spaces are intentional, as some languages concatenate words together.
-- TODO: Update the golden frying pan (strange professional killstreak)
+
The raw strings for these words live in Template:Dictionary/common_strings#Module:Backpack_item
 +
Note that extra spaces, empty parenthesis, and extra dashes will be cleaned up automatically.
 +
--]]
  
local p = {}
+
-- Missing translations: ar, cs
 
+
-- Self translated: none
require('Module:Backpack item/format')
+
item_name_format = {
 
+
  ['da']      = 'quality filter festivized killstreak item_name',
-- Defined here so that it can be referenced inside the next function
+
  ['de']      = 'item_name - quality filter killstreak - festivized',
local frame = nil
+
  ['en']      = 'quality filter festivized killstreak item_name',
 +
  ['es']      = 'item_name quality filter festivized killstreak',
 +
  ['fi']      = 'qualityfilter killstreak festivizeditem_name',
 +
  ['fr']      = 'item_name festivized (quality filter killstreak)',
 +
  ['hu']      = 'qualityfilter festivizedkillstreak item_name',
 +
  ['it']      = 'item_name (festivized) (quality filter killstreak)',
 +
  ['ja']      = 'quality filter killstreak festivized item_name',
 +
  ['ko']      = 'quality filter festivized killstreak item_name',
 +
  ['nl']      = 'qualityfilter festivizedkillstreakitem_name',
 +
  ['no']      = 'quality filter festivized killstreak item_name',
 +
  ['pl']      = 'item_name (quality filter festivized killstreak)',
 +
  ['pt']      = 'item_name filter (festivized) (quality killstreak)',
 +
  ['pt-br']  = 'item_name filter (festivized) (quality killstreak)',
 +
  ['ro']      = 'qualityfilter festivizedkillstreakitem_name',
 +
  ['ru']      = 'item_name killstreak festivized quality filter',
 +
  ['sv']      = 'quality filter festivized killstreakitem_name',
 +
  ['tr']      = 'qualityfitler festivizedkillstreak item_name',
 +
  ['zh-hans'] = 'quality filter festivizedkillstreak item_name',
 +
  ['zh-hant'] = 'qualityfilter festivizedkillstreakitem_name',
 +
}
  
--[[ A more natural way to invoke templates in lua. Examples:
+
-- Missing translations: fi, fr (limited is missing), pl
  {{If lang}} -> template('If lang')
+
-- Self translated: none
   {{See also|Foo}} -> template('See also', 'Foo')
+
item_desc_format_strange = {
   {{Hatnote|Foo|Bar}} -> template('Hatnote', {'Foo', 'Bar'})
+
   ['cs']      = 'limited strange_rank_name item_kind rankson',
   {{Lang
+
   ['da']      = 'limited item_kind strange_rank_name rankson',
    | de = German
+
   ['de']      = 'limited item_kind strange_rank_name rankson',
    | en = English
+
  ['en']      = 'limited strange_rank_name item_kind rankson',
    | ru = Russian
+
  ['es']     = 'item_kind strange_rank_name limited rankson',
  }}
+
  ['fr']     = 'item_kind strange_rank_name rankson',
  ->
+
  ['hu']     = 'limitedstrange_rank_name item_kind rankson',
  template('Lang', {
+
   ['it']     = 'item_kind - limited strange_rank_name rankson',
    ['de'] = 'German',
+
   ['ja']      = 'limited strange_rank_name item_kind rankson',
    ['en'] = 'English',
+
   ['ko']      = 'limited strange_rank_name item_kind rankson',
    ['ru'] = 'Russian',
+
   ['nl']      = 'limitedstrange_rank_name item_kind rankson',
   }
+
  ['no']      = 'limiteditem_kind strange_rank_name rankson',
]]
+
  ['pt']     = 'item_kind strange_rank_name limited rankson',
function template(title, args)
+
  ['pt-br']   = 'item_kind strange_rank_name limited rankson',
   if type(args) == 'string' then
+
   ['ro']      = 'limitedstrange_rank_name item_kind rankson',
    args = {args}
+
   ['ru']      = 'item_kind strange_rank_name limited rankson',
   end
+
   ['sv']      = 'limitedstrange_rank_name item_kind rankson',
  return frame:expandTemplate{title=title, args=args}
+
   ['tr']      = 'limitedstrange_rank_name item_kind rankson',
end
+
   ['zh-hans'] = 'limitedstrange_rank_name item_kind rankson',
 
+
   ['zh-hant'] = 'limitedstrange_rank_name item_kind rankson',
-- Returns '(text)', with correct parenthesis characters in Chinese.
 
function paren(text)
 
   return template('Lang', {
 
    -- English and default
 
    ['en']      = '(' .. text .. ')',
 
    ['zh-hans'] = '(' .. text .. ')',
 
    ['zh-hant'] = '(' .. text .. ')',
 
   })
 
end
 
 
 
-- Colors are from styles in https://wiki.teamfortress.com/wiki/MediaWiki:Common.css
 
local grade_colors = {
 
  civilian  = 'B0C3D9',
 
   freelance = '5E98D9',
 
   mercenary = '4B69FF',
 
   commando  = '8847FF',
 
   assassin  = 'D32CE6',
 
   elite    = 'EB4B4B',
 
 
}
 
}
  
local quality_colors = {
+
-- A list of strings which mean "don't show" rather than "use me"
   normal        = 'B2B2B2',
+
local negatives = {
  unusual        = '8650AC',
+
   ['none'] = true,
   vintage        = '476291',
+
   ['no'] = true,
  unique        = 'FFD700',
+
   ['0'] = true,
   community      = '70B04A',
+
   ['false'] = true,
  valve          = 'A50F79',
+
   ['hide'] = true,
   ['self-made'] = '70B04A',
+
   ['negative'] = true,
   customized    = '476291',
+
   [''] = true,
  genuine        = '4D7455',
 
   strange        = 'CF6A32',
 
  completed      = '8650AC',
 
  haunted        = '38F3AB',
 
   collectors    = 'C00000',
 
  decorated      = 'FAFAFA',
 
 
}
 
}
  
-- Potentially add the icon in the corner (paint splotch, unusual, pyroland, etc)
+
function format_string(order, args)
function get_corner_icon(args)
+
  assert(type(order) == 'string', 'You must call Lang() on the format before passing it to this function')
  local image
 
  local image_link
 
  if args['paint-color'] then
 
    image = 'Paint Splat ' .. args['paint-color']
 
    image_link = 'Paint Can'
 
  elseif args['unusual-effect'] then
 
    image = 'Viewmode unusual'
 
    image_link = 'Unusual'
 
  elseif args['decal-icon'] then
 
    image = 'Paint Splat Decal'
 
    image_link = 'Decal Tool'
 
  elseif args['strange-icon'] then
 
    image = 'Viewmode strange'
 
    image_link = 'Strange'
 
  elseif args['stat-icon'] then
 
    image = 'Viewmode statclock'
 
    image_link = 'Decorated'
 
  elseif args['pyroland-icon'] then
 
    image = 'Viewmode pyrovision'
 
    image_link = 'Pyroland'
 
  else -- Early exit if there's no icon, there's no reason to make an empty div
 
    return ''
 
  end
 
 
 
  local icon_begin = '<div style="width: 26px; height: 26px; ' .. template('Lang', {
 
    ['ar'] = 'float: left; right: 86px;',
 
    -- English and default
 
    ['en'] = 'float: right; left: 86px;',
 
  }) .. ' position: absolute; top: 6px;">'
 
  local icon_end = '</div>'
 
 
 
  return icon_begin .. '[[File:' .. image .. '.png|25px|link=' .. image_link .. template('if lang') .. ']]' .. icon_end
 
end
 
  
-- The main item image (and link)
+
   local output_string
function get_main_image(args)
 
   local image
 
  local image_link = args['item-name']
 
  if args['custom-icon'] then
 
    image = args['custom-icon']
 
    image_link = '{{{BASEPAGENAME}}'
 
  elseif args['grade'] and args['wear'] then
 
    image = 'Backpack ' .. args['item-name'] .. ' ' .. args['item-kind'] .. ' ' .. args['wear'] .. '.png'
 
  elseif args['festivized'] then
 
    image = 'Backpack Festivized ' .. args['item-name'] .. '.png'
 
  elseif args['item-name'] then
 
    image = 'Backpack ' .. args['item-name'] .. '.png'
 
  else
 
    image = 'Backpack Unknownweapon.png'
 
  end
 
  
   local wikitext = '[[File:' .. image .. '|90x90px'
+
   local idx = string.find(order, 'item_name')
   if args['file-link'] ~= nil then
+
   if idx ~= nil then
     wikitext = wikitext .. '|link=' .. image_link
+
     -- Step 1: Split out prefix/suffix
  end
+
    local prefix = string.sub(order, 0, idx - 1)
  return wikitext .. ']]'
+
    local suffix = string.sub(order, idx + 9)
end
 
  
-- The color and font for the full name of the item
+
    -- Step 2: Replace parameters with their actual values
function get_full_item_name_style(args)
+
    for arg_name, arg_text in pairs(args) do
  local color
+
      assert(arg_text, 'Text for arg ' .. arg_name .. ' was nil')
  if args['custom-color'] then
+
      prefix = string.gsub(prefix, arg_name, arg_text)
    color = args['custom-color']
+
      suffix = string.gsub(prefix, arg_name, arg_text)
  elseif grade_colors[args['grade']] then
 
    color = '#' .. grade_colors[args['grade']]
 
  elseif quality_colors[args['quality']] then
 
    color = '#' .. quality_colors[args['quality']]
 
  else -- Default
 
    color = '#' .. quality_colors['unique']
 
  end
 
 
 
  local style = 'color: ' .. color .. '; font-size:18px; line-height:16px; text-shadow: 2px 1px 1px #24201B; font-family:'
 
 
 
  -- Specific fonts for some languages that aren't supported by the standard TF2 Build font.
 
  style = style .. template('Lang', {
 
    ['ar']      = 'مبنيات TF2, bold;',
 
    -- English and default
 
    ['en']      = 'TF2 Build, Verdana, Geneva, sans-serif;',
 
    ['hu']      = 'Verdana, Geneva, sans-serif; font-weight: bold;',
 
    ['ja']      = 'TF2 Build, Shin Go, Heisei Kaku Gothic W5; font-weight: bold;',
 
    ['ko']      = 'Pathang, sans-serif, Totum, Kodig, serif; font-weight: bold;',
 
    ['zh-hans'] = 'SimSum-18030, SimHei, serif; font-weight: bold;',
 
    ['zh-hant'] = 'JhengHei, Verdana, JhengHei, JhengHei, sans-serif; font-weight: bold;',
 
  })
 
  return style
 
end
 
 
 
-- The full name of the item, including any prefixes and suffixes
 
function get_full_item_name(args)
 
  -- Early exit if 'custom-name' is specified.
 
  if args['custom-name'] then
 
    if args['no-custom-name-quotes'] == 'yes' then
 
      return args['custom-name']
 
    else
 
      return '"' .. args['custom-name'] .. '"'
 
 
     end
 
     end
  end
 
  
  -- Step 2: Compute the strings
+
    -- Step 3: Replace prefix/suffix as needed
  local festivized = ''
+
     if negatives[args['prefix']] then
  local filter = ''
+
      prefix = ''
  local item_name = ''
+
     elseif args['prefix'] ~= nil then
  local killstreak = ''
+
      prefix = args['prefix'] .. ' '
  local quality = ''
 
 
 
  if args['festivized'] then
 
     festivized = template('Decorated name', 'festivized')
 
  end
 
 
 
  if args['filter'] then
 
    filter = template('Item name', 'from ' .. args['filter'])
 
  end
 
 
 
  if args['item-name-local'] then
 
    item_name = args['item-name-local']
 
  else
 
    item_name = template('Item name', args['item-name'])
 
  end
 
 
 
  -- TODO: killstreak string comes from somewhere, maybe it's directly in the template?
 
  -- I can probably do better than that.
 
  if args['killstreak'] then
 
    killstreak = 'KILLSTREAK'
 
  end
 
 
 
  if args['quality'] == 'unique' or args['quality'] == 'normal' then
 
     -- Unique and Normal weapons do not have a quality name
 
  elseif args['quality'] == 'strange' then
 
    quality = template('Strange rank name', {['item-name']=args['item-name'], rankson=args['rankson'], kills=args['killcount']})
 
  elseif args['quality'] ~= nil then
 
    -- Not one of the above qualities, default to the quality name.
 
    quality = template('Item name', args['quality'])
 
  end
 
 
 
  local foo = {
 
    festivized=festivized,
 
    filter=filter,
 
    item_name=item_name,
 
    killstreak=killstreak,
 
    quality=quality,
 
    prefix=args['prefix'],
 
    suffix=args['suffix'],
 
  }
 
  assert(foo.item_name, args['item-name'])
 
  assert(foo['item_name'], args['item-name'])
 
 
 
  return format_string(template('Lang', item_name_format), foo)
 
end
 
 
 
-- The full level of the item, including any strange suffixes, wears, grades, etc
 
function get_full_item_level(args)
 
  if args['no-level'] == 'yes' then
 
    return ''
 
  end
 
 
 
  -- Step 1: Precompute a bunch of language-aware strings
 
  local limited = ''
 
  local item_kind = ''
 
  local grade = ''
 
  local wear = ''
 
  local rankson = ''
 
  local strange_rank_name = ''
 
  local item_level = ''
 
 
 
  if args['limited'] then
 
    limited = '[[Limited Quantity' .. template('If lang') .. '|<span style="color:yellow; hover:yellow">' .. template('Common string', 'backpack item limited') .. '</span>]] '
 
  end
 
 
 
  if args['item-kind'] then
 
    item_kind = args['item-kind'] .. ' '
 
  end
 
 
 
  if args['grade'] then
 
    grade = template('Decorated name', args['grade'] .. ' grade') .. ' '
 
  end
 
 
 
  if args['wear'] then
 
    wear = paren(template('Decorated name', args['wear'])) .. ' '
 
  end
 
 
 
  if args['rankson'] then
 
    strange_rank_name = template('Strange rank name', {['item-name']=args['item-name'], rankson=args['rankson'], kills=args['killcount']}) .. ' '
 
    rankson = '- ' .. template('Item name', args['rankson']) .. ': ' .. args['killcount'] .. ' '
 
    for n = 2, 10 do
 
      if args['rankson' .. n] then
 
        if args['killcount' .. n] == nil then args['killcount' .. n] = 0 end
 
        rankson = rankson .. '<br>' .. paren(template('Item name', args['rankson' .. n]) .. ': ' .. args['killcount' .. n])
 
      end
 
 
     end
 
     end
  end
+
    if negatives[args['suffix']] then
 
+
      suffix = ''
  -- Adapted from WebAPI
+
     elseif args['suffix'] ~= nil then
  -- Missing languages: de, fi, nl
+
       suffix = ' ' .. args['suffix']
  -- Self translated: none
 
  if args['item-level'] then
 
    item_level = args['item-level']
 
     if args['item-kind'] then
 
       item_level = template('Common string', {'backpack item level', args['item-level']}) .. ' '
 
 
     end
 
     end
  end
 
 
  -- Step 2: Allow individual languages to specify how to order the strings
 
  -- Note that there are essentially three types of weapon levels: Strange, Grade/Wear, and default.
 
 
  local full_item_level;
 
  if grade ~= '' then
 
    -- Adapted from WebAPI
 
    -- Missing translation: all
 
    -- Self translated: none
 
    -- TODO
 
--[[
 
    full_item_level = template('Lang', {
 
      ['cs']      =
 
      ['da']      =
 
      ['de']      =
 
      ['en']      = grade .. item_kind .. wear,
 
      ['es']      =
 
      ['fi']      =
 
      ['fr']      =
 
      ['hu']      =
 
      ['it']      =
 
      ['ja']      =
 
      ['ko']      =
 
      ['nl']      =
 
      ['no']      =
 
      ['pl']      =
 
      ['pt']      =
 
      ['pt-br']  =
 
      ['ro']      =
 
      ['ru']      =
 
      ['sv']      =
 
      ['tr']      =
 
      ['zh-hans'] =
 
      ['zh-hant'] =
 
    })
 
]]
 
    full_item_level = grade .. item_kind .. wear
 
  elseif strange_rank_name ~= '' then
 
    assert(false, template('Lang', item_desc_format_strange) .. item_desc_format_strange);
 
    full_item_level = format_string(template('Lang', item_desc_format_strange), {
 
      limited=limited,
 
      strange_rank_name=strange_rank_name,
 
      item_kind=item_kind,
 
      rankson=rankson,
 
    })
 
  else
 
    -- Adapted from WebAPI
 
    -- Missing translation: all
 
    -- Self translated: none
 
    -- TODO
 
--[[
 
    full_item_level = template('Lang', {
 
      ['cs']      =
 
      ['da']      =
 
      ['de']      =
 
      ['en']      = limited .. item_level .. item_kind,
 
      ['es']      =
 
      ['fi']      =
 
      ['fr']      =
 
      ['hu']      =
 
      ['it']      =
 
      ['ja']      =
 
      ['ko']      =
 
      ['nl']      =
 
      ['no']      =
 
      ['pl']      =
 
      ['pt']      =
 
      ['pt-br']  =
 
      ['ro']      =
 
      ['ru']      =
 
      ['sv']      =
 
      ['tr']      =
 
      ['zh-hans'] =
 
      ['zh-hant'] =
 
    })
 
]]
 
    full_item_level = limited .. item_level .. item_kind
 
  end
 
 
  -- Step 3: Add coloring for certain types
 
  local color = '#756B5E'
 
  if args['limited'] then
 
    color = 'yellow'
 
  elseif grade_colors[args['grade']] then
 
    color = '#' .. grade_colors[args['grade']]
 
  end
 
 
  return '<span style="color: ' .. color .. '">' .. full_item_level .. '</span>'
 
end
 
 
function p.backpack_item(f)
 
  frame = f
 
  -- TODO: Trying this, it may not work.
 
  local args = f.args
 
  if #args == 0 then args = f.args end
 
 
  -- Setting some defaults for parameters
 
  if args['width'] == nil then args['width'] = '100%' end
 
  if args['style'] == nil then args['style'] = '' end
 
  if args['killcount'] == nil then args['killcount'] = 0 end
 
  if args['item-level'] == nil then args['item-level'] = '1-100' end
 
 
  -- Normalize some parameters
 
  if args['quality'] ~= nil then args['quality'] = args['quality']:lower() end
 
  if args['grade'] ~= nil then args['grade'] = args['grade']:lower() end
 
  if args['no-custom-name-quotes'] ~= nil then args['no-custom-name-quotes'] = args['no-custom-name-quotes']:lower() end
 
  if args['prefix'] ~= nil then args['prefix'] = args['prefix']:lower() end
 
  if args['suffix'] ~= nil then args['suffix'] = args['suffix']:lower() end
 
 
  -- Define the div for the darker, background box with rounded corners
 
  body = '<div style="text-align: center; background: #24201B; width: ' .. args['width'] .. '; -moz-border-radius: 10px; border-radius: 10px; padding:7px 0px; ' .. args['style'] .. '">'
 
 
  -- Add the div for the lighter, foreground box which holds the item image
 
  -- TODO: <center>? This is probably not needed.
 
  body = body .. '<center><div style="align:center; width: 120px; height: 90px; -moz-border-radius: 10px; border-radius: 10px; background: #3C362F; margin-bottom: 5px; border: 0px #847668 solid; position:relative;">'
 
 
  body = body .. get_corner_icon(args)
 
  body = body .. get_main_image(args)
 
 
  -- End of foreground box
 
  body = body .. '</div></center>'
 
 
  -- Add a transparent div for the text.
 
  body = body .. '<div style="-moz-border-radius: 10px; border-radius: 10px; background: transparent; padding: 5px; margin: 0px 7px;">'
 
 
  -- Add the main item name (and style)
 
  body = body .. '<span style="' .. get_full_item_name_style(args) .. '">' .. get_full_item_name(args) .. '</span>'
 
  
  -- Add a div for the secondary attributes
+
    assert(args['item_name'], 'Text for arg item_name was nil')
  body = body .. '<div style="font-family: ' .. template('Lang', {
+
     output_string = prefix .. args['item_name'] .. suffix
  -- Specific fonts for some languages that aren't supported by the standard TF2 Secondary font. -->
+
  else -- No prefix/suffix, since there's no item_name
    ['en']     = 'TF2 Secondary, Verdana, Geneva, sans-serif;',
+
     -- Step 2: Replace parameters with their actual values
    ['hu']      = 'Verdana, Genva, sans-serif; font-weight: bold;',
+
     for arg_name, arg_text in pairs(args) do
     ['ja']      = 'TF2 Secondary, Shin Go, Heisei Kaku Gothic W5; font-weight: bold;',
+
       assert(arg_text, 'Text for arg ' .. arg_name .. ' was nil')
    ['ko']     = 'Pathang, sans-serif, Totum, Kodig, serif; font-weight: bold;',
+
       order = string.gsub(order, arg_name, arg_text)
    ['zh-hans'] = 'SimSum-18030, SimHei, serif;',
 
     ['zh-hant'] = 'JhengHei, JhengHei, sans-serif;',
 
  }) .. 'font-size: 15px">'
 
 
 
  body = body .. get_full_item_level(args)
 
 
 
  -- TODO: Something legacy with strange-secondary and att-strangerank?
 
 
 
  if args['unusual-effect'] then
 
     body = body .. '<br><font color="#8650AC">' .. template('Common string', {'backpack item unusual effect', args['unusual-effect']}) .. '</font>'
 
  end
 
 
 
  for n = 1, 8 do
 
    if args['att-' .. n .. '-positive'] then
 
       body = body .. '<br><span class="att_positive">' .. args['att-' .. n .. '-positive'] .. '</span>'
 
    elseif args['att-' .. n .. '-negative'] then
 
       body = body .. '<br><span class="att_negative">' .. args['att-' .. n .. '-negative'] .. '</span>'
 
    elseif args['att-' .. n .. '-neutral'] then
 
      body = body .. '<br><span class="att_neutral">' .. args['att-' .. n .. '-neutral'] .. '</span>'
 
 
     end
 
     end
  end
 
  
  if args['festivized'] then
+
     output_string = order
     body = body .. '<br><span style="color: #8650AC">' .. template('Common string', 'backpack item festivized') .. '</span>'
 
  end
 
  if args['killstreaker'] then
 
    body = body .. '<br><span class="att_positive">' .. template('Common string', {'backpack item killstreaker', args['killstreaker']}) .. '</span>'
 
  end
 
  if args['sheen'] then
 
    body = body .. '<br><span class="att_positive">' .. template('Common string', {'backpack item sheen', args['sheen']}) .. '</span>'
 
  end
 
  if args['killstreak'] then
 
    body = body .. '<br><span class="att_positive">' .. template('Common string', 'backpack item killstreaks active') .. '</span>'
 
 
   end
 
   end
  
   local item_description
+
   -- Step 4: Clean up extra characters
   if args['custom-description'] then
+
   output_string = string.gsub(output_string, '%( *', '(') -- Extra spaces in parenthesis
    item_description = '"' .. args['custom-description'] .. '"'
+
   output_string = string.gsub(output_string, ' *%)', ')') -- Extra spaces in parenthesis
   elseif args['item-description'] then
+
  output_string = string.gsub(output_string, '%(%)', '') -- Empty parentheticals
    item_description = args['item-description']
 
  end
 
  if item_description ~= nil then
 
    -- TODO: Span containing just an nbsp? Why?
 
    body = body .. '<br><span style="line-height: 0.3em;">&nbsp;</span>'
 
    body = body .. '<br><span class="att_neutral" style="line-height: 1.2em; font-size: 95%; width: 80%;">' .. item_description .. '</span>'
 
  end
 
  
   if args['gift-from'] then
+
   output_string = string.gsub(output_string, '- *-', '-') -- Double dashes
    body = body .. '<br><br><span class="att_positive">' .. template('Common string', {'backpack item gift from', args['gift-from']}) .. '</span>'
+
  output_string = string.gsub(output_string, '- *$', '') -- Trailing dashes
    if args['date-received'] then
+
  output_string = string.gsub(output_string, '^ *-', '') -- Leading dashes
      body = body .. '<br><span class="att_neutral">' .. template('Common string', {'backpack item date received', args['date-received']}) .. '</span>'
 
    end
 
  end
 
  
   if args['item-expiration'] then
+
   output_string = string.gsub(output_string, ' +', ' ') -- Double spaces
    body = body .. '<br><br><span class="att_negative" style="line-height:1.2em; font-size:95%; width:80%;">' .. template('Common string', {'backpack item item expiration', args['item-expiration']}) .. '</span>'
+
   output_string = string.gsub(output_string, '^ ', '') -- Extra space at start
  end
+
   output_string = string.gsub(output_string, ' $', '') -- Extra space at end
   if args['item-uses'] then
 
    body = body .. '<br><br><span class="att_uses">' .. template('Common string', {'backpack item item uses', args['item-uses']}) .. '</span>'
 
   end
 
  if args['item-flags'] then
 
    body = body .. '<br><br><span class="att_neutral">( ' .. args['item-flags'] .. ' )</span>'
 
  end
 
  
   -- End of secondary attributes div
+
   -- TODO: Replace () in chinese (maybe just pass language in?)
  -- End of transparent div
 
  -- End of darker, background box
 
  body = body .. '</div></div></div>'
 
  
   return body
+
   return output_string
 
end
 
end
 
return p
 

Revision as of 03:13, 13 June 2019

This template is used to display item stats similar to how they appear in-game.

Parameters

Parameter Example Description Code Example Result
Basics
item-name Condor Cap Proper item name in English (Condor Cap instead of Bonnet de condor, for example). Automatically grabs the item's icon. The item name is automatically translated on any language subpages.
{{Backpack item
| item-name = Condor Cap
| item-kind = Hat
}}
Backpack Condor Cap.png

Condor Cap
Level 1 - 100 Hat

custom-name Spam Hammer Adds a custom name with quotation marks. Still needs item-name to produce the correct image.
{{Backpack item
| item-name = Homewrecker
| custom-name = Spam Hammer
| item-kind = Sledgehammer
| item-level = 5
| att-1-positive = +100% damage vs buildings
| att-2-positive = Damage removes Sappers
| att-3-negative = -25% damage vs players
}}
Backpack Homewrecker.png

"Spam Hammer"
Level 5 Sledgehammer
+100% damage vs buildings
Damage removes Sappers
-25% damage vs players

no-custom-name-quotes yes Removes quotation marks from custom names if the custom-name field is used. Used in conjunction with custom-name.
{{Backpack item
| item-name = Texas Slim's Dome Shine
| custom-name = Cream Gravy
| no-custom-name-quotes = yes
| item-kind = Hat
| item-description = Sometimes you just need a little less hat.
}}
Backpack Texas Slim's Dome Shine.png

Cream Gravy
Level 1 - 100 Hat
Sometimes you just need a little less hat.

custom-icon BLU Bombcart.png Adds a custom icon for users' own purposes. This parameter is independent from item-name parameter.
{{Backpack item
| item-name = Mr. Quackers
| custom-icon = BLU Bombcart.png
| item-kind = Mask
| grade = Assassin
}}
BLU Bombcart.png

Mr. Quackers
Assassin Grade Mask
Mask

item-description This hat is the greatest. For items that have an in-game description. Will not display if a custom-description is set. Can use a custom value.
{{Backpack item
| item-name = Yule Hog
| item-kind = Hat
| grade = Mercenary
| item-description = This hat is the greatest.
}}
Backpack Yule Hog.png

Yule Hog
Mercenary Grade Hat
Hat

This hat is the greatest.

custom-description This hat is also the greatest. For items that have a custom in-game description (from a Description Tag). Displays like a normal description but has quotation marks surrounding it. Can use a custom value.
{{Backpack item
| item-name = Napper's Respite
| item-kind = Hat
| custom-description = This hat is also the greatest.
}}
Backpack Napper's Respite.png

Napper's Respite
Level 1 - 100 Hat
"This hat is also the greatest."

prefix none
The Best
Set to "none" to hide any prefix added by quality, while keeping the quality's color. Can use a custom value.
{{Backpack item
| item-name = Flare Gun
| prefix = The Best
| item-kind = Flare Gun
| item-level = 10
| item-description = This weapon will reload when not active
| att-1-positive = 100% critical hit vs burning players
}}
Backpack Flare Gun.png

The Best Flare Gun
Level 10 Flare Gun
100% critical hit vs burning players
This weapon will reload when not active

suffix none
(Slight Return)
Similar to prefix, mainly for use in non-en pages. Can use a custom value.
{{Backpack item
| item-name = Flame Thrower
| suffix = (Slight Return)
| quality = normal
| item-kind  = Flame Thrower
| item-level = 1
| att-1-positive = Extinguishing teammates restores 20 health
| item-description = Afterburn reduces Medi Gun healing and resist shield effects.<br>Alt-Fire: Release a blast of air that pushes enemies and projectiles and extinguish teammates that are on fire.
}}
Backpack Flame Thrower.png

Flame Thrower (Slight Return)
Level 1 Flame Thrower
Extinguishing teammates restores 20 health
Afterburn reduces Medi Gun healing and resist shield effects.
Alt-Fire: Release a blast of air that pushes enemies and projectiles and extinguish teammates that are on fire.

item-level none The item's level. Set to "none" to hide the level. Defaults to "Level 1 - 100". Can use a custom value.
{{Backpack item
| item-name = Antarctic Researcher
| prefix = The
| item-kind = Coat
| item-level = 68
}}
Backpack Antarctic Researcher.png

The Antarctic Researcher
Level 68 Coat

item-kind Hat Item kind. This string is not automatically translated.
{{Backpack item
| item-name = Lucky Cat Hat
| grade = Elite
| item-kind = Absolute Meower
}}
Backpack Lucky Cat Hat.png

Lucky Cat Hat
Elite Grade Absolute Meower
Absolute Meower

Quality
quality genuine Defaults to "unique" (the standard item yellow). See Item quality for a list of accepted values.
{{Backpack item
| item-name = Horseless Headless Horsemann's Headtaker
| quality = genuine
| item-kind = Axe
| item-description = Cursed by dark spirits similar to those that dwell within the Eyelander.
| item-flags = Not Tradable or Marketable
| att-1-neutral = This weapon has a large melee range and deploys and holsters slower
| att-2-negative = No random critical hits
| att-3-negative = -25 max health on wearer
}}
Backpack Horseless Headless Horsemann's Headtaker.png

Genuine Horseless Headless Horsemann's Headtaker
Level 1 - 100 Axe
This weapon has a large melee range and deploys and holsters slower
No random critical hits
-25 max health on wearer
Cursed by dark spirits similar to those that dwell within the Eyelander.

( Not Tradable or Marketable )

unusual-effect Stormy Storm Adds the specified Unusual effect to the item's description. Also adds an Unusual at the top right of the item icon, indicating the item has an Unusual effect. See Unusual for a list of accepted values.
{{Backpack item
| item-name = Rimmed Raincatcher
| unusual-effect = Stormy Storm
| quality = unusual
| item-kind = Hat
| item-description = Because there's no alliteration like alliteration with an 'ARR!'
}}
Viewmode unusual.png
Backpack Rimmed Raincatcher.png

Unusual Rimmed Raincatcher
Level 1 - 100 Hat
★ Unusual Effect: Stormy Storm
Because there's no alliteration like alliteration with an 'ARR!'

rankson Buildings Sapped Primary stat being tracked for a Strange item.
{{Backpack item
| item-name = Shovel
| strange-icon = yes
| rankson = Buildings Sapped
| killcount = 0
| quality = strange
| item-kind  = Shovel
}}
Viewmode strange.png
Backpack Shovel.png

Strange Shovel
Strange Shovel - Buildings Sapped: 0

killcount 7613 Number of points for the primary stat being tracked. Can use a custom value.
{{Backpack item
| item-name = Big Elfin Deal
| strange-icon = yes
| rankson = Points Scored
| killcount = 7613
| quality = strange
| item-kind  = Hat
| item-description = You ask me if I wanna be a dentist one more time, pal, I'm gonna kick your teeth in.
}}
Viewmode strange.png
Backpack Big Elfin Deal.png

Australian Big Elfin Deal
Australian Hat - Points Scored: 7613
You ask me if I wanna be a dentist one more time, pal, I'm gonna kick your teeth in.

rankson2 Kill assists For Strange Parts or the secondary stat being tracked. rankson3 and rankson4 work similarly.
{{Backpack item
| item-name = Medi Gun
| strange-icon = yes
| rankson = Ubers
| rankson2 = Kill Assists
| killcount = 0
| killcount2 = 0
| quality = strange
| item-kind  = Medi Gun
}}
Viewmode strange.png
Backpack Medi Gun.png

Strange Medi Gun
Strange Medi Gun - Ubers: 0
(Kill Assists: 0)

killcount2 50 Number of points for Strange Parts or the secondary stat being tracked. killcount3 and killcount4 work similarly. Can use a custom value.
{{Backpack item
| item-name = Sniper Rifle
| strange-icon = yes
| rankson = Kills
| rankson2 = Damage Dealt
| killcount = 0
| killcount2 = 50
| quality = strange
| item-kind  = Sniper Rifle
}}
Viewmode strange.png
Backpack Sniper Rifle.png

Strange Sniper Rifle
Strange Sniper Rifle - Kills: 0
(Damage Dealt: 50)

filter Coldfront Adds the appropriate Strange Filter prefix/suffix to the weapon. See List of maps for a list of accepted values.
{{Backpack item
| item-name = Shotgun
| filter = Coldfront
| strange-icon = yes
| rankson = kills
| killcount = 0
| quality = strange
| item-kind  = Shotgun
| item-level = 1
}}
Viewmode strange.png
Backpack Shotgun.png

Strange Frosty Shotgun
Strange Shotgun - Kills: 0

strange-icon yes Adds an icon at the top right of the item icon, indicating the item is Strange.
{{Backpack item
| item-name = Scattergun
| strange-icon = yes
| rankson = kills
| killcount = 0
| quality = strange
| item-kind  = Scattergun
}}
Viewmode strange.png
Backpack Scattergun.png

Strange Scattergun
Strange Scattergun - Kills: 0

limited yes If set to "yes", the the item kind will be colored yellow, and the word "Limited" will be added with a link to Limited Quantity.
{{Backpack item
| item-name = Batter's Helmet
| limited = yes
| item-kind = Hat
| item-description = You'll be batting a thousand (skulls in) when you don this red piece of plastic!
}}
Backpack Batter's Helmet.png

Batter's Helmet
Limited Level 1 - 100 Hat
You'll be batting a thousand (skulls in) when you don this red piece of plastic!

grade Commando Colors the item's name according to the specified grade, and treats it like a graded item. Valid options are Civilian, Freelance, Mercenary, Commando, Assassin, and Elite.
{{Backpack item
| item-name = Sapper
| grade = Commando
| quality = Normal
| item-kind = Sapper
| item-description = Place on enemy buildings to disable and slowly drain away its health. Placing a sapper does not remove your disguise
}}
Backpack Electro Sapper.png

Sapper
Commando Grade Sapper
Sapper

Place on enemy buildings to disable and slowly drain away its health. Placing a sapper does not remove your disguise

wear Field-Tested For use on Decorated weapons. Valid options are Battle Scarred, Well-Worn, Field-Tested, Minimal Wear, and Factory New.
{{Backpack item
| item-name = Minigun
| wear = Field-Tested
| skin = War Room
| grade = Commando
| item-kind = Minigun
}}
Backpack War Room Minigun Field-Tested.png

War Room Minigun
Commando Grade Minigun (Field-Tested)

skin Warhawk Decorated skin name. See Template:Dictionary/decorated#Skins for a list of accepted values.
{{Backpack item
| item-name = Rocket Launcher
| wear = Factory New
| skin = Warhawk
| grade = Elite
| item-kind = Rocket Launcher
}}
Backpack Warhawk Rocket Launcher Factory New.png

Warhawk Rocket Launcher
Elite Grade Rocket Launcher (Factory New)

stat-icon yes Adds an icon at the top right of the backpack image, indicating the item is Graded and Strange.
{{Backpack item
| item-name = Revolver
| wear = Factory New
| skin = Blitzkrieg
| grade = Mercenary
| quality = Strange
| stat-icon = yes
| rankson = kills
| killcount = 25
| item-kind = Revolver
}}
Viewmode statclock.png
Backpack Blitzkrieg Revolver Factory New.png

Scarcely Lethal Blitzkrieg Revolver
Mercenary Grade Revolver (Factory New)
Scarcely Lethal Stat Clock Attached
Kills: 25

pyroland-icon yes Adds an icon at the top right of the backpack image, indicating the item is only visible in Pyroland.
{{Backpack item
| item-name = Pyrovision Goggles
| item-kind = Pyrovision Goggles
| item-level = 1-100
| pyroland-icon = yes
| item-description = Enter a world of imagination that's far superior to the crap-ass worlds of imagination you get from books. Pyrovision lets you see the TF2niverse from our favorite arsonist's perspective.
| item-flags = Achievement Item: Not Tradable or Marketable
| att-1-positive = On Equip: Visit Pyroland
}}
Viewmode pyrovision.png
Backpack Pyrovision Goggles.png

Pyrovision Goggles
Level 1-100 Pyrovision Goggles
On Equip: Visit Pyroland
Enter a world of imagination that's far superior to the crap-ass worlds of imagination you get from books. Pyrovision lets you see the TF2niverse from our favorite arsonist's perspective.

( Achievement Item: Not Tradable or Marketable )

halloween-icon yes Adds an icon at the top right of the backpack image, indicating the item is only visible during Halloween/Full Moon.
{{Backpack item
| item-name = Chicken Kiev
| prefix = The
| item-kind = Bird Head
| halloween-icon = yes
| att-1-negative = Holiday Restriction: Halloween / Full Moon
}}
Viewmode spooky off.png
Backpack Chicken Kiev.png

The Chicken Kiev
Level 1 - 100 Bird Head
Holiday Restriction: Halloween / Full Moon

loaner-icon yes Adds an icon at the top right of the backpack image, indicating the item is being loaned.
{{Backpack item
| item-name      = Black Box
| item-kind      = Rocket Launcher
| item-level     = 5
| loaner-icon    = yes
| prefix         = The
| att-1-positive = On Hit: Gain up to +20 health per attack
| att-2-negative = -25% clip size
| item-flags     = Loaner - Cannot be traded, marketed, crafted, or modified
}}
Viewmode loaner.png
Backpack Black Box.png

The Black Box
Level 5 Rocket Launcher
On Hit: Gain up to +20 health per attack
-25% clip size

( Loaner - Cannot be traded, marketed, crafted, or modified )

Attributes
paint-color Mann Co. Orange Adds a colored paint splat at the top right of the item icon, indicating the item is painted. See Paint Can for a list of accepted values.
{{Backpack item
| item-name = Mislaid Sweater
| paint-color = Mann Co. Orange
| prefix = The
| item-kind = Sweater
| grade = commando
}}
Paint Splat CF7336.png
Backpack Mislaid Sweater.png

The Mislaid Sweater
Commando Grade Sweater
Sweater

decal-icon yes Adds this icon at the top right of the item icon, indicating the item has a Decal applied.
{{Backpack item
| item-name = Photo Badge
| decal-icon = yes
| item-kind = Photo Badge
| item-level = 20
| item-description = Discourage identity theft.<br><br>Custom decals can be applied to this item.
}}
Paint Splat Decal.png
Backpack Photo Badge.png

Photo Badge
Level 20 Photo Badge
Discourage identity theft.

Custom decals can be applied to this item.

att-n-neutral
att-n-positive
att-n-negative

att-1-neutral = Cloak Type: Feign Death
att-2-positive = Live forever!
att-3-negative = Just kidding.

Displays up to 8 positive, negative, or neutral values. Can use custom values.
{{Backpack item
| item-name = Dead Ringer
| item-kind = Invis Watch
| item-level = 5
| att-1-neutral = Cloak Type: Feign Death
| att-2-positive = Live forever!
| att-3-negative = Just kidding.
}}
Backpack Dead Ringer.png

Dead Ringer
Level 5 Invis Watch
Cloak Type: Feign Death
Live forever!
Just kidding.

gift-from Robin Display the source of a gifted item. Can use a custom value.
{{Backpack item
| item-name = Robin Walkers
| item-kind = Slippers
| grade = Elite
| gift-from = Robin
| date-received = Mar 29, 2018 (21:43:08)
}}
Backpack Robin Walkers.png

Robin Walkers
Elite Grade Slippers
Slippers


Gift from: Robin
Date Received: Mar 29, 2018 (21:43:08)

date-received Sep 13, 2188 (06:30:00) Display the date a gifted item was received. Will not appear if no gift-from is defined. Can use a custom value.
{{Backpack item
| item-name = Level Three Chin
| item-kind = Chin
| prefix = The
| item-level = 3
| gift-from = Gabe Newell
| date-received = Sep 13, 2188 (06:30:00)
}}
Backpack Level Three Chin.png

The Level Three Chin
Level 3 Chin

Gift from: Gabe Newell
Date Received: Sep 13, 2188 (06:30:00)

item-expiration Dec 31, 2010 (23:00:00) For items that expire. Can use a custom value.
{{Backpack item
| item-name = Gift-Stuffed Stocking
| item-uses = 1
| item-level = 13
| item-kind = Gift
| item-flags = Not Tradable or Marketable
| item-description = Contains an assortment of goodies for good little Mercenaries.
| item-expiration = Dec 31, 2010 (23:00:00)
}}
Backpack Gift-Stuffed Stocking.png

Gift-Stuffed Stocking
Level 13 Gift
Contains an assortment of goodies for good little Mercenaries.

This item will expire on Dec 31, 2010 (23:00:00).

This is a limited use item. Uses: 1

( Not Tradable or Marketable )

item-uses 4 For items that have a limited number of uses. Can use a custom value.
{{Backpack item
| item-name = Noise Maker - Black Cat 
| item-uses = 4
| item-level = 5
| item-kind = Party Favor
}}
Backpack Noise Maker - Black Cat.png

Noise Maker - Black Cat
Level 5 Party Favor

This is a limited use item. Uses: 4

item-flags Not Tradable, Marketable, or Usable in Crafting For item flags. Displays them inside brackets. Can use a custom value.
{{Backpack item
| item-name = Wiki Cap
| quality = community
| item-kind = Hat
| item-level = 20
| unusual-effect   = Community Sparkle
| item-description = Given to valuable Community Contributors<br>Given to valuable contributors to the official TF2 wiki<br>https://wiki.teamfortress.com/
| item-flags = Not Tradable, Marketable, or Usable in Crafting
}}
Viewmode unusual.png
Backpack Wiki Cap.png

Community Wiki Cap
Level 20 Hat
★ Unusual Effect: Community Sparkle
Given to valuable Community Contributors
Given to valuable contributors to the official TF2 wiki
https://wiki.teamfortress.com/


( Not Tradable, Marketable, or Usable in Crafting )

Styling
width 75% Width parameter. Defaults to 100% in infoboxes, but for other usages, can be any value (%, px, pt, em).
{{Backpack item
| item-name = Jumper's Jeepcap
| width = 75%
| item-kind = Hat
}}
Backpack Jumper's Jeepcap.png

Jumper's Jeepcap
Level 1 - 100 Hat

style background: #490040; Style parameter. Can add any valid CSS style to the main div.
{{Backpack item
| item-name = Burstchester
| style = background: #490040;
| prefix = Taunt:
| item-kind = Special Taunt
| grade = Assassin
| item-description = Community Created All Class Taunt
}}
Backpack Burstchester.png

Taunt: Burstchester
Assassin Grade Special Taunt
Special Taunt

Community Created All Class Taunt

See also

Image insertion templatesview  edit )



--[[
These are the order of parameters in different languages.
When the template is evaluated, the english text here will be replaced with language-appropriate strings.
Please note the missing spaces are intentional, as some languages concatenate words together.
The raw strings for these words live in Template:Dictionary/common_strings#Module:Backpack_item
Note that extra spaces, empty parenthesis, and extra dashes will be cleaned up automatically.
--]]

-- Missing translations: ar, cs
-- Self translated: none
item_name_format = {
  ['da']      = 'quality filter festivized killstreak item_name',
  ['de']      = 'item_name - quality filter killstreak - festivized',
  ['en']      = 'quality filter festivized killstreak item_name',
  ['es']      = 'item_name quality filter festivized killstreak',
  ['fi']      = 'qualityfilter killstreak festivizeditem_name',
  ['fr']      = 'item_name festivized (quality filter killstreak)',
  ['hu']      = 'qualityfilter festivizedkillstreak item_name',
  ['it']      = 'item_name (festivized) (quality filter killstreak)',
  ['ja']      = 'quality filter killstreak festivized item_name',
  ['ko']      = 'quality filter festivized killstreak item_name',
  ['nl']      = 'qualityfilter festivizedkillstreakitem_name',
  ['no']      = 'quality filter festivized killstreak item_name',
  ['pl']      = 'item_name (quality filter festivized killstreak)',
  ['pt']      = 'item_name filter (festivized) (quality killstreak)',
  ['pt-br']   = 'item_name filter (festivized) (quality killstreak)',
  ['ro']      = 'qualityfilter festivizedkillstreakitem_name',
  ['ru']      = 'item_name killstreak festivized quality filter',
  ['sv']      = 'quality filter festivized killstreakitem_name',
  ['tr']      = 'qualityfitler festivizedkillstreak item_name',
  ['zh-hans'] = 'quality filter festivizedkillstreak item_name',
  ['zh-hant'] = 'qualityfilter festivizedkillstreakitem_name',
}

-- Missing translations: fi, fr (limited is missing), pl
-- Self translated: none
item_desc_format_strange = {
  ['cs']      = 'limited strange_rank_name item_kind rankson',
  ['da']      = 'limited item_kind strange_rank_name rankson',
  ['de']      = 'limited item_kind strange_rank_name rankson',
  ['en']      = 'limited strange_rank_name item_kind rankson',
  ['es']      = 'item_kind strange_rank_name limited rankson',
  ['fr']      = 'item_kind strange_rank_name rankson',
  ['hu']      = 'limitedstrange_rank_name item_kind rankson',
  ['it']      = 'item_kind - limited strange_rank_name rankson',
  ['ja']      = 'limited strange_rank_name item_kind rankson',
  ['ko']      = 'limited strange_rank_name item_kind rankson',
  ['nl']      = 'limitedstrange_rank_name item_kind rankson',
  ['no']      = 'limiteditem_kind strange_rank_name rankson',
  ['pt']      = 'item_kind strange_rank_name limited rankson',
  ['pt-br']   = 'item_kind strange_rank_name limited rankson',
  ['ro']      = 'limitedstrange_rank_name item_kind rankson',
  ['ru']      = 'item_kind strange_rank_name limited rankson',
  ['sv']      = 'limitedstrange_rank_name item_kind rankson',
  ['tr']      = 'limitedstrange_rank_name item_kind rankson',
  ['zh-hans'] = 'limitedstrange_rank_name item_kind rankson',
  ['zh-hant'] = 'limitedstrange_rank_name item_kind rankson',
}

-- A list of strings which mean "don't show" rather than "use me"
local negatives = {
  ['none'] = true,
  ['no'] = true,
  ['0'] = true,
  ['false'] = true,
  ['hide'] = true,
  ['negative'] = true,
  [''] = true,
}

function format_string(order, args)
  assert(type(order) == 'string', 'You must call Lang() on the format before passing it to this function')

  local output_string

  local idx = string.find(order, 'item_name')
  if idx ~= nil then
    -- Step 1: Split out prefix/suffix
    local prefix = string.sub(order, 0, idx - 1)
    local suffix = string.sub(order, idx + 9)

    -- Step 2: Replace parameters with their actual values
    for arg_name, arg_text in pairs(args) do
      assert(arg_text, 'Text for arg ' .. arg_name .. ' was nil')
      prefix = string.gsub(prefix, arg_name, arg_text)
      suffix = string.gsub(prefix, arg_name, arg_text)
    end

    -- Step 3: Replace prefix/suffix as needed
    if negatives[args['prefix']] then
      prefix = ''
    elseif args['prefix'] ~= nil then
      prefix = args['prefix'] .. ' '
    end
    if negatives[args['suffix']] then
      suffix = ''
    elseif args['suffix'] ~= nil then
      suffix = ' ' .. args['suffix']
    end

    assert(args['item_name'], 'Text for arg item_name was nil')
    output_string = prefix .. args['item_name'] .. suffix
  else -- No prefix/suffix, since there's no item_name
    -- Step 2: Replace parameters with their actual values
    for arg_name, arg_text in pairs(args) do
      assert(arg_text, 'Text for arg ' .. arg_name .. ' was nil')
      order = string.gsub(order, arg_name, arg_text)
    end

    output_string = order
  end

  -- Step 4: Clean up extra characters
  output_string = string.gsub(output_string, '%( *', '(') -- Extra spaces in parenthesis
  output_string = string.gsub(output_string, ' *%)', ')') -- Extra spaces in parenthesis
  output_string = string.gsub(output_string, '%(%)', '') -- Empty parentheticals

  output_string = string.gsub(output_string, '- *-', '-') -- Double dashes
  output_string = string.gsub(output_string, '- *$', '') -- Trailing dashes
  output_string = string.gsub(output_string, '^ *-', '') -- Leading dashes

  output_string = string.gsub(output_string, '  +', ' ') -- Double spaces
  output_string = string.gsub(output_string, '^ ', '') -- Extra space at start
  output_string = string.gsub(output_string, ' $', '') -- Extra space at end

  -- TODO: Replace () in chinese (maybe just pass language in?)

  return output_string
end