Simplfiy LangSelector (#1226)

## Description:

Simplify how translations work. 

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
Scott Anderson
2025-06-19 16:01:51 -04:00
committed by GitHub
parent 2dad1dc9a6
commit 5895aa7882
+50 -47
View File
@@ -26,8 +26,8 @@ import uk from "../../resources/lang/uk.json";
@customElement("lang-selector")
export class LangSelector extends LitElement {
@state() public translations: any = {};
@state() private defaultTranslations: any = {};
@state() public translations: Record<string, string> | undefined;
@state() private defaultTranslations: Record<string, string> | undefined;
@state() private currentLang: string = "en";
@state() private languageList: any[] = [];
@state() private showModal: boolean = false;
@@ -91,16 +91,18 @@ export class LangSelector extends LitElement {
const savedLang = localStorage.getItem("lang");
const userLang = this.getClosestSupportedLang(savedLang || browserLocale);
this.defaultTranslations = await this.loadLanguage("en");
this.translations = await this.loadLanguage(userLang);
this.defaultTranslations = this.loadLanguage("en");
this.translations = this.loadLanguage(userLang);
this.currentLang = userLang;
await this.loadLanguageList();
this.applyTranslation(this.translations);
this.applyTranslation();
}
private async loadLanguage(lang: string): Promise<any> {
return Promise.resolve(this.languageMap[lang] || {});
private loadLanguage(lang: string): Record<string, string> {
const language = this.languageMap[lang] || {};
const flat = flattenTranslations(language);
return flat;
}
private async loadLanguageList() {
@@ -166,15 +168,15 @@ export class LangSelector extends LitElement {
}
}
private async changeLanguage(lang: string) {
private changeLanguage(lang: string) {
localStorage.setItem("lang", lang);
this.translations = await this.loadLanguage(lang);
this.translations = this.loadLanguage(lang);
this.currentLang = lang;
this.applyTranslation(this.translations);
this.applyTranslation();
this.showModal = false;
}
private applyTranslation(translations: any) {
private applyTranslation() {
const components = [
"single-player-modal",
"host-lobby-modal",
@@ -195,32 +197,17 @@ export class LangSelector extends LitElement {
"o-button",
];
document.title = translations.main?.title || document.title;
document.title = this.translateText("main.title") ?? document.title;
document.querySelectorAll("[data-i18n]").forEach((element) => {
const key = element.getAttribute("data-i18n");
const keys = key?.split(".") || [];
let text = translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
}
if (!text && this.defaultTranslations) {
let fallback = this.defaultTranslations;
for (const k of keys) {
fallback = fallback?.[k];
if (!fallback) break;
}
text = fallback;
}
if (text) {
element.innerHTML = text;
} else {
if (key === null) return;
const text = this.translateText(key);
if (text === null) {
console.warn(`Translation key not found: ${key}`);
return;
}
element.textContent = text;
});
components.forEach((tag) => {
@@ -236,23 +223,18 @@ export class LangSelector extends LitElement {
key: string,
params: Record<string, string | number> = {},
): string {
const keys = key.split(".");
let text: any = this.translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
let text: string | undefined;
if (this.translations && key in this.translations) {
text = this.translations[key];
} else if (this.defaultTranslations && key in this.defaultTranslations) {
text = this.defaultTranslations[key];
} else {
console.warn(`Translation key not found: ${key}`);
return key;
}
if (!text && this.defaultTranslations) {
text = this.defaultTranslations;
for (const k of keys) {
text = text?.[k];
if (!text) return key;
}
}
for (const [param, value] of Object.entries(params)) {
for (const param in params) {
const value = params[param];
text = text.replace(`{${param}}`, String(value));
}
@@ -309,3 +291,24 @@ export class LangSelector extends LitElement {
`;
}
}
function flattenTranslations(
obj: Record<string, any>,
parentKey = "",
result: Record<string, string> = {},
): Record<string, string> {
for (const key in obj) {
const value = obj[key];
const fullKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === "string") {
result[fullKey] = value;
} else if (value && typeof value === "object" && !Array.isArray(value)) {
flattenTranslations(value, fullKey, result);
} else {
console.warn("Unknown type", typeof value, value);
}
}
return result;
}