Skip to content

Localization: wrap remaining untranslated strings with L() - #1170

Merged
EllesmereGaming merged 10 commits into
EllesmereGaming:mainfrom
labrie75:Localization-wrap-remaining-untranslated-strings-with-L()
Aug 3, 2026
Merged

Localization: wrap remaining untranslated strings with L()#1170
EllesmereGaming merged 10 commits into
EllesmereGaming:mainfrom
labrie75:Localization-wrap-remaining-untranslated-strings-with-L()

Conversation

@labrie75

@labrie75 labrie75 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

A handful of user-facing strings never reach L(). Some are raw literals passed
straight to SetText, others are sentences assembled at runtime with .. or
format(). They stay English on every client no matter what locale is loaded.

Most of them already have translations in the locale files -- the source just
never looks them up. Locales/koKR.lua (and deDE, frFR) already ship:

L["Unlock Mode is where you can adjust\npositioning for all the elements of EllesmereUI"] = ...
L["ADD NEW"] = ...
L["(middle click to remove)"] = ...
L["No death recap available"] = ...
L["Detailed information is\nsecret while in combat"] = ...

Translators did that work and it has been sitting unused. Wrapping the call sites
turns it on for every language at once.

The clearest cases are inconsistencies inside a single file -- same helper,
same if/else, one branch wrapped and the other not:

Wrapped already Not wrapped (this PR)
EUI__General_Options.lua:3845 return EllesmereUI.L("Not Bound") EUI_PartyMode_Options.lua:101 return "Not Bound"
EUI_ActionBars_Options.lua:1961 same EUI_DamageMeters_Options.lua:266 same
EllesmereUIDamageMeters.lua:1757 ApplyTTHeader(..., L("Damage Taken")) :1691 ApplyTTHeader(..., "Death Recap")
EUI_RaidFrames_BuffManager.lua:4870 EllesmereUI.L(typeName) :4868 titleFS:SetText(ind.name)
EUI_RaidFrames_BuffManager.lua:6160 EllesmereUI.L("(no spells)") :6150 "(" .. ind.name .. ")"
EUI_RaidFrames_ManagerPages.lua:541 return L("No filters routed") :538 names[#names+1] = CAT_VALUES[cat]

Runtime-assembled keys become positional formats. The window title built its
own lookup key by concatenation:

-- before
local titlePrefix = isOverall and "Overall " or ""
W._fullTitle = L(titlePrefix .. (DM_TYPE_NAMES[W.curDMType] or "Damage Done"))
-- after
local typeName = L(DM_TYPE_NAMES[W.curDMType] or "Damage Done")
W._fullTitle = isOverall and EllesmereUI.Lf("Overall %1$s", typeName) or typeName

A locale previously had to ship "Overall Damage Done", "Overall Healing Done",
and so on -- one entry per meter type. Overall %1$s replaces all of them. Same
story for %1$s's %2$s Breakdown (nine combinations), %1$s's Death Recap, and
You may only have %1$d windows active. Word order differs per language, so these
have to be positional rather than plain %s.

One string had English grammar baked into it. The Mythic Timer death counter
appended an English plural s at runtime, and called format() directly so it
never reached L() at all:

-- before
f._deathFS:SetText(format("%d Death%s  -%s",
    run.deaths, run.deaths ~= 1 and "s" or "", FormatTime(run.deathTimeLost)))
-- after
f._deathFS:SetText(run.deaths == 1
    and EllesmereUI.Lf("%1$d Death  -%2$s", run.deaths, FormatTime(run.deathTimeLost))
    or EllesmereUI.Lf("%1$d Deaths  -%2$s", run.deaths, FormatTime(run.deathTimeLost)))

Korean and Chinese have no plural form and Russian has three, so no locale could
express this. Two complete keys let each one handle it its own way.

Files touched

File Lines What
EllesmereUI/EllesmereUI_FirstInstall.lua 8 First-run popup was fully English. 304/333 wrap the loop variables, covering 3 group headers + 17 addon entries
EllesmereUI/EllesmereUI.lua 1 Unlock Mode hint
EllesmereUI/EUI_PartyMode_Options.lua 1 "Not Bound"
EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua 14 Detail tooltip (all 4 states), + ADD NEW row, meter type menu, window title, window-limit tooltip
EllesmereUIDamageMeters/EUI_DamageMeters_Options.lua 1 "Not Bound"
EllesmereUIMythicTimer/EllesmereUIMythicTimer.lua 1 Death counter
EllesmereUIRaidFrames/EUI_RaidFrames_ManagerPages.lua 1 Filter category names
EllesmereUIRaidFrames/EUI_RaidFrames_BuffManager.lua 2 Indicator tile name + spell list line

Locales/_keys.txt regenerated, 649 -> 666. Variable wraps (L(group.header),
L(ind.name), L(CAT_VALUES[cat]), L(n)) add nothing, as expected.

Locales/koKR.lua gains the new format keys and fills in entries that were
correctly wrapped in source but had no Korean. Four Korean entries that only
existed to patch the runtime-assembled keys are dropped -- they are unreachable
once the source stops building keys at runtime.

On an enUS client L() returns the key unchanged, so nothing changes visually.

Deliberately left alone

  • Strings passed to EUI.ShowWidgetTooltip(...) and EllesmereUI:ShowConfirmPopup(...)
    -- both already call EllesmereUI.L() on their arguments internally
    (EllesmereUI_Widgets.lua:1759, EllesmereUI.lua:6344), so wrapping at the call
    site would double-wrap. The window-limit tooltip is the one exception: it hands
    over an already-assembled sentence, so it needs Lf() before the call.
  • DM_TYPE_NAMES / ICON_STYLE_VALUES and similar table literals -- they are
    resolved through L() at use time; wrapping the table would freeze the value at
    load time.
  • Fallbacks behind issecretvalue() guards ("Unknown", "Heal", "Melee") and
    the unreachable or "You" in EllesmereUIDamageMeters.lua:3564/:3568
    (StripRealm never returns nil). Happy to cover them in a follow-up.
  • "+" and other symbol-only literals.

How was it tested?

Tested in game on live retail with a koKR client: the first-run popup, unlock mode
hint, meter home grid, detail tooltip in all four states (breakdown / death recap /
no recap / in combat), meter type menu, window title including the Overall prefix,
the Mythic Timer death counter, and the Not Bound key fields all render translated,
with no load errors and no layout changes.

The three Raid Frames changes live in the 12.1 manager UI (EUI_RaidFrames_ManagerPages.lua
self-gates on EllesmereUI.IS_121), so those were verified separately on the 12.1
PTR: indicator tile names and their spell list lines render translated.

Also re-ran the key extractor to confirm _keys.txt matches, and grepped for
double-wrapped tooltip calls (ShowWidgetTooltip(..., EllesmereUI.L(...))) -- none.

Screenshots

First-run popup:
image

Damage meter:
image
image
image

Checklist

  • New settings default OFF -- n/a, this PR adds no setting; pure localization with no behavior change (on enUS the rendered text is identical)
  • Zero cost while disabled -- n/a; no events, timers or frames added
  • Cheap while enabled -- one table lookup per string, at the point it is already being drawn
  • No writes onto Blizzard-owned frames -- only sets text on EllesmereUI's own FontStrings
  • Tested in-game on live retail; the 12.1-gated Raid Frames lines tested on 12.1 PTR; no load errors

@EllesmereGaming
EllesmereGaming merged commit e72e30a into EllesmereGaming:main Aug 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants