diff --git a/source/graphics/FontManager.cpp b/source/graphics/FontManager.cpp index c1a5321460..d81238c5c9 100644 --- a/source/graphics/FontManager.cpp +++ b/source/graphics/FontManager.cpp @@ -110,11 +110,22 @@ CFontManager::CFontManager() }); } -std::shared_ptr CFontManager::LoadFont(CStrIntern fontName) +std::shared_ptr CFontManager::LoadFont(CStrIntern fontName, CStrIntern locale) { - const std::string locale{g_L10n.GetCurrentLocale() != icu::Locale::getUS() ? g_L10n.GetCurrentLocaleString() : ""}; + const std::string localeToUse{[&] + { + if (!locale.empty()) + return locale.string(); + + if (g_L10n.GetCurrentLocale() == icu::Locale::getUS()) + return std::string{}; + + // Use the current locale, but not US English. + return g_L10n.GetCurrentLocaleString(); + } () + }; const float guiScale{g_ConfigDB.Get("gui.scale", 1.0f)}; - CStrIntern localeFontName{fmt::format("{}{}-{}", locale ,fontName.string(), guiScale)}; + CStrIntern localeFontName{fmt::format("{}{}-{}", localeToUse ,fontName.string(), guiScale)}; FontsMap::iterator it{m_Fonts.find(localeFontName)}; if (it != m_Fonts.end()) @@ -148,13 +159,13 @@ std::shared_ptr CFontManager::LoadFont(CStrIntern fontName) // TODO: explicit Locale like RTL or Arabic fonts. // 1. Locale-specific fonts first - if (!locale.empty()) + if (!localeToUse.empty()) { if (fontSpec.bold) - candidateFonts.push_back(fmt::format("fonts.{}.{}.bold", locale, fontSpec.type)); + candidateFonts.push_back(fmt::format("fonts.{}.{}.bold", localeToUse, fontSpec.type)); if (fontSpec.italic) - candidateFonts.push_back(fmt::format("fonts.{}.{}.italic", locale, fontSpec.type)); - candidateFonts.push_back(fmt::format("fonts.{}.{}.regular", locale, fontSpec.type)); + candidateFonts.push_back(fmt::format("fonts.{}.{}.italic", localeToUse, fontSpec.type)); + candidateFonts.push_back(fmt::format("fonts.{}.{}.regular", localeToUse, fontSpec.type)); } // 2. Then global fonts diff --git a/source/graphics/FontManager.h b/source/graphics/FontManager.h index 6248dc863f..65c5a7ae89 100644 --- a/source/graphics/FontManager.h +++ b/source/graphics/FontManager.h @@ -40,7 +40,7 @@ public: ~CFontManager() = default; NONCOPYABLE(CFontManager); - std::shared_ptr LoadFont(CStrIntern fontName); + std::shared_ptr LoadFont(CStrIntern fontName, CStrIntern locale); void UploadTexturesAtlasToGPU(); private: diff --git a/source/graphics/FontMetrics.cpp b/source/graphics/FontMetrics.cpp index d6739a1f62..91ac545d28 100644 --- a/source/graphics/FontMetrics.cpp +++ b/source/graphics/FontMetrics.cpp @@ -25,8 +25,13 @@ #include "renderer/Renderer.h" CFontMetrics::CFontMetrics(CStrIntern font) + : CFontMetrics(font, CStrIntern()) { - m_Font = g_Renderer.GetFontManager().LoadFont(font); +} + +CFontMetrics::CFontMetrics(CStrIntern font, CStrIntern locale) +{ + m_Font = g_Renderer.GetFontManager().LoadFont(font, locale); } float CFontMetrics::GetHeight() const diff --git a/source/graphics/FontMetrics.h b/source/graphics/FontMetrics.h index f992202cf7..999ed97f4d 100644 --- a/source/graphics/FontMetrics.h +++ b/source/graphics/FontMetrics.h @@ -32,6 +32,7 @@ class CFontMetrics { public: CFontMetrics(CStrIntern font); + CFontMetrics(CStrIntern font, CStrIntern locale); float GetHeight() const; float GetCapHeight(); diff --git a/source/graphics/TextRenderer.cpp b/source/graphics/TextRenderer.cpp index 00dec60103..7ddbeebb5b 100644 --- a/source/graphics/TextRenderer.cpp +++ b/source/graphics/TextRenderer.cpp @@ -55,7 +55,7 @@ CTextRenderer::CTextRenderer() { ResetTranslate(); SetCurrentColor(CColor(1.0f, 1.0f, 1.0f, 1.0f)); - SetCurrentFont(str_sans_10); + SetCurrentFont(str_sans_10, CStrIntern{}); } void CTextRenderer::ResetTranslate(const CVector2D& translate) @@ -84,12 +84,12 @@ void CTextRenderer::SetCurrentColor(const CColor& color) } } -void CTextRenderer::SetCurrentFont(CStrIntern font) +void CTextRenderer::SetCurrentFont(CStrIntern font, CStrIntern locale) { if (font != m_FontName) { m_FontName = font; - m_Font = g_Renderer.GetFontManager().LoadFont(font); + m_Font = g_Renderer.GetFontManager().LoadFont(font, locale); m_Dirty = true; } } diff --git a/source/graphics/TextRenderer.h b/source/graphics/TextRenderer.h index c43e1dd639..7db102e7d8 100644 --- a/source/graphics/TextRenderer.h +++ b/source/graphics/TextRenderer.h @@ -61,7 +61,7 @@ public: /** * Set the font for subsequent print calls. */ - void SetCurrentFont(CStrIntern font); + void SetCurrentFont(CStrIntern font, CStrIntern locale); /** * Print formatted text at (0,0) under the current transform, diff --git a/source/gui/CGUIText.cpp b/source/gui/CGUIText.cpp index 16426bc737..47b987b1c0 100644 --- a/source/gui/CGUIText.cpp +++ b/source/gui/CGUIText.cpp @@ -471,7 +471,7 @@ void CGUIText::Draw(CGUI& pGUI, CCanvas2D& canvas, const CGUIColor& DefaultColor continue; textRenderer.SetCurrentColor(tc.m_UseCustomColor ? tc.m_Color : DefaultColor); - textRenderer.SetCurrentFont(tc.m_Font); + textRenderer.SetCurrentFont(tc.m_Font, tc.m_FontLocale); textRenderer.Put(pos.X + tc.m_Pos.X, pos.Y + tc.m_Pos.Y, &tc.m_String); } diff --git a/source/gui/CGUIText.h b/source/gui/CGUIText.h index 0f258cd25d..56d80b01a8 100644 --- a/source/gui/CGUIText.h +++ b/source/gui/CGUIText.h @@ -124,6 +124,19 @@ public: */ CStrIntern m_Font; + /** + * BCP-47 locale tag (e.g. `"en"`, `"ja"`, `"zh-hans"`) that tells the + * FontManager which per-locale font to use for this portion of text. + * + * If the string is non-empty, the renderer tries to substitute a font + * registered for that locale; otherwise it falls back to the game-wide + * UI font so legacy captions behave exactly as before. + * + * The value is filled in by the `[locale='xx']…[/locale]` inline-markup + * parser and read during glyph layout. + */ + CStrIntern m_FontLocale; + /** * Settings */ diff --git a/source/gui/ObjectTypes/CInput.cpp b/source/gui/ObjectTypes/CInput.cpp index d582237ff7..466657637e 100644 --- a/source/gui/ObjectTypes/CInput.cpp +++ b/source/gui/ObjectTypes/CInput.cpp @@ -1255,7 +1255,7 @@ void CInput::DrawContent(CCanvas2D& canvas) const float ls{font.GetHeight()}; CTextRenderer textRenderer; - textRenderer.SetCurrentFont(font_name); + textRenderer.SetCurrentFont(font_name, CStrIntern{}); textRenderer.Translate( (float)(int)(m_CachedActualSize.left) + m_BufferZone, diff --git a/source/gui/SettingTypes/CGUIString.cpp b/source/gui/SettingTypes/CGUIString.cpp index 0d6733cac0..33734f309d 100644 --- a/source/gui/SettingTypes/CGUIString.cpp +++ b/source/gui/SettingTypes/CGUIString.cpp @@ -207,6 +207,9 @@ void CGUIString::GenerateTextCall(const CGUI& pGUI, SFeedback& Feedback, CStrInt // TODO Gee: (2004-08-15) Check if Font exists? TextCall.m_Font = CStrIntern(utf8_from_wstring(tag.m_TagValue)); break; + case TextChunk::Tag::TAG_LOCALE: + TextCall.m_FontLocale = CStrIntern(utf8_from_wstring(tag.m_TagValue)); + break; case TextChunk::Tag::TAG_TOOLTIP: TextCall.m_Tooltip = tag.m_TagValue; break; @@ -219,7 +222,7 @@ void CGUIString::GenerateTextCall(const CGUI& pGUI, SFeedback& Feedback, CStrInt // Calculate the size of the font. CSize2D size; float cx, cy; - CFontMetrics font (TextCall.m_Font); + CFontMetrics font{TextCall.m_Font, TextCall.m_FontLocale}; font.CalculateStringSize(TextCall.m_String.c_str(), cx, cy); size.Width = cx; @@ -273,6 +276,8 @@ CGUIString::TextChunk::Tag::TagType CGUIString::TextChunk::Tag::GetTagType(const return TAG_COLOR; if (tagtype == L"font") return TAG_FONT; + if (tagtype == L"locale") + return TAG_LOCALE; if (tagtype == L"icon") return TAG_ICON; if (tagtype == L"imgleft") diff --git a/source/gui/SettingTypes/CGUIString.h b/source/gui/SettingTypes/CGUIString.h index 7f7770bb6d..02a80748fc 100644 --- a/source/gui/SettingTypes/CGUIString.h +++ b/source/gui/SettingTypes/CGUIString.h @@ -66,6 +66,7 @@ public: TAG_B, TAG_I, TAG_FONT, + TAG_LOCALE, TAG_SIZE, TAG_COLOR, TAG_IMGLEFT, diff --git a/source/ps/CConsole.cpp b/source/ps/CConsole.cpp index b48075db6a..c04b80a9aa 100644 --- a/source/ps/CConsole.cpp +++ b/source/ps/CConsole.cpp @@ -225,7 +225,7 @@ void CConsole::Render(CCanvas2D& canvas) DrawWindow(canvas); CTextRenderer textRenderer; - textRenderer.SetCurrentFont(CStrIntern(m_consoleFont)); + textRenderer.SetCurrentFont(CStrIntern{m_consoleFont}, CStrIntern{}); // Animation: slide in from top of screen. const float deltaY = (1.0f - m_VisibleFrac) * m_Height; textRenderer.Translate(m_X, m_Y - deltaY); diff --git a/source/ps/CLogger.cpp b/source/ps/CLogger.cpp index 196aae644e..f382aba64f 100644 --- a/source/ps/CLogger.cpp +++ b/source/ps/CLogger.cpp @@ -182,7 +182,7 @@ void CLogger::Render(CCanvas2D& canvas) float height{font.GetHeight()}; CTextRenderer textRenderer; - textRenderer.SetCurrentFont(font_name); + textRenderer.SetCurrentFont(font_name, CStrIntern{}); textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 1.0f, 1.0f)); // Offset by an extra 35px vertically to avoid the top bar. diff --git a/source/ps/ProfileViewer.cpp b/source/ps/ProfileViewer.cpp index d72a74767d..4fa86f8a7c 100644 --- a/source/ps/ProfileViewer.cpp +++ b/source/ps/ProfileViewer.cpp @@ -200,7 +200,7 @@ void CProfileViewer::RenderProfile(CCanvas2D& canvas) // Print table and column titles. CTextRenderer textRenderer; - textRenderer.SetCurrentFont(font_name); + textRenderer.SetCurrentFont(font_name, CStrIntern{}); textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 1.0f, 1.0f)); textRenderer.PrintfAt(2.0f, height, L"%hs", table->GetTitle().c_str()); textRenderer.Translate(22.0f, height*2.0f); diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index 02f8f7c1c7..b4770fdfee 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -793,7 +793,7 @@ void TerrainRenderer::RenderPriorities(CCanvas2D& canvas, int cullGroup) ENSURE(m->phase == Phase_Render); CTextRenderer textRenderer; - textRenderer.SetCurrentFont(CStrIntern("mono-stroke-10")); + textRenderer.SetCurrentFont(CStrIntern{"mono-stroke-10"}, CStrIntern{}); textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 0.0f, 1.0f)); std::vector& visiblePatches = m->visiblePatches[cullGroup];