i18n: add Simplified Chinese translation for imgui_demo + Apple Metal example

- imgui_demo.cpp: wrap all user-visible string literals with Tr(); add
  Language menu in DemoWindowMenuBar (guarded by IMGUI_DEMO_ENABLE_I18N);
  guard extern g_need_font_rebuild with IMGUI_DEMO_ENABLE_I18N + __APPLE__
- examples/example_apple_metal/main.mm: CJK font loading via RebuildFonts()
  (always loads 2-glyph stub in English, full range in zh_CN); Language menu
  triggers font atlas rebuild before next NewFrame(); wrap demo window strings
  with Tr(); fix v1.92 ImplicitRefSize assertion by passing explicit SizePixels
  to AddFontDefault()
- examples/example_apple_metal/Makefile: add i18n sources and
  -DIMGUI_DEMO_ENABLE_I18N flag
This commit is contained in:
xuk 2026-06-08 11:22:24 +08:00
parent 22c87d2bf7
commit 0576c0e3a1
3 changed files with 785 additions and 701 deletions

View File

@ -5,8 +5,10 @@ IMGUI_DIR = ../../
SOURCES = main.mm
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_osx.mm $(IMGUI_DIR)/backends/imgui_impl_metal.mm
SOURCES += $(IMGUI_DIR)/i18n/imgui_i18n.cpp
SOURCES += $(IMGUI_DIR)/i18n/locale/zh_CN.cpp
CXXFLAGS = -std=c++11 -ObjC++ -fobjc-arc -Wall -Wextra -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
CXXFLAGS = -std=c++11 -ObjC++ -fobjc-arc -Wall -Wextra -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(IMGUI_DIR)/i18n -DIMGUI_DEMO_ENABLE_I18N
FRAMEWORKS = -framework AppKit -framework Metal -framework MetalKit -framework QuartzCore -framework GameController
all: $(EXE)

View File

@ -18,6 +18,8 @@
#import <MetalKit/MetalKit.h>
#include "imgui.h"
#include "imgui_i18n.h"
#include <unistd.h>
#include "imgui_impl_metal.h"
#if TARGET_OS_OSX
#include "imgui_impl_osx.h"
@ -38,6 +40,43 @@
// AppViewController
//-----------------------------------------------------------------------------------
bool g_need_font_rebuild = false;
static void RebuildFonts()
{
ImGuiIO& io = ImGui::GetIO();
io.Fonts->ClearFonts();
// v1.92: must pass explicit SizePixels so base font is not marked ImplicitRefSize,
// otherwise MergeMode on the CJK font triggers an assertion.
ImFontConfig default_cfg;
default_cfg.SizePixels = 13.0f;
io.Fonts->AddFontDefault(&default_cfg);
// Merge CJK font:
// zh_CN → full common range (~2500 glyphs, renders all translated text)
// English → only "中文" (2 glyphs U+4E2D,U+6587) so Language menu label renders correctly
// Hiragino Sans GB is designed for Simplified Chinese; fall back to STHeiti.
const char* font_paths[] = {
"/System/Library/Fonts/Hiragino Sans GB.ttc",
"/System/Library/Fonts/STHeiti Light.ttc",
nullptr
};
const char* chosen = nullptr;
for (int i = 0; font_paths[i]; ++i) {
if (access(font_paths[i], R_OK) == 0) { chosen = font_paths[i]; break; }
}
if (chosen) {
static const ImWchar zh_label_ranges[] = { 0x4E2D, 0x4E2D, 0x6587, 0x6587, 0 };
bool is_zh = (strcmp(imgui_i18n::getLocale(), "zh_CN") == 0);
ImFontConfig cfg;
cfg.MergeMode = true;
cfg.PixelSnapH = true;
io.Fonts->AddFontFromFileTTF(chosen, 13.0f, &cfg,
is_zh ? io.Fonts->GetGlyphRangesChineseSimplifiedCommon() : zh_label_ranges);
}
io.Fonts->Build();
}
@implementation AppViewController
-(instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
@ -110,6 +149,9 @@
ImGui_ImplOSX_Init(self.view);
[NSApp activateIgnoringOtherApps:YES];
#endif
// Build initial font atlas with CJK glyphs so the Language menu renders correctly from the start.
RebuildFonts();
}
-(void)drawInMTKView:(MTKView*)view
@ -134,6 +176,12 @@
return;
}
// 语言切换后重建字体 Atlas(必须在 NewFrame 之前,避免当帧纹理 use-after-free)
if (g_need_font_rebuild) {
g_need_font_rebuild = false;
RebuildFonts();
}
// Start the Dear ImGui frame
ImGui_ImplMetal_NewFrame(renderPassDescriptor);
#if TARGET_OS_OSX
@ -155,30 +203,30 @@
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
ImGui::Begin(Tr("Hello, world!"));
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::Text("%s", Tr("This is some useful text."));
ImGui::Checkbox(Tr("Demo Window"), &show_demo_window);
ImGui::Checkbox(Tr("Another Window"), &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
ImGui::SliderFloat(Tr("float"), &f, 0.0f, 1.0f);
ImGui::ColorEdit3(Tr("clear color"), (float*)&clear_color);
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
if (ImGui::Button(Tr("Button")))
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text(Tr("counter = %d"), counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Text(Tr("Application average %.3f ms/frame (%.1f FPS)"), 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}
// 3. Show another simple window.
if (show_another_window)
{
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
ImGui::Begin(Tr("Another Window"), &show_another_window);
ImGui::Text("%s", Tr("Hello from another window!"));
if (ImGui::Button(Tr("Close Me")))
show_another_window = false;
ImGui::End();
}

File diff suppressed because it is too large Load Diff