Files
armorpaint/base/Sources/Translator.ts
T
luboslenco 747806eaa3 Cleanup
2024-01-28 12:21:16 +01:00

155 lines
5.1 KiB
TypeScript

class Translator {
static translations: Map<string, string> = new Map();
// The font index is a value specific to font_cjk.ttc.
static cjkFontIndices: Map<string, i32> = new Map([
["ja", 0],
["ko", 1],
["zh_cn", 2],
["zh_tw", 3],
["zh_tw.big5", 4]
]);
static lastLocale = "en";
// Mark strings as localizable in order to be parsed by the extract_locale script.
// The string will not be translated to the currently selected locale though.
static _tr = (s: string) => {
return s;
}
// Localizes a string with the given placeholders replaced (format is `{placeholderName}`).
// If the string isn't available in the translation, this method will return the source English string.
static tr = (id: string, vars: Map<string, string> = null): string => {
let translation = id;
// English is the source language
if (Config.raw.locale != "en" && Translator.translations.has(id)) {
let atranslations = Translator.translations as any;
translation = atranslations[id];
}
if (vars != null) {
for (let [key, value] of vars) {
translation = translation.replaceAll(`{${key}}`, String(value));
}
}
return translation;
}
// (Re)loads translations for the specified locale
static loadTranslations = (newLocale: string) => {
if (newLocale == "system") {
Config.raw.locale = Krom.language();
}
// Check whether the requested or detected locale is available
if (Config.raw.locale != "en" && Translator.getSupportedLocales().indexOf(Config.raw.locale) == -1) {
// Fall back to English
Config.raw.locale = "en";
}
// No translations to load, as source strings are in English
// Clear existing translations if switching languages at runtime
Translator.translations.clear();
if (Config.raw.locale == "en" && Translator.lastLocale == "en") {
// No need to generate extended font atlas for English locale
return;
}
Translator.lastLocale = Config.raw.locale;
if (Config.raw.locale != "en") {
// Load the translation file
let translationJson = System.bufferToString(Krom.loadBlob(`data/locale/${Config.raw.locale}.json`));
let data = JSON.parse(translationJson);
for (let field in data) {
let atranslations = Translator.translations as any;
atranslations[field] = data[field];
}
}
// Generate extended font atlas
Translator.extendedGlyphs();
// Push additional char codes contained in translation file
let cjk = false;
for (let s of Translator.translations.values()) {
for (let i = 0; i < s.length; ++i) {
// Assume cjk in the > 1119 range for now
if (s.charCodeAt(i) > 1119 && Graphics2.fontGlyphs.indexOf(s.charCodeAt(i)) == -1) {
if (!cjk) {
Graphics2.fontGlyphs = Graphics2.makeGlyphs(32, 127);
cjk = true;
}
Graphics2.fontGlyphs.push(s.charCodeAt(i));
}
}
}
if (cjk) {
let cjkFontPath = (Path.isProtected() ? Krom.savePath() : "") + "font_cjk.ttc";
let cjkFontDiskPath = (Path.isProtected() ? Krom.savePath() : Path.data() + Path.sep) + "font_cjk.ttc";
if (!File.exists(cjkFontDiskPath)) {
File.download("https://github.com/armory3d/armorbase/raw/main/Assets/common/extra/font_cjk.ttc", cjkFontDiskPath, () => {
if (!File.exists(cjkFontDiskPath)) {
// Fall back to English
Config.raw.locale = "en";
Translator.extendedGlyphs();
Translator.translations.clear();
Translator.initFont(false, "font.ttf", 1.0);
}
else Translator.initFont(true, cjkFontPath, 1.4);
}, 20332392);
}
else Translator.initFont(true, cjkFontPath, 1.4);
}
else Translator.initFont(false, "font.ttf", 1.0);
}
static initFont = (cjk: bool, fontPath: string, fontScale: f32) => {
Graphics2.fontGlyphs.sort((a: i32, b: i32) => { return a - b; });
// Load and assign font with cjk characters
App.notifyOnInit(() => {
Data.getFont(fontPath, (f: Font) => {
if (cjk) {
let acjkFontIndices = Translator.cjkFontIndices as any;
let fontIndex = Translator.cjkFontIndices.has(Config.raw.locale) ? acjkFontIndices[Config.raw.locale] : 0;
f.setFontIndex(fontIndex);
}
Base.font = f;
// Scale up the font size and elements width a bit
Base.theme.FONT_SIZE = Math.floor(Base.defaultFontSize * fontScale);
Base.theme.ELEMENT_W = Math.floor(Base.defaultElementW * (Config.raw.locale != "en" ? 1.4 : 1.0));
let uis = Base.getUIs();
for (let ui of uis) {
ui.setFont(f);
ui.setScale(ui.SCALE());
}
});
});
}
static extendedGlyphs = () => {
// Basic Latin + Latin-1 Supplement + Latin Extended-A
Graphics2.fontGlyphs = Graphics2.makeGlyphs(32, 383);
// + Greek
for (let i = 880; i < 1023; ++i) Graphics2.fontGlyphs.push(i);
// + Cyrillic
for (let i = 1024; i < 1119; ++i) Graphics2.fontGlyphs.push(i);
}
// Returns a list of supported locales (plus English and the automatically detected system locale)
static getSupportedLocales = (): string[] => {
let locales = ["system", "en"];
for (let localeFilename of File.readDirectory(Path.data() + Path.sep + "locale")) {
// Trim the `.json` file extension from file names
locales.push(localeFilename.substr(0, -5));
}
return locales;
}
}