feat(i18n): 统一国际化系统架构,支持插件独立翻译 (#301)

* feat(i18n): 统一国际化系统架构,支持插件独立翻译

## 主要改动

### 核心架构
- 增强 LocaleService,支持插件命名空间翻译扩展
- 新增 editor-runtime/i18n 模块,提供 createPluginLocale/createPluginTranslator
- 新增 editor-core/tokens.ts,定义 LocaleServiceToken 等服务令牌
- 改进 PluginAPI 类型安全,使用 ServiceToken<T> 替代 any

### 编辑器本地化
- 扩展 en.ts/zh.ts 翻译文件,覆盖所有 UI 组件
- 新增 es.ts 西班牙语支持
- 重构 40+ 组件使用 useLocale() hook

### 插件本地化系统
- behavior-tree-editor: 新增 locales/ 和 useBTLocale hook
- material-editor: 新增 locales/ 和 useMaterialLocale hook
- particle-editor: 新增 locales/ 和 useParticleLocale hook
- tilemap-editor: 新增 locales/ 和 useTilemapLocale hook
- ui-editor: 新增 locales/ 和 useUILocale hook

### 类型安全改进
- 修复 Debug 工具使用公共接口替代 as any
- 修复 ChunkStreamingSystem 添加 forEachChunk 公共方法
- 修复 blueprint-editor 移除不必要的向后兼容代码

* fix(behavior-tree-editor): 使用 ServiceToken 模式修复服务解析

- 创建 BehaviorTreeServiceToken 遵循"谁定义接口,谁导出Token"原则
- 使用 ServiceToken.id (symbol) 注册服务到 ServiceContainer
- 更新 PluginSDKRegistry.resolveService 支持 ServiceToken 检测
- BehaviorTreeEditorPanel 现在使用类型安全的 PluginAPI.resolve

* fix(behavior-tree-editor): 使用 ServiceContainer.resolve 获取类注册的服务

* fix: 修复多个包的依赖和类型问题

- core: EntityDataCollector.getEntityDetails 使用 HierarchySystem 获取父实体
- ui-editor: 添加 @esengine/editor-runtime 依赖
- tilemap-editor: 添加 @esengine/editor-runtime 依赖
- particle-editor: 添加 @esengine/editor-runtime 依赖
This commit is contained in:
YHH
2025-12-09 18:04:03 +08:00
committed by GitHub
parent 995fa2d514
commit 1b0d38edce
103 changed files with 8015 additions and 1633 deletions

View File

@@ -3,6 +3,7 @@ import { FolderOpen, FileText, Terminal, ChevronDown, ChevronUp, Activity, Wifi,
import type { MessageHub, LogService } from '@esengine/editor-core';
import { ContentBrowser } from './ContentBrowser';
import { OutputLogPanel } from './OutputLogPanel';
import { useLocale } from '../hooks/useLocale';
import '../styles/StatusBar.css';
interface StatusBarProps {
@@ -26,6 +27,7 @@ export function StatusBar({
projectPath,
onOpenScene
}: StatusBarProps) {
const { t } = useLocale();
const [consoleInput, setConsoleInput] = useState('');
const [activeTab, setActiveTab] = useState<ActiveTab>('output');
const [contentDrawerOpen, setContentDrawerOpen] = useState(false);
@@ -254,7 +256,7 @@ export function StatusBar({
onClick={handleContentDrawerClick}
>
<FolderOpen size={14} />
<span>{locale === 'zh' ? '内容侧滑菜单' : 'Content Drawer'}</span>
<span>{t('statusBar.contentDrawer')}</span>
{contentDrawerOpen ? <ChevronDown size={12} /> : <ChevronUp size={12} />}
</button>
@@ -265,7 +267,7 @@ export function StatusBar({
onClick={handleOutputLogClick}
>
<FileText size={12} />
<span>{locale === 'zh' ? '输出日志' : 'Output Log'}</span>
<span>{t('statusBar.outputLog')}</span>
</button>
<button
@@ -282,7 +284,7 @@ export function StatusBar({
<input
ref={inputRef}
type="text"
placeholder={locale === 'zh' ? '输入控制台命令' : 'Enter Console Command'}
placeholder={t('statusBar.consolePlaceholder')}
value={consoleInput}
onChange={(e) => setConsoleInput(e.target.value)}
onKeyDown={handleConsoleSubmit}
@@ -294,17 +296,17 @@ export function StatusBar({
<div className="status-bar-right">
<button className="status-bar-indicator">
<Activity size={12} />
<span>{locale === 'zh' ? '回追踪' : 'Trace'}</span>
<span>{t('statusBar.trace')}</span>
<ChevronDown size={10} />
</button>
<div className="status-bar-divider" />
<div className="status-bar-icon-group">
<button className="status-bar-icon-btn" title={locale === 'zh' ? '网络' : 'Network'}>
<button className="status-bar-icon-btn" title={t('statusBar.network')}>
<Wifi size={14} />
</button>
<button className="status-bar-icon-btn" title={locale === 'zh' ? '源代码管理' : 'Source Control'}>
<button className="status-bar-icon-btn" title={t('statusBar.sourceControl')}>
<GitBranch size={14} />
</button>
</div>
@@ -313,11 +315,11 @@ export function StatusBar({
<div className="status-bar-info">
<Save size={12} />
<span>{locale === 'zh' ? '所有已保存' : 'All Saved'}</span>
<span>{t('statusBar.allSaved')}</span>
</div>
<div className="status-bar-info">
<span>{locale === 'zh' ? '版本控制' : 'Revision Control'}</span>
<span>{t('statusBar.revisionControl')}</span>
</div>
</div>
</div>