Editing Module:Sandbox/LVLGive feedback
Jump to navigation
Jump to search
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local util_module = require("Module:Utilities") | |||
local lang_codes_set = mw.loadJsonData("Data:LangCodes.json") | |||
local dictionary_module = require("Module:Dictionary") | |||
local | -- Overrides applied to searches by key. Designed to handle edge cases where | ||
-- the expected key does not have a localization entry | |||
local KEY_OVERRIDES = { | |||
MoveSlowPercent_label = 'MovementSlow_label', | |||
BonusHealthRegen_label = 'HealthRegen_label', | |||
BarbedWireRadius_label = 'Radius_label', | |||
BarbedWireDamagePerMeter_label = 'DamagePerMeter_label', | |||
BuildUpDuration_label = 'BuildupDuration_label', | |||
TechArmorDamageReduction_label = 'TechArmorDamageReduction_Label', | |||
DamageAbsorb_label = 'DamageAbsorb_Label', | |||
InvisRegen_label = 'InvisRegen_Label', | |||
EvasionChance_label = 'EvasionChance_Label', | |||
DelayBetweenShots_label = 'DelayBetweenShots_Label', | |||
} | |||
function get_lang_file(lang_code) | |||
-- For testing purposes, always load the sandbox data regardless of language code. | |||
return mw.loadJsonData("Data:Sandbox/Lang.json") | |||
end | end | ||
local function | local function process_newlines(text) | ||
if not text or type(text) ~= "string" then return text end | |||
if | -- Replace "n with newline character | ||
return text:gsub('"n', '<br/>') | |||
return | |||
end | end | ||
local function process_html_tags(text, frame) | |||
if not text or type(text) ~= "string" then return text end | |||
local replacements = { | |||
SpiritIcon = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', link='Spirit Damage'}, | |||
tech_damage = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', link='Spirit Damage'}, | |||
SpiritDamage = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
StatDesc_TechPower = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
SpiritResist = {icon='{{Icon/Purple|[[File:Spirit_Armor.png|link=Damage_Resistance|20px]]}}', label_color='#bc8ee8', link='Damage_Resistance'}, | |||
FireRate = {icon='{{Icon/White|[[File:Fire_Rate.png|link=Fire Rate|20px]]}}', label_color='#ffefd7', link='Fire Rate'}, | |||
fire_rate = {icon='{{Icon/White|[[File:Fire_Rate.png|link=Fire Rate|20px]]}}', label_color='#ffefd7', link='Fire Rate'}, | |||
ReducedFireRate = {icon='{{Icon/White|[[File:Fire_Rate.png|link=Fire Rate|20px]]}}', label_color='#ffefd7', link='Fire Rate'}, | |||
MeleeDamage = {icon='{{Icon/Brown|[[File:Melee damage.png|link=Melee Damage|20px]]}}', label_color='#ec981a', link='Melee Damage'}, | |||
WeaponDamage = {icon='{{Icon/Brown|[[File:Damage.png|link=Weapon Damage|20px]]}}', label_color='#ec981a', link='Weapon Damage'}, | |||
damage = {icon='{{Icon/Brown|[[File:Damage.png|link=Weapon Damage|20px]]}}', label_color='#ec981a', link='Weapon Damage'}, | |||
bullet_damage = {icon='{{Icon/Brown|[[File:Damage.png|link=Weapon Damage|20px]]}}', label_color='#ec981a', link='Weapon Damage'}, | |||
BulletResist = {icon='{{Icon/Brown|[[File:Bullet_Armor.png|link=Damage_Resistance|20px]]}}', label_color='#ec981a', link='Damage_Resistance'}, | |||
CombatBarrier = {icon='{{Icon/White|[[File:Bullet_Armor.png|link=Damage_Resistance|20px]]}}', label_color='#ffefd7', link='Barrier'}, | |||
BonusWeaponDamage = {icon='{{Icon/Brown|[[File:Damage.png|link=Weapon Damage|20px]]}}', label_color='#ec981a', link='Weapon Damage'}, | |||
MoveSpeed = {icon='{{Icon/White|[[File:Move_speed.png|link=Move speed|20px]]}}', label_color='#ffefd7', link='Move speed'}, | |||
BonusSpiritDamage = {icon='{{Icon/Purple|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
BonusFireRate = {icon='{{Icon/White|[[File:Fire_Rate.png|link=Fire Rate|20px]]}}', label_color='#ffefd7', link='Fire Rate'}, | |||
Heal = {icon='{{Icon/Green|[[File:Health_regen.png|link=Health_Regen|20px]]}}', label_color='#13f278', link='Health_Regen'}, | |||
BonusMoveSpeed = {icon='{{Icon/White|[[File:Move_speed.png|link=Move speed|20px]]}}', label_color='#ffefd7', link='Move speed'}, | |||
move_speed = {icon='{{Icon/White|[[File:Move_speed.png|link=Move speed|20px]]}}', label_color='#ffefd7', link='Move speed'}, | |||
BonusSprintSpeed = {icon='{{Icon/Green|[[File:Move_speed.png|link=Sprint speed|20px]]}}', label_color='#ffefd7', link='Sprint speed'}, | |||
Spirit = {icon='{{Icon/NoColor|[[File:Spirit_icon.png|link=Spirit Power|20px]]}}', label_color='#bc8ee8', link='Spirit Power'}, | |||
Stun = {icon='{{Icon/White|[[File:Status_Stun.png|link=Stun|20px]]}}', link='Stun'}, | |||
SpiritDPS = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
Healing = {icon='{{Icon/Green|[[File:Health_regen.png|link=Healing|20px]]}}', label_color='#13f278', link='Healing'}, | |||
Regen = {icon='{{Icon/Green|[[File:Health_regen.png|link=Health_Regen|20px]]}}', label_color='#13f278', link='Health_Regen'}, | |||
MaxHealth = {icon='{{Icon/Green|[[File:Healing.png|link=Health|20px]]}}', label_color='#13f278', link='Health'}, | |||
Slow = {icon='{{Icon/White|[[File:MoveSlow.png|link=Movement_Slow|20px]]}}', label_color='#ffefd7', link='Movement_Slow'}, | |||
slow = {icon='{{Icon/White|[[File:MoveSlow.png|link=Movement_Slow|20px]]}}', label_color='#ffefd7', link='Movement_Slow'}, | |||
SlowResistance = {icon='{{Icon/White|[[File:MoveSlow.png|link=Movement_Slow#Movement_Slow_Resist|20px]]}}', label_color='#ffefd7', link='Movement_Slow#Movement_Slow_Resist'}, | |||
Immobilize = {icon='{{Icon/White|[[File:Immobilize.png|link=Status Effects#Immobilized|20px]]}}', label_color='#ffefd7', link='Status Effects#Immobilized'}, | |||
cooldown = {icon='{{Icon/White|[[File:AttributeIconTechDuration.png|link=Ability Cooldown|20px]]}}', label_color='#ffefd7', link='Ability Cooldown'}, | |||
stun = {icon='{{Icon/White|[[File:Stun.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
invisible = {icon='{{Icon/White|[[File:Invisible.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
cast = {icon='{{Icon/White|[[File:Cast.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
silence = {icon='{{Icon/White|[[File:Silence.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
Silence = {icon='{{Icon/Purple|[[File:Silence.png|link=Silence|20px]]}}', label_color='#bc8ee8', link='Silence'}, | |||
revealed = {icon='{{Icon/White|[[File:Revealed.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
bullet_armor_up = {icon='{{Icon/White|[[File:Barrier.png|link=Barrier|20px]]}}', label_color='#ffefd7', link='Barrier'}, | |||
tech_armor_up = {icon='{{Icon/White|[[File:Barrier.png|link=Barrier|20px]]}}', label_color='#ffefd7', link='Barrier'}, | |||
Pulling = {icon='{{Icon/NoColor|[[File:Displacement.png|link=|20px]]}}', label_color='#ffefd7', link='Status_Effects'}, | |||
Pull = {icon='{{Icon/NoColor|[[File:Displacement.png|link=|20px]]}}', label_color='#ffefd7', link='Status_Effects'}, | |||
Pulls = {icon='{{Icon/NoColor|[[File:Displacement.png|link=|20px]]}}', label_color='#ffefd7', link='Status_Effects'}, | |||
KnockBack = {icon='{{Icon/NoColor|[[File:Displacement.png|link=|20px]]}}', label_color='#ffefd7', link='Status_Effects'}, | |||
KnockUp = {icon='{{Icon/NoColor|[[File:Displacement.png|link=|20px]]}}', label_color='#ffefd7', link='Status_Effects'}, | |||
} | |||
-- Process HTML spans | |||
text = text:gsub('<span class="highlight">(.-)</span>', "<span style= font-weight:bold>%1</span>") | |||
text = text:gsub('<span class="highlight_spirit">(.-)</span>', '<span style="font-weight:bold; color:#bc8ee8">%1</span>') | |||
text = text:gsub('<span class="diminish">(.-)</span>', '<span style="font-style: italic;color:#C0C0C0">%1</span>') | |||
text = text:gsub('<span class=".-">(.-)</span>', '%1') | |||
text = text:gsub('<Panel class=\"AbilityPropertyIcon StatDesc_TechPower\">', '{{Icon/Purple|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}') | |||
text = text:gsub('</Panel>', '') | |||
-- Process panel tags | |||
text = text:gsub('<Panel class=\"AbilityPropertyIcon prop_(.-)\">', function(key) | |||
local replacement = replacements[key] | |||
if replacement then | |||
local label_color = replacement.label_color or 'inherit' | |||
local icon = replacement.icon or '' | |||
local link = replacement.link or '' | |||
-- pre-process string, then check to omit link if the string is empty | |||
local translated = frame:preprocess('{{#invoke:Lang|get_string|InlineAttribute_' .. key .. '}}') | |||
if translated == '' then | |||
return string.format('<span class="no-blue-link" style="text-wrap:nowrap; font-weight:bold; color:%s;">%s</span>', label_color, icon) | |||
else | |||
return string.format('<span class="no-blue-link" style="text-wrap:nowrap; font-weight:bold; color:%s;">%s [[%s|%s]]</span>', | |||
label_color, | |||
icon, | |||
link, | |||
translated) | |||
end | |||
else | |||
return '<span style="color:red;font-weight:bold;border-bottom:1px dotted red;" title="Missing attribute definition - Add \''..key..'\' to local replacements in [Module:Lang]">['..key..']</span>' | |||
end | |||
end) | |||
-- Process citadel_inline_attribute tags | |||
text = text:gsub("{g:citadel_inline_attribute:'(.-)'}", function(key) | |||
local replacement = replacements[key] | |||
if replacement then | |||
local label_color = replacement.label_color or 'inherit' | |||
local icon = replacement.icon or '' | |||
local link = replacement.link or '' | |||
-- pre-process string, then check to omit link if the string is empty | |||
local translated = frame:preprocess('{{#invoke:Lang|get_string|InlineAttribute_' .. key .. '}}') | |||
if translated == '' then | |||
return string.format('<span class="no-blue-link" style="text-wrap:nowrap; font-weight:bold; color:%s;">%s</span>', label_color, icon) | |||
else | |||
return string.format('<span class="no-blue-link" style="text-wrap:nowrap; font-weight:bold; color:%s;">%s [[%s|%s]]</span>', | |||
label_color, | |||
icon, | |||
link, | |||
translated) | |||
end | |||
else | |||
return '<span style="color:red;font-weight:bold;border-bottom:1px dotted red;" title="Missing attribute definition - Add \''..key..'\' to local replacements in [Module:Lang]">['..key..']</span>' | |||
end | |||
end) | |||
KEYBINDS = { | |||
Attack = '{{Mouse|1}}', | |||
ADS = '{{Mouse|2}}', | |||
AltCast = '{{Mouse|3}}', | |||
Reload = 'R', | |||
Roll = 'Shift', | |||
Mantle = 'Space', | |||
Crouch = 'Ctrl', | |||
AbilityMelee = 'Melee', | |||
Ability1 = '1', | |||
Ability2 = '2', | |||
Ability3 = '3', | |||
Ability4 = '4', | |||
MoveDown = 'Down', | |||
MoveForward = 'Forward', | |||
} | |||
-- Process citadel_binding/citadel_keybind tags to replace with keybind strings/templates | |||
text = text:gsub("{g:citadel_binding:'(.-)'}", function(key) | |||
local replacement = KEYBINDS[key] | |||
if replacement then | |||
return replacement | |||
else | |||
return '<span style="color:red;font-weight:bold;border-bottom:1px dotted red;" title="Missing keybind - Add \''..key..'\' to local replacements in [Module:Lang]">['..key..']</span>' | |||
end | |||
end) | |||
text = text:gsub("{g:citadel_keybind:'(.-)'}", function(key) | |||
local replacement = KEYBINDS[key] | |||
if replacement then | |||
return replacement | |||
else | |||
return '<span style="color:red;font-weight:bold;border-bottom:1px dotted red;" title="Missing keybind - Add \''..key..'\' to local replacements in [Module:Lang]">['..key..']</span>' | |||
end | |||
end) | |||
return frame:preprocess(text) | |||
end | |||
local | function p.get_string(key, lang_code_override, fallback_str, remove_var_index, item_name) | ||
if | -- Get frame object (either passed directly or via first argument) | ||
local frame | |||
if type(key) == "table" and key.args then | |||
frame = key | |||
key = frame.args[1] | |||
lang_code_override = frame.args["lang_code_override"] | |||
fallback_str = frame.args["fallback_str"] | |||
remove_var_index = frame.args["remove_var_index"] | |||
item_name = frame.args["item_name"] | |||
else | |||
frame = mw.getCurrentFrame() | |||
end | end | ||
local | -- Determine lang_code if not overridden | ||
if | local lang_code = lang_code_override | ||
if (lang_code == '' or lang_code == nil) then | |||
lang_code = p.get_lang_code() | |||
end | end | ||
-- Retrieve lang data | |||
local data = get_lang_file(lang_code) | |||
if (data == nil) then | |||
return string.format("Lang code '%s' does not have a json file", lang_code) | |||
end | end | ||
-- Localize | |||
local label = data[KEY_OVERRIDES[key] or key] | |||
-- Some labels use key with "_postvalue_label" instead, so check this if previous search failed | |||
if label == nil and key ~= nil and key:sub(-6) == '_label' then | |||
local postvalue_key = key:sub(1, -7) .. '_postvalue_label' | |||
if | label = data[postvalue_key] | ||
end | |||
if (label == nil) then | |||
-- | -- Apply fallback (without HTML processing for fallback) | ||
local fallback_tooltip = frame:expandTemplate{title = "MissingValveTranslationTooltip"} | |||
local fallback | |||
if (fallback_str == 'en') then | |||
fallback = p.get_string(key, 'en', key .. fallback_tooltip, remove_var_index, item_name) | |||
elseif fallback_str == 'dictionary' then | |||
return dictionary_module.translate(key, lang_code_override) | |||
elseif fallback_str ~= nil then | |||
fallback = fallback_str | |||
else | |||
return '' | |||
end | end | ||
return fallback .. fallback_tooltip | |||
end | end | ||
-- | -- Always retrieve the last parameter if string contains "|" | ||
if | if type(label) == "string" and label:find("|") then | ||
local | local parts = mw.text.split(label, "|") | ||
label = parts[#parts] | |||
end | |||
-- Remove hashtags | |||
if type(label) == "string" and label:find("#") then | |||
local parts = mw.text.split(label, "#") | |||
label = parts[#parts] | |||
end | end | ||
if | -- Apply remove_var | ||
if (remove_var_index ~= nil) then | |||
label = util_module.remove_var(label, remove_var_index) | |||
end | end | ||
local | -- Process variables if item_name is provided | ||
if item_name and item_name ~= '' then | |||
local item_data_module = require('Module:ItemData') | |||
label = label:gsub("{s:([^}]+)}", function(variable_name) | |||
-- Get the value from ItemData | |||
local value = item_data_module.get_prop({args = {item_name, variable_name}}) | |||
end | |||
-- If value exists, remove non-number symbols (but keep + and -) | |||
if value then | |||
value = value:gsub("[^0-9+-]", "") | |||
-- Return empty string if nothing left, otherwise return the filtered value | |||
return value ~= "" and value or variable_name | |||
else | |||
return variable_name -- Fallback to variable name if value not found | |||
end | |||
end) | |||
end | end | ||
local | |||
return | -- Process HTML and return as "raw" HTML | ||
label = process_newlines(label) | |||
label = process_html_tags(label, frame) | |||
-- Create HTML object for safe output | |||
local html = mw.html.create() | |||
html:wikitext(label) | |||
return tostring(html) | |||
end | end | ||
-- = | -- Search for a localized string using its English label | ||
p.search_string = function(frame) | |||
local label = frame.args[1] | |||
local lang_code_override = frame.args[2] | |||
local result = p._search_string(label, lang_code_override) | |||
local | result = process_newlines(result) | ||
return result | |||
end | |||
-- search_string, but for internal use by other modules | |||
p._search_string = function(label, lang_code_override) | |||
lang_code = lang_code_override | |||
if (lang_code == '' or lang_code == nil) then | |||
lang_code = p.get_lang_code() | |||
end | end | ||
-- Load the language files | |||
local data_en = get_lang_file('en') -- English data | |||
local data_lang = get_lang_file(lang_code) -- Target language data | |||
if (data_lang == nil) then | |||
error("Lang code '%s' does not have a json file", lang_code) | |||
end | end | ||
-- | -- Search for the key in the English data | ||
local | local key = nil | ||
for k, v in pairs(data_en) do | |||
if v == label then | |||
key = k -- Find the key corresponding to the label | |||
break | |||
end | end | ||
end | end | ||
-- | -- Default to input label if localized string is not found | ||
if (key == nil) then | |||
return label | |||
return | |||
end | end | ||
if (data_lang[key] == nil) then | |||
return label | |||
end | end | ||
return data_lang[key] | |||
end | |||
p.get_lang_code = function() | |||
local title = mw.title.getCurrentTitle() | |||
local lang_code = title.fullText:match(".*/(.*)$") | |||
if lang_code == nil or lang_codes_set[lang_code] == nil then | |||
return 'en' | |||
end | end | ||
return lang_code | |||
return | |||
end | end | ||
return p | return p | ||