Blade的UI都是预定义的接口, 然后由插件来负责实现, 目前只有MFC的插件. 最近加上了TableView的视图, 用于一些文件的查看和编辑, 比如前面在文件包的笔记中提到需写一个package exploerer, 以及多国语言相关的笔记中需要的lang-edit-tool, 目前Blade还没有简化的二进制数据表, 如果有的话, 这个视图也可以用用于Database的编辑, 同时这个view也已经加到了max导出插件中, 用于定义多个导出动画.
设计的思路是用MVC, 记得以前还想专门写一些代码来"练习MVC", 但实际上是, 代码写多了, 不经意间就会用MVC, 而且这次是写完以后发现竟然用的是MVC, 当然实际中未必跟标准的MVC完全一样.
这个TableView在Blade中叫ITableEditorWindow : IEditorWindow, 是一个公开的接口, 其具体实现注册到Factory<IEditorWindow>, 客户代码使用工厂创建对象.
他对应了一个controller, 用于实现导航. Blade内置了一个controller, 其导航方式类似Exel, Enter下一行, Tab下一列列, Shift+Enter和Shift+Tab相反等等, 一般情况下客户代码不需要自己实现controller, 因为内置的这个已经足够用了,当然如果有复杂的需求可能需要定制controller.
窗口视图上的工具栏, 是客户代码负责添加, 并绑定对应的功能, UI只负责添加工具, 与业务逻辑无关, 比如下面两种文件, 使用的是同一个view, 工具栏(浏览导航和翻译查找等)是插件自己负责添加的.
这主要是接口设计的问题. 对于翻译工具, 可以选择多个可视列, 用于对比和参考, 这个主要是用于翻译的.
下面主要备忘多国语言的问题, 前面提到了大致思路, 思路是很简单, 实现起来是还是有很多问题的.
blade的lang-build tool是一个CLI工具, 它会把所有的源文件(.h,.hpp,.c, .cpp,.cc,.cxx等等), 甚至包括了win32下的rc文件, 全部读取并扫描出可翻译的原始字符串, 保存到字典. 然后就可以用lang-edit插件来翻译了.
可翻译的字符串前面已经提到过, 使用一个宏, 比如XLANG("Hello"), 这样就可以识别XLANG这个宏, 并扫描字符串了.
XLANG宏定义是一个开关, 如果开启多国语言, 就会调用接口来翻译, 如果关掉多国语言, 那么就使用原始字符串.
遇到的问题:
1.同一个单词, 在不同语境下意思不同.
这个问题一开始就遇到了, 但是不太好解决. 比如Parent, 翻译为"父节点"还是"父空间"等等. 注意如果是整句话的时候, 这个情况一般是不会出现的, 因为一句话本身就有包含了自己的语境. 目前的解决方案是: 如果两个文件中发现相同的字符串, 而且两个文件的工程路径比较远, 就报错. 这样用来提醒程序员, 这两个词可能会有歧义, 需要修改.
比如把简化的"Parent"改为"Parent Space" 和"Parent Node".
当然相同意思的字符串本身可以在不同文件*享使用, 但不能直接使用, 因为会当做错误报错. 只能用过预定义宏来使用, 比如
#define EDIT "Edit", 所有文件中都可以使用XLANG(EDIT)来使用这个字符串.
这种方式很黑(hacky), 不太好, 用起来也不友好, 而且并不能真正完全解决问题. 好在lang-buid-tool已经集成到了VS工程, 并且报错的时候会有原文件和行号, 跟编译错误一样, 可以双击直接跟踪, 方便修改.
更好的方式: 添加语境上下文. 比如类似于TEXT("Parent", "transform space"), TEXT("Parent", "node hierarchy") 第二个字符串就是context, 他会被收集起来用于翻译, 运行时也作为sub Key来索引翻译项.
Blade没有使用这个方式的原因是多了一个参数, 感觉稍微有点不方便(也不太适用于目前的视图), 而且很多时候明确不许需要context, 比如一个完整句子, 那也必须写一个参数, 即TEXT("blah blah", "")
2.扫描源文件, 需要解析条件编译.
首先, 如果真正要扫描一个文件, 需要把它包含的头文件(递归的)全部加入(#include),这个问题比较容易解决.
其次, #if #else/#elif #endif需要选择扫描有效的分支, 这个问题变得更复杂, 因为#if ABC > EFG甚至#if ((ABC*3) <= EFG || (HIJ != OPQ)) && XYZ需要对表达式求值.
另外, 宏的嵌套. 比如
#define A "A"
#define B A
#define C B
... XLANG(C)
这个时候需要展开宏定义到最终的字符串.
整个问题差不多需要手写一个pre-processor, 而我也差一点要做这个功能, 但是想了想还没有这个必要, 所以没有做.
所以目前采用的是暴力的方式, 即不处理#include, 扫描所有文件, 所以, 条件编译以及对应表达式求值还没有解决, 一些#if 0对应的分支, 也会把字符串加入, 会多一些无用的翻译. 而且如果两个文件各自定义了不同的宏定义, 也会冲突.
而我公司因为使用的是公司自己写的编译器, 这个功能已经内置到编译器里去了...所以在pre-processor就把无用的文本strip掉了.
3.运行时强制翻译的字符串的收集.
前面提到, 一些字符串是在win32 .rc文件里面, 也是UI显示时强制翻译的. 工厂中的类名不能翻译(翻译了之后创建对象也会跟locale相关), 显示时需要动态翻译, 也就是不适用XLANG()来直接加入词典, 而是在UI模块显示的时候, 手动调用翻译接口.
那么对于这种字符串, 因为没有用XLANG(), 所以lang-build-tool不能识别. 现在的解决方案是, 使用另外一个宏, 比如XLANGRAW() 从而上lang-build-tool识别并收集. 实际上XLANGRAW()并不会调用翻译接口, 只是为了后台工具扫描加入词典.
如果使用context的话, TEXT("String", "NOLOC") 即使用一个特殊的context来标记这个字符串不需要翻译.
总结
总的来说, 目前的实现比较简陋, 而且可以预见一些问题, 如果加上context, 就不会有冲突的问题了, 而且复用的单词不需要统一使用宏来定义, 用起来更方便. 最好的方式是XLANG可以接受一个或者两个参数, 通常只用一个参数就可以了, 只有遇到单词冲突时, lang-build-tool报错, 这个时候再加上第二个参数-context都来得及, 这种情况相对少很多. 需要注意的是, 如果一个文件的字符串跟原有文件的冲突, 那么就提示修改过的文件处的错误, 而不是原有文件, 这个在lang-build-tool扫描文件时, 按照时间戳排序就可以了. 唯一的问题是为了处理MBCS和WCS的问题, XLANG必须是一个宏, 如果想要接受不同的参数, 需要使用variadic macro, 这个是C++11才有的特性, 所以目前不考虑. 不使用宏也可以, 使用函数重载:
cosnt TString& XLANG(const char* originalString);
const TString& XLANG(const char* originalString, const char* context); 但是在使用WCS并且关闭翻译时, 需要在runtime, 最起码在第一次使用时, 进行MBCS/WCS的转换: 把原始字符串转换为一模一样的wstring, 效率会有损失. 而且进程镜像中的MBCS常量被浪费了, 需要额外保存WCS字符串, 多耗费了内存.
这个问题准备先放一放, 后面有时间考虑一下再说.
上面只是源代码中文字翻译的处理(以程序员为用户), 实际上一个游戏的文字很可能是由策划使用一个工具来填写(比如大量文字的AVG), 这个翻译问题也有想过, 简单写了接口但是还没有真正去做.
引擎设计跟踪(九.14.2j) TableView工具填坑以及多国语言的更多相关文章
-
引擎设计跟踪(九.14.2a) 导出插件问题修复和 Tangent Space 裂缝修复
由于工作很忙, 近半年的业余时间没空搞了, 不过工作马上忙完了, 趁十一有时间修了一些小问题. 这次更新跟骨骼动画无关, 修复了一个之前的, 关于tangent space裂缝的问题: 引擎设计跟踪( ...
-
引擎设计跟踪(九.14.2g) 将GNUMake集成到Visual Studio
最近在做纹理压缩工具, 以及数据包的生成. shader编译已经在vs工程里面了, 使用custom build tool, build命令是调用BladeShaderComplier, 并且每个文件 ...
-
引擎设计跟踪(九.14.2f) 最近更新: OpenGL ES &; tools
之前骨骼动画的IK暂时放一放, 最近在搞GLES的实现. 之前除了GLES没有实现, Android的代码移植已经完毕: [原]跨平台编程注意事项(三): window 到 android 的 移植 ...
-
引擎设计跟踪(九.14.2d) [翻译] shader的跨平台方案之2014
Origin: http://aras-p.info/blog/2014/03/28/cross-platform-shaders-in-2014/ 简译 translation: 作者在2012年写 ...
-
引擎设计跟踪(九.14.3.4) mile stone 2 - model和fbx导入的补漏
之前milestone2已经做完的工作, 现在趁有时间记下笔记. 1.设计 这里是指兼容3ds max导出/fbx格式转换等等一系列工作的设计. 最开始, Blade的3dsmax导出插件, 全部代码 ...
-
引擎设计跟踪(九.14.2 final) Inverse Kinematics: CCD 在Blade中的实现
因为工作忙, 好久没有记笔记了, 但是有时候发现还得翻以前的笔记去看, 所以还是尽量记下来备忘. 关于IK, 读了一些paper, 觉得之前翻译的那篇, welman的paper (http://gr ...
-
引擎设计跟踪(九.14.2i) Android GLES 3.0 完善
最近把渲染设备对应的GLES的API填上了. 主要有IRenderDevice/IShader/ITexture/IGraphicsResourceManager/IIndexBuffer/IVert ...
-
引擎设计跟踪(九.14.2b) 骨骼动画基本完成
首先贴一个介绍max的sdk和骨骼动画的文章, 虽然很早的文章, 但是很有用, 感谢前辈们的贡献: 3Ds MAX骨骼动画导出插件编写 1.Dual Quaternion 关于Dual Quatern ...
-
引擎设计跟踪(九.14.2c) 最近一些小的更新
1. bump map与normal map 昨天拿了crytek sponza(http://www.crytek.com/cryengine/cryengine3/downloads)场景测试, ...
随机推荐
-
2.4嵌套多重if else 的闰年判断以及bool变量的用法
#include<stdio.h> #include<stdbool.h> int main() { int year; bool leap; //把leap定义为bool , ...
-
使用hexo搭建属于自己的博客
如果你喜欢拥有自己的博客域名,如果你喜欢折腾,可以先点击luckykun.com,看看效果,再选择要不要进来看看--- 之前一直都在博客园写博客,不过最近在逛园子的时候不小心看到了hexo,简直有种相 ...
-
C语言深度学习——第一天
首先声明一下,在我们写的程序中,会使用到一个头文件# include <head.h> 因为,在linux系统编程的时候,会用到很多头文件,为此,我用一个头文件全部包含在一起,头文件内容如 ...
-
DLL编程学习
原文出处:http://www.blogjava.net/wxb_nudt/archive/2007/09/11/144371.html DLL编写教程 半年不能上网,最近网络终于通了,终于可以更新博 ...
-
angular代码分析之异常日志设计
angular代码分析之异常日志设计 错误异常是面向对象开发中的记录提示程序执行问题的一种重要机制,在程序执行发生问题的条件下,异常会在中断程序执行,同时会沿着代码的执行路径一步一步的向上抛出异常,最 ...
-
Android App监听软键盘按键的三种方式与改变软键盘右下角确定键样式
actionNone : 回车键,按下后光标到下一行actionGo : Go,actionSearch : 放大镜actionSend : SendactionNext : NextactionDo ...
-
oracle中利用trigger,sequence自动生成ID
http://zhanghong.iteye.com/blog/865937 1. 首先创建数据库表 SQL> create table customer( 2 id number(8) no ...
-
PHP+memcache扩展(集成环境wampserver环境下)
按照别人的步骤,安装和配置后没有任何的错误提示!在计算机服务里面也看到memcached服务已经开启,但是phpinfo里面就是没有memcache已经安装成功的信息!后来才发现原来是没有开启wamp ...
-
[转载]SoapUI 参数化&;数据库连接
引用自 : http://www.cnblogs.com/liulinghua90/p/4954045.html 如果是没有代码能力的小白,要利用工具进行接口测试的时候,经常会遇到接口地址 或者接口参 ...
-
9)Java内部类(Inner Class)
内部类:不可以有静态数据,静态方法或者又一个静态内部类 内部类的优点:隐藏类的细节,内部类可以声明为私有.内部类可以访问外部类的对象(包括private) 静态内部类:可以有静态数据,静 ...