Japanese localization (#415)

* translate most option names and setting names

* add most header/button guide japanese localizations

* include furigana usage guidance for japanese localization

* remove os message window furigana note

* fix incorrect kana usage for 'lock'

* add more option localization

* translate achievement notification option

* remove english battle theme description from japanese

* fix compilation errors

* Japanese localization 1st pass

* Revert JP localization taken from in-game

* Use "Window Size"

* add support for zero width line break hints

* add dirty hack for kana being clipped at the right edge of the info panel

* Changed line to original meaning

* Added zero width spaces to option descriptions

* Japanese localization 2nd pass

* Remove furigana from Video_BackendError

* add more japanese specific hacks for installer_wizard description

* locale small fixes

* Update config_locale.cpp

* options menu scroll fixes, minor localization touchups

* adjust installer wizard text spacing

* fix installer wizard text positioning when a line starts with a normal kana in japanese

* Installer adjustment

* option fixes

* Added missing line break on introduction

* do touchups to message window annotation drop shadow

* adjust spacing in TitleMissingDLC text to fit furigana properly

* add brianuuuSonic to credits

* change capitalization of xbox in xbox color correction

* remove usage of temporaries

* tweak cutscene aspect ratio description

* add missing credit for german translation

* update resources submodule

---------

Co-authored-by: brianuuu <38166666+brianuuu@users.noreply.github.com>
This commit is contained in:
ĐeäTh
2025-02-20 18:20:49 +01:00
committed by GitHub
parent de7148772b
commit b101e016cf
10 changed files with 376 additions and 128 deletions
+133 -3
View File
@@ -464,17 +464,39 @@ std::vector<std::string> Split(const char* strStart, const ImFont* font, float f
const bool wordWrapEnabled = (maxWidth > 0.0f);
const char *wordWrapEOL = nullptr;
auto IsKanji = [](const char* str, const char* strEnd)
{
const char* tempStr = str;
unsigned int c = (unsigned int)*tempStr;
if (c < 0x80)
tempStr += 1;
else
tempStr += ImTextCharFromUtf8(&c, tempStr, strEnd);
// Basic CJK and CJK Extension A
return (c >= 0x4E00 && c <= 0x9FBF) || (c >= 0x3400 && c <= 0x4DBF);
};
while (*str != 0)
{
if (wordWrapEnabled)
{
if (wordWrapEOL == nullptr)
{
wordWrapEOL = font->CalcWordWrapPositionA(scale, str, strEnd, maxWidth - lineWidth);
wordWrapEOL = CalcWordWrapPositionA(font, scale, str, strEnd, maxWidth - lineWidth);
}
if (str >= wordWrapEOL)
{
if (IsKanji(str, strEnd))
{
// If the current character is Kanji, move back to prevent splitting Kanji
while (str > lineStart && IsKanji(str - 3, strEnd))
{
str -= 3;
}
}
if (textWidth < lineWidth)
textWidth = lineWidth;
@@ -648,7 +670,7 @@ ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidt
return MeasureCentredParagraph(font, fontSize, lineMargin, lines);
}
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred)
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred, bool leadingSpace)
{
auto annotationFontSize = fontSize * ANNOTATION_FONT_SIZE_MODIFIER;
@@ -656,8 +678,14 @@ void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, c
auto lines = Split(input.first.c_str(), font, fontSize, maxWidth);
for (auto& line : lines)
{
line = ReAddRubyAnnotations(line, input.second);
if (!line.empty() && line.substr(0, 3) != "" && leadingSpace)
{
line.insert(0, " ");
}
}
auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines);
auto offsetY = 0.0f;
@@ -826,3 +854,105 @@ void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha)
drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOffUVs), lightCol);
}
}
// Taken from ImGui because we need to modify to break for '\u200B\ too
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char* CalcWordWrapPositionA(const ImFont* font, float scale, const char* text, const char* text_end, float wrap_width)
{
// For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!"
// ^ ^ ^ ^ ^__ ^ ^
// List of hardcoded separators: .,;!?'"
// Skip extra blanks after a line returns (that includes not counting them in width computation)
// e.g. "Hello world" --> "Hello" "World"
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
float line_width = 0.0f;
float word_width = 0.0f;
float blank_width = 0.0f;
wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
const char* word_end = text;
const char* prev_word_end = NULL;
bool inside_word = true;
const char* s = text;
IM_ASSERT(text_end != NULL);
while (s < text_end)
{
unsigned int c = (unsigned int)*s;
const char* next_s;
if (c < 0x80)
next_s = s + 1;
else
next_s = s + ImTextCharFromUtf8(&c, s, text_end);
if (c < 32)
{
if (c == '\n')
{
line_width = word_width = blank_width = 0.0f;
inside_word = true;
s = next_s;
continue;
}
if (c == '\r')
{
s = next_s;
continue;
}
}
const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX);
if (ImCharIsBlankW(c) || c == 0x200B)
{
if (inside_word)
{
line_width += blank_width;
blank_width = 0.0f;
word_end = s;
}
blank_width += char_width;
inside_word = false;
}
else
{
word_width += char_width;
if (inside_word)
{
word_end = next_s;
}
else
{
prev_word_end = word_end;
line_width += word_width + blank_width;
word_width = blank_width = 0.0f;
}
// Allow wrapping after punctuation.
inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"');
}
// We ignore blank width at the end of the line (they can be skipped)
if (line_width + word_width > wrap_width)
{
// Words that cannot possibly fit within an entire line will be cut anywhere.
if (word_width < wrap_width)
s = prev_word_end ? prev_word_end : word_end;
break;
}
s = next_s;
}
// Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
// +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
if (s == text && text < text_end)
return s + 1;
return s;
}
+2 -1
View File
@@ -72,7 +72,7 @@ std::vector<std::string> RemoveAnnotationFromParagraph(const std::vector<std::st
std::string RemoveAnnotationFromParagraphLine(const std::vector<TextSegment>& annotatedLine);
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector<std::string>& lines);
ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text);
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred = false);
void DrawRubyAnnotatedText(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function<void(const char*, ImVec2)> drawMethod, std::function<void(const char*, float, ImVec2)> annotationDrawMethod, bool isCentred = false, bool leadingSpace = false);
float Lerp(float a, float b, float t);
float Cubic(float a, float b, float t);
float Hermite(float a, float b, float t);
@@ -81,3 +81,4 @@ ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t);
void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70));
void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop = false);
void DrawToggleLight(ImVec2 pos, bool isEnabled, float alpha = 1.0f);
const char* CalcWordWrapPositionA(const ImFont* font, float scale, const char* text, const char* text_end, float wrap_width);
+13 -1
View File
@@ -736,6 +736,14 @@ static void DrawDescriptionContainer()
}
else if (g_currentPage == WizardPage::InstallFailed)
{
// Japanese needs text to be brought in by a normal width space
// as it allows for text to begin further than others for
// special characters.
if (Config::Language == ELanguage::Japanese)
{
strncat(descriptionText, " ", 1);
}
strncat(descriptionText, g_installerErrorMessage.c_str(), sizeof(descriptionText) - 1);
}
@@ -769,6 +777,8 @@ static void DrawDescriptionContainer()
textX += annotationFontSize;
textY += annotationFontSize;
lineWidth += annotationFontSize;
}
drawList->PushClipRect(clipRectMin, clipRectMax, false);
@@ -788,7 +798,9 @@ static void DrawDescriptionContainer()
[=](const char* str, float size, ImVec2 pos)
{
DrawTextBasic(g_seuratFont, size, pos, IM_COL32(255, 255, 255, 255 * textAlpha), str);
}
},
false,
Config::Language == ELanguage::Japanese
);
drawList->PopClipRect();
+1 -1
View File
@@ -370,7 +370,7 @@ void MessageWindow::Draw()
},
[=](const char* str, float size, ImVec2 pos)
{
DrawTextWithShadow(g_fntSeurat, size, pos, IM_COL32(255, 255, 255, 255), str, 1.0f);
DrawTextWithShadow(g_fntSeurat, size, pos, IM_COL32(255, 255, 255, 255), str, 1.5f, 1.5f);
},
true
+7 -2
View File
@@ -1497,6 +1497,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
auto textX = clipRectMin.x - Scale(0.5f);
auto textY = thumbnailMax.y + offsetY;
float lineWidth = clipRectMax.x - clipRectMin.x;
if (Config::Language == ELanguage::Japanese)
{
@@ -1511,9 +1512,13 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
clipRectMax.x += annotationFontSize;
textY += annotationFontSize;
// Dirty hack to disallow clipping on Japanese text
// whilst allowing annotations to go over the border
lineWidth -= annotationFontSize;
}
auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, clipRectMax.x - clipRectMin.x, 5.0f, desc.c_str());
auto textSize = MeasureCentredParagraph(g_seuratFont, fontSize, lineWidth, 5.0f, desc.c_str());
drawList->PushClipRect(clipRectMin, clipRectMax, false);
@@ -1589,7 +1594,7 @@ static void DrawInfoPanel(ImVec2 infoMin, ImVec2 infoMax)
(
g_seuratFont,
fontSize,
clipRectMax.x - clipRectMin.x,
lineWidth,
{ textX, textY - scrollOffset },
5.0f,
desc.c_str(),