Skip to content

Conversation

@dodoels
Copy link

@dodoels dodoels commented Apr 28, 2025

🌍 Full i18n Support

image
image
image
image

Summary

This PR introduces full internationalization (i18n) support, with French (FR) added as a second-language example. The i18n implementation is fully integrated with the current UI and production-ready.


1. 🧱 Introducing the i18n Framework

image

Following the design proposed earlier, this PR completes the green-box portion in the diagram — enabling robust i18n support for the UI.

What’s included:

  • Refactored existing language logic to use the new centralized framework.
  • Added /ui/i18n/, which contains all configuration and utility functions for i18n.
  • Locales are stored in /assets/locale/ using a standardized JSON format, which can be reused by the Golang backend.
  • All i18n development guidelines and documentation are now available under /docs/. The main README.md has been simplified and now links to the relevant sections.

2. 🌐 What’s Been Localized

  • Language toggle implemented, with French (FR) added as a working second language.
  • Fallback mechanism: If a key is missing in the selected locale, it will gracefully fall back to English (EN), which is the source of truth.
  • Localized the landing page (index.html) and simulator/spec page (index_template.html).
  • All tab titles (Gear, Settings, Talents, etc.) have been localized.
  • The sidebar, including the header, buttons, and stat labels, is fully localized.

This implementation serves as a working example to guide future localization work.


3. 🧩 Design Considerations

🗂️ Locale JSON Structure

Localization files are modularized by UI section to improve clarity and maintainability:

landing: for index.html
sim: for index_template.html
sidebar: includes header, buttons, and stats
common: shared labels like stats, classes, specs, etc.
gear
setting
talents
rotation
results
import
export

Benefit: Clear structure for contributors to locate and manage translations efficiently.


🔁 Enum Mapping & Safe Translations

Many internal enums like Strength or Agility are used as UI text, keys, and logic values. Direct translation could break core functionality.

Solution:

  • entity_mapping.ts: Maps raw Go-enum values to stable English keys.
  • translation.ts: Maps those keys to localized strings from the locale files.

This separation ensures translation logic is safe, extensible, and won’t interfere with the sim's logic or state management.


@dodoels dodoels changed the title [i18n] Part2. add localization service, home page localization with EN and FR [i18n] Part2. Full i18n Support & Landing Page i18n (EN/FR) Apr 29, 2025
@dodoels dodoels marked this pull request as ready for review April 29, 2025 01:39
@1337LutZ
Copy link

The NaN / 0 is a WoWhead issue, can replicate it on their site as well.

@1337LutZ
Copy link

@dodoels can you update your branch from current master

@dodoels
Copy link
Author

dodoels commented May 1, 2025

@1337LutZ please review when you get chance, thanks!

@1337LutZ
Copy link

1337LutZ commented May 1, 2025

@1337LutZ please review when you get chance, thanks!

I think there were some more changes made that you are missing and need to merge in. The test is failing on a Frontend warnign that has been resolved. Sorry!

@kayla-glick
Copy link

@1337LutZ can you send the page that you tested with on wowhead? Trying to pass it along to their devs

@dodoels dodoels changed the title [i18n] Full i18n Support: examples of landing page [i18n] Full i18n Support Jul 4, 2025
[i18n] Full i18n Support: restructure and some redesign, and more examples on the UI
@dodoels
Copy link
Author

dodoels commented Jul 4, 2025

@kayla-glick @1337LutZ @dodoels I believe this MR has very big value for us as there are a lot of people that could benefit from the wowhead items being in their language (I was surprised how many people play the game in their native languages).

Is it possible we come back to this and try to get it in a mergable state?

Updated the branch with the latest changes. This should now be a good MVP to be included in the release.

}
},
"common": {
"phases": {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If phases are just going to be a number then we actually dont need this right? We can just use the browser's Int.NumberFormat. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat.

If we use this entry I would suggest that we actually have this for example:

  • "1": "Phase 1 (T14)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

resolved

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh that's where you use it. We also use it in the Filter dropdown (I thought that's where you wanted this).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which filter are you referring? xD

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Phases filter in the Gear picker popup

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that will be later, I am doing BFS now and making sure all first level UI are localized

const statName = unitStat.getShortName(player.getClass());
let statName: string;
if (unitStat.isStat()) {
statName = translateStat(unitStat.getStat());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that we don't translate the stat names directly? We have getFullName and getShortName so this should already be possible. We can then get the translation in these functions.

Both of these use: getStatName and getClassPseudoStatName, so moving the stat translations to over there should resolve any manual interaction needed here.

Copy link
Author

@dodoels dodoels Jul 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getStatName and getClassPseudoStatName returns the enums generated from protobuf which is a more rebust solution.

Stat.StatStrength <-- get the stat enum
[Stat.StatStrength]: 'strength' <-- find the translation key
en[common.stat.strength] <-- find the actual translated text

if we go with direct translation from the short names..

Spell Crit <-- spell name in string
spell_crit <-- parse into translation key
en[common.stat.spell_crit] <-- find the actual translated text

Copy link
Author

@dodoels dodoels Jul 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be specific, enums to string is more robust than string to parsed string to string which requires more manual works

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I mean move the translateStat into the getFullName and getShortName. That way when you use those 2 functions, it's always localized and the translateStat is contained to only there

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved, but not sure if this is still revelant

// Not sure if this is needed anymore, but keeping it for now
if (playerClass == Class.ClassHunter) {
	return fullName.replace('Physical', 'Ranged');
} else {
	return fullName.replace('Physical', 'Melee');
}

I left it as it is for now

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is still relevant! :D Hunter still has Ranger AP specific buffs

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in e7f267c

window.location.reload();
};
li.appendChild(a);
dropdownMenu.appendChild(li);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change this to a localization.tsx file, we can nicely use JSX to build this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some functions in localization.ts are being imported into index.html, let me try if jsx can be resolved in the build

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, but we can still write this in a JSX syntax and append it to the DOM

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

Copy link

@1337LutZ 1337LutZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments!

@dodoels
Copy link
Author

dodoels commented Jul 5, 2025

@1337LutZ Addressed all your comments. Please checkout the branch and play with it see if any further issue. I will be afk until August, if this is not mergeble I will have to come back later - Otherwise, please feel free to merge it!

image

@dodoels dodoels requested a review from 1337LutZ July 5, 2025 02:51
@github-actions github-actions bot added the Druid label Jul 12, 2025
@kayla-glick kayla-glick merged commit aa01f34 into wowsims:master Jul 12, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants