Editing Module:Sandbox/LVLGive feedback
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', | |||
MoveSlowDuration_postfix = 'EnemyMoveSlowDuration_postfix', | |||
} | |||
local | function get_lang_file(lang_code) | ||
local file_name = string.format("Data:Lang_%s.json", lang_code) | |||
local success, data = pcall(mw.loadJsonData, file_name) | |||
if success then | |||
return | return data | ||
else | |||
return | return nil | ||
end | end | ||
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 = { | |||
BonusFireRate = {icon='{{Icon/Brown|[[File:Fire_Rate.png|link=Fire Rate|20px]]}}', label_color='#ffefd7', link='Fire Rate'}, | |||
BonusMoveSpeed = {icon='{{Icon/Green|[[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'}, | |||
BonusSpiritDamage = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
BonusWeaponDamage = {icon='{{Icon/NoColor|[[File:Bullet Damage.png|link=Weapon Damage|20px]]}}', label_color='#ec981a', link='Weapon Damage'}, | |||
bullet_armor_up = {icon='{{Icon/White|[[File:Barrier.png|link=Barrier|20px]]}}', label_color='#ffefd7', link='Barrier'}, | |||
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'}, | |||
cast = {icon='{{Icon/White|[[File:Cast.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
CombatBarrier = {icon='{{Icon/White|[[File:Bullet Armor (item).png|link=Damage_Resistance|20px]]}}', label_color='#ffefd7', link='Barrier'}, | |||
cooldown = {icon='{{Icon/White|[[File:AttributeIconTechDuration.png|link=Ability Cooldown|20px]]}}', label_color='#ffefd7', link='Ability Cooldown'}, | |||
damage = {icon='{{Icon/NoColor|[[File:Damage heart.png|link=Damage|20px]]}}', label_color='#ec981a', link='Damage'}, | |||
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'}, | |||
Heal = {icon='{{Icon/Green|[[File:Healing.png|link=Health_Regen|20px]]}}', label_color='#13f278', link='Health_Regen'}, | |||
Healing = {icon='{{Icon/Green|[[File:Healing.png|link=Healing|20px]]}}', label_color='#13f278', link='Healing'}, | |||
Immobilize = {icon='{{Icon/White|[[File:Immobilize.png|link=Status Effects#Immobilized|20px]]}}', label_color='#ffefd7', link='Status Effects#Immobilized'}, | |||
invisible = {icon='{{Icon/White|[[File:Invisible.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
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'}, | |||
MaxHealth = {icon='{{Icon/Green|[[File:Healing.png|link=Health|20px]]}}', label_color='#13f278', link='Health'}, | |||
MeleeDamage = {icon='{{Icon/Brown|[[File:Melee damage.png|link=Melee Damage|20px]]}}', label_color='#ec981a', link='Melee Damage'}, | |||
MoveSpeed = {icon='{{Icon/Green|[[File:Move_speed.png|link=Move speed|20px]]}}', label_color='#ffefd7', link='Move speed'}, | |||
move_speed = {icon='{{Icon/Green|[[File:Move_speed.png|link=Move speed|20px]]}}', label_color='#ffefd7', link='Move speed'}, | |||
Pull = {icon='{{Icon/NoColor|[[File:Displacement.png|link=|20px]]}}', label_color='#ffefd7', link='Status_Effects'}, | |||
Pulling = {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'}, | |||
ReducedFireRate = {icon='{{Icon/Brown|[[File:Fire_Rate.png|link=Fire Rate|20px]]}}', label_color='#ffefd7', link='Fire Rate'}, | |||
Regen = {icon='{{Icon/Green|[[File:Healing.png|link=Health_Regen|20px]]}}', label_color='#13f278', link='Health_Regen'}, | |||
revealed = {icon='{{Icon/White|[[File:Revealed.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'}, | |||
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'}, | |||
Spirit = {icon='{{Icon/NoColor|[[File:Spirit_icon.png|link=Spirit Power|20px]]}}', label_color='#bc8ee8', link='Spirit Power'}, | |||
SpiritDamage = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
SpiritDPS = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
SpiritIcon = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', link='Spirit Damage'}, | |||
SpiritResist = {icon='{{Icon/Purple|[[File:Spirit_Armor.png|link=Damage_Resistance|20px]]}}', label_color='#bc8ee8', link='Damage_Resistance'}, | |||
StatDesc_TechPower = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', label_color='#bc8ee8', link='Spirit Damage'}, | |||
Stun = {icon='{{Icon/White|[[File:Status_Stun.png|link=Stun|20px]]}}', link='Stun'}, | |||
stun = {icon='{{Icon/White|[[File:Stun.png|link=|20px]]}}', label_color='#ffefd7', link=''}, | |||
tech_armor_up = {icon='{{Icon/White|[[File:Barrier.png|link=Barrier|20px]]}}', label_color='#ffefd7', link='Barrier'}, | |||
tech_damage = {icon='{{Icon/NoColor|[[File:AttributeIconTechShieldHealth.png|link=Spirit Damage|12px]]}}', link='Spirit Damage'}, | |||
WeaponDamage = {icon='{{Icon/NoColor|[[File:Bullet Damage.png|link=Weapon Damage|20px]]}}', label_color='#ec981a', link='Weapon Damage'}, | |||
} | |||
-- 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 = { | |||
-- Movement | |||
MoveForward = "'''Move Forward button'''", | |||
MoveBackwards = "'''Move Backward button'''", | |||
MoveLeft = "'''Move Left button'''", | |||
MoveRight = "'''Move Right button'''", | |||
Mantle = "'''Jump button'''", | |||
Zipline = "'''Zipline button'''", | |||
Roll = "'''Dash button'''", | |||
Crouch = "'''Crouch button'''", | |||
MoveDown = "'''Fly Down button'''", | |||
MoveUp = "'''Fly Up button'''", | |||
-- Combat | |||
Attack = "'''Fire button'''", | |||
ADS = "'''Alt-Fire button'''", | |||
Reload = "'''Reload button'''", | |||
AbilityMelee = "'''Melee button'''", | |||
HeldItem = "'''Melee Parry/Throw Held Item button'''", | |||
-- Abilities | |||
Ability1 = "'''Ability 1 button'''", | |||
Ability2 = "'''Ability 2 button'''", | |||
Ability3 = "'''Ability 3 button'''", | |||
Ability4 = "'''Ability 4 button'''", | |||
AltCast = "'''Alternate Cast'''", | |||
-- UI & Menus | |||
OpenHeroSheet = "'''Open Shop button'''", | |||
PurchaseQuickbuy = "'''Purchase Quickbuy button'''", | |||
Scoreboard = "'''Scoreboard button'''", | |||
OpenMainMenu = "'''Open Main Menu button'''", | |||
-- Communication | |||
PushToTalk = "'''Voice Chat button'''", | |||
local | -- Misc & Hideout | ||
Pause = "'''Pause button'''", | |||
ReplayDeath = "'''Replay Death button'''", | |||
HideoutUse = "'''Interact with an object in the Hideout button'''", | |||
HideoutCancelMatchmaking = "'''Cancel Matchmaking button'''", | |||
} | |||
-- 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) | |||
-- Force spaces around keybinds where the source data omits them | |||
text = text:gsub("'''([a-zA-Z])", "''' %1") | |||
-- Clean up extra spaces between a keybind and following punctuation | |||
text = text:gsub("'''%s+:", "''':") | |||
return frame:preprocess(text) | |||
end | end | ||
function p.get_string(key, lang_code_override, fallback_str, remove_var_index, item_name) | |||
-- 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 | ||
-- | -- Determine lang_code if not overridden | ||
local lang_code = lang_code_override | |||
if (lang_code == '' or lang_code == nil) then | |||
lang_code = p.get_lang_code() | |||
end | end | ||
if | -- Retrieve lang data | ||
return | 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 | |||
local | if label == nil and key ~= nil and key:sub(-6) == '_label' then | ||
local postvalue_key = key:sub(1, -7) .. '_postvalue_label' | |||
label = data[postvalue_key] | |||
end | |||
if (label == nil) then | |||
-- Apply fallback (without HTML processing for fallback) | |||
local fallback_tooltip = frame and frame:expandTemplate{title = "MissingValveTranslationTooltip"} | |||
or '<span title="This text is missing a Valve translation" style="border-bottom:1px dotted red;">?</span>' | |||
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 | |||
-- Always retrieve the last parameter if string contains "|" | |||
if type(label) == "string" and label:find("|") then | |||
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 | ||
-- 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}}) | |||
-- 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 | else | ||
return variable_name -- Fallback to variable name if value not found | |||
end | end | ||
end) | |||
end | |||
-- Process HTML and return as "raw" HTML | |||
label = process_newlines(label) | |||
label = process_html_tags(label, frame) | |||
return label | |||
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) | |||
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 | |||
-- 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 | ||