import type { Currency } from '../enums/currency'; import PowerduckState from '../../app/powerduck-state'; import LanguageTranslator from '../enum-translation/language-translator'; import { Language } from '../enums/language'; import { sortBy } from '../extensions/array-extensions'; export class LanguageUtils { static supportedLanguages: Language[] = [ Language.sk, Language.en, ]; private static cachedLanguageList: LanguageUtils.LanguageListItem[] | null = null; private static readonly currencyMap: Record = { PLN: 'zł', CZK: 'kč', EUR: '€', HUF: 'Ft', }; private static readonly currencyRegex = new RegExp(Object.keys(LanguageUtils.currencyMap).join('|'), 'g'); private static getAdjectiveName(name: string): string { if (name.endsWith('ý')) { name = `${name.substring(0, name.length - 1)}y`; } return name; } private static normalizeCurrency(value: string): string { return value.replace(LanguageUtils.currencyRegex, match => LanguageUtils.currencyMap[match]); } static getLanguageList(sort?: boolean): Array { if (LanguageUtils.cachedLanguageList) { return LanguageUtils.cachedLanguageList; } const retVal: Array = []; const getLanguage = (langCode: Language): LanguageUtils.LanguageListItem => { const newOption = { id: langCode, text: LanguageUtils.getAdjectiveName(LanguageTranslator.getString(langCode)), value: langCode.toString(), flag: null, }; switch (langCode) { case 'sk': newOption.flag = 'svk'; break; case 'en': newOption.flag = 'gbr'; break; case 'cs': newOption.flag = 'cze'; break; case 'de': newOption.flag = 'ger'; break; case 'pl': newOption.flag = 'pol'; break; case 'it': newOption.flag = 'ita'; break; case 'hu': newOption.flag = 'hun'; break; default: break; } return newOption; }; for (const langCode of [ Language.sk, Language.en, Language.cs, Language, Language.pl, Language.it, Language.hu, Language.de, ]) { if (LanguageUtils.supportedLanguages.includes(langCode as any)) { retVal.push(getLanguage(langCode as any)); } } if (sort === true) { try { retVal.sort((a, b) => a.text.localeCompare(b.text)); } catch (e) { retVal[sortBy](p => p.text); } } LanguageUtils.cachedLanguageList = retVal; return retVal; } /** * Returns enum value from string language code * * @param langCode 2-letter language code [sk, en, cs...] */ static getLanguageEnum(langCode: string): Language { return (langCode as any) ?? Language.en; } static getLanguageFlagUrl(languageFlag: string, svg?: boolean): string { if (!svg) { return `${PowerduckState.getAssetRootDirectory()}/img/flags/${languageFlag}.png`; } else { return `${PowerduckState.getAssetRootDirectory()}/img/flags/svg/${languageFlag}.svg`; } } static floatToCurrency( value: number, currency: string | Currency | number | any, killZeroCents?: boolean, ): string { if (value == null) { value = 0; } let currencyISO: string; if (currency == null || currency == '') { currencyISO = '?'; } else { currencyISO = currency as any; } if (killZeroCents == null) { killZeroCents = true; } const rounded = Math.round(value * 100) / 100; const hasCents = Math.round(rounded * 100) % 100 !== 0; // Drop the cents only when they are zero. Using fraction-digit options // instead of stripping ".00"/",00" from the formatted string, which // breaks in locales where the thousands separator is "," (e.g. en: // "CZK 1,000.00" -> "CZK 10"). const fractionDigits = (killZeroCents && !hasCents) ? 0 : 2; try { return LanguageUtils.normalizeCurrency(rounded.toLocaleString(PowerduckState.getCurrentLanguage(), { style: 'currency', currency: currencyISO, minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits, })); } catch (e) { const text = fractionDigits === 0 ? Math.round(rounded).toString() : rounded.toFixed(2); return `${text} ${currencyISO}`; } } static negateString(str: string): string { return PowerduckState.getResourceValue('negationBase') + str.toLowerCase(); } } // Type-only namespace merge so `LanguageUtils.LanguageListItem` keeps working // for callers that use it as a type annotation. Only declares an interface — // no runtime members, no `let`/`var`/`function` exports, so Oxc accepts it. // eslint-disable-next-line ts/no-namespace export namespace LanguageUtils { export interface LanguageListItem { text: string; value: string; flag: string; id: Language; } }