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

@@ -2,9 +2,11 @@ import { IValidator, ValidationResult, ValidationError } from '../../domain/inte
import { BehaviorTree } from '../../domain/models/BehaviorTree';
import { Node } from '../../domain/models/Node';
import { Connection } from '../../domain/models/Connection';
import { translateBT } from '../../hooks/useBTLocale';
/**
* 行为树验证器实现
* Behavior Tree Validator Implementation
*/
export class BehaviorTreeValidator implements IValidator {
/**
@@ -37,21 +39,22 @@ export class BehaviorTreeValidator implements IValidator {
/**
* 验证节点
* Validate node
*/
validateNode(node: Node): ValidationResult {
const errors: ValidationError[] = [];
// 验证节点必填字段
// 验证节点必填字段 | Validate required fields
if (!node.id) {
errors.push({
message: '节点 ID 不能为空',
message: translateBT('validation.nodeIdRequired'),
nodeId: node.id
});
}
if (!node.template) {
errors.push({
message: '节点模板不能为空',
message: translateBT('validation.nodeTemplateRequired'),
nodeId: node.id
});
}
@@ -64,30 +67,31 @@ export class BehaviorTreeValidator implements IValidator {
/**
* 验证连接
* Validate connection
*/
validateConnection(connection: Connection, tree: BehaviorTree): ValidationResult {
const errors: ValidationError[] = [];
// 验证连接的源节点和目标节点都存在
// 验证连接的源节点和目标节点都存在 | Validate source and target nodes exist
const fromNode = tree.nodes.find((n) => n.id === connection.from);
const toNode = tree.nodes.find((n) => n.id === connection.to);
if (!fromNode) {
errors.push({
message: `连接的源节点不存在: ${connection.from}`
message: translateBT('validation.sourceNodeNotFound', undefined, { nodeId: connection.from })
});
}
if (!toNode) {
errors.push({
message: `连接的目标节点不存在: ${connection.to}`
message: translateBT('validation.targetNodeNotFound', undefined, { nodeId: connection.to })
});
}
// 不能自己连接自己
// 不能自己连接自己 | Cannot connect to self
if (connection.from === connection.to) {
errors.push({
message: '节点不能连接到自己',
message: translateBT('validation.selfConnection'),
nodeId: connection.from
});
}
@@ -100,6 +104,7 @@ export class BehaviorTreeValidator implements IValidator {
/**
* 验证是否会产生循环引用
* Validate no cycles exist
*/
validateNoCycles(tree: BehaviorTree): ValidationResult {
const errors: ValidationError[] = [];
@@ -134,7 +139,7 @@ export class BehaviorTreeValidator implements IValidator {
for (const node of tree.nodes) {
if (hasCycle(node.id)) {
errors.push({
message: '行为树中存在循环引用',
message: translateBT('validation.cycleDetected'),
nodeId: node.id
});
break;