专栏首页游戏杂谈游戏繁体化那些让人蛋疼的事儿

游戏繁体化那些让人蛋疼的事儿

项目首先从境内开始做,然后跟中国台湾那边谈了合作,要发行中国台湾版本。这过程中遇到一些问题,特别的坑,特此记录一下

1、文字的翻译;

之前开发的一款页游项目,翻译的工作全部由中国台湾那边负责,他们还专门花钱请人做翻译,翻译的人员是按字收钱的。现在转手游了,首先代理费一般是没有的(即便有也是很低,低的可怜),汉字的翻译全部是我们自己消化了,今时不同往日啊,页游的日子一去不复返了,手游CPA导用户水分也是越来越大,伴随腾讯在IOS榜单阵地的猛烈攻势,小公司小团队投入市场的成本已经高的吓死人了,看这趋势,推广成本只会越来越高,门槛也是越来越高,是好事也是坏事,好事就是小团队、小公司的生存空间被挤压的不行,没钱推啊,好事就是能活下来的就是好样的,可以算B+甚至A级的产品了,对用户来讲是好事。

汉字的翻译,首先word,用google翻译(貌似要“翻”墙)会遇到的问题就是在网页上看是繁体,一复制就变简体了。word的“简转繁”还是挺强大的。不过也有坑,往下看

“冊”是一样,但注就不一样了。单独一个注它是不繁体化的,注册二个字就繁体化了,你说蛋疼不

2、图片的处理;

图片的处理也很疼蛋,美术同事提供的繁体字库,我们用GlyphDesigner生成字体(1.4 破解版,没花钱买正版,嗯,确实很无耻,我自己的MAC都是掏真金白银买正版)。

生成一张png、一份fnt格式的文件,但是那些字体库基本上都是缺胳膊少腿的,就拿二个简单的词汇来看:“注册”、“魅力”,有的“魅”能出来,“册”就出不来,有的“册”能出来,“魅”就出不来,蛋疼不

用黑体吧,看上去不协调,很突兀,好忧伤…

好吧,目前我就只能来二份字体

3、词汇的特殊处理,汉字的翻译也好,字体库的选择也好,这都是死的,麻烦的是有些术语得手工改,这里我就根据之前做台湾版,台湾同胞提供的一些对比信息,我列出一些系统中常用的语句

-------------------------------   我是可恶的分隔线 -------------------------------

哪里    那裡

显摆    誇耀

手链    手鍊

好闲    好閒

几率    機率

淩    凌

糊弄    唬弄

创建    建立

锻炼    鍛練

利索    利落

采回來    採回來

下麵(简体下面)    下面

找茬    找碴

太後(简体太后)    太后

待機(简体待机)    準備

流覽器(简体浏览器)    瀏覽器

信息    訊息

充值    儲值

設置    設定

內置    系統

全屏    全螢幕

連接斷開    連線中斷

連接    連結

登錄    登入

角色名    角色名稱

概率    機率

默認    預設

添加    增加

刷新    更新

剔除    踢除

掉線    斷線

滾屏    滾頻

添加好友    新增好友

刷屏    洗頻

其实繁体化是所有国际化中最为简单的,因为最为相近,之前我搞过越南文,界面都得跟着调整,最主要的是你还看不懂那段文字是什么意思,就更为蛋疼了

---------------------------------- 分隔线 ----------------------------------

cocos2d-x中是通过CCLabelBMFont::createFontChars()来创建文字的,其实就是读取.fnt文件中的char id=N

void CCLabelBMFont::createFontChars()
{
    int nextFontPositionX = 0;
    int nextFontPositionY = 0;
    unsigned short prev = -1;
    int kerningAmount = 0;

    CCSize tmpSize = CCSizeZero;

    int longestLine = 0;
    unsigned int totalHeight = 0;

    unsigned int quantityOfLines = 1;

    unsigned int stringLen = cc_wcslen(m_sString);
    if (stringLen == 0)
    {
        return;
    }

    for (unsigned int i = 0; i < stringLen - 1; ++i)
    {
        unsigned short c = m_sString[i];
        if (c == '\n')
        {
            quantityOfLines++;
        }
    }

    totalHeight = m_pConfiguration->m_nCommonHeight * quantityOfLines;
    nextFontPositionY = 0-(m_pConfiguration->m_nCommonHeight - m_pConfiguration->m_nCommonHeight * quantityOfLines);

    for (unsigned int i= 0; i < stringLen; i++)
    {
        unsigned short c = m_sString[i];

        if (c == '\n')
        {
            nextFontPositionX = 0;
            nextFontPositionY -= m_pConfiguration->m_nCommonHeight;
            continue;
        }

        tFontDefHashElement *element = NULL;

        // unichar is a short, and an int is needed on HASH_FIND_INT
        unsigned int key = c;
        HASH_FIND_INT(m_pConfiguration->m_pFontDefDictionary, &key, element);
       
        // 这里添加如果没有找到的判断,输出相应的key
        if(!element)
        {
            char str[100];
            sprintf(str, "font not found: %d", key);
            CCLuaLog(str);
            return;
        }

        //CCAssert(element, "FontDefinition could not be found!");

        ccBMFontDef fontDef = element->fontDef;

        CCRect rect = fontDef.rect;
        rect = CC_RECT_PIXELS_TO_POINTS(rect);

        rect.origin.x += m_tImageOffset.x;
        rect.origin.y += m_tImageOffset.y;

        CCSprite *fontChar;

        fontChar = (CCSprite*)(this->getChildByTag(i));
        if( ! fontChar )
        {
            fontChar = new CCSprite();
            fontChar->initWithTexture(m_pobTextureAtlas->getTexture(), rect);
            this->addChild(fontChar, 0, i);
            fontChar->release();
        }
        else
        {
            // reusing fonts
            fontChar->setTextureRect(rect, false, rect.size);

            // restore to default in case they were modified
            fontChar->setVisible(true);
            fontChar->setOpacity(255);
        }

        // See issue 1343. cast( signed short + unsigned integer ) == unsigned integer (sign is lost!)
        int yOffset = m_pConfiguration->m_nCommonHeight - fontDef.yOffset;
        CCPoint fontPos = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount,
            (float)nextFontPositionY + yOffset - rect.size.height*0.5f * CC_CONTENT_SCALE_FACTOR() );
        fontChar->setPosition(CC_POINT_PIXELS_TO_POINTS(fontPos));

        // update kerning
        nextFontPositionX += fontDef.xAdvance + kerningAmount;
        prev = c;

        // Apply label properties
        fontChar->setOpacityModifyRGB(m_bIsOpacityModifyRGB);
        // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
        fontChar->setColor(m_tColor);

        // only apply opaccity if it is different than 255 )
        // to prevent modifying the color too (issue #610)
        if( m_cOpacity != 255 )
        {
            fontChar->setOpacity(m_cOpacity);
        }

        if (longestLine < nextFontPositionX)
        {
            longestLine = nextFontPositionX;
        }
    }

    tmpSize.width  = (float) longestLine;
    tmpSize.height = (float) totalHeight;

    this->setContentSize(CC_SIZE_PIXELS_TO_POINTS(tmpSize));
    
}

通过调用JavaScript里的代码“String.fromCharCode(N)”很容易知道是什么字没有找到。我这里遇到的问题是,使用繁体字库时,输入的文字其实是简体,它自动转成繁体的,导致生成的fnt全部为简体字,对应的Unicode码也是简体的Unicode编码,我用node.js把.fnt文件转成最终可用的文字。步骤:

1、先用word将简体字翻译成繁体;

2、读取.fnt文件,替换char对应的id值,它的值由该行的letter获得;

char id=20445 x=203 y=117 width=29 height=29 xoffset=2 yoffset=-1 xadvance=31 page=0 chnl=0 letter="保"

function run(filePath) {
    fs.readFile(filePath, function(err, data) {
        if (err) throw err;        
        var str = data.toString()
        str = str.replace(/(char id=)(\d+)([^\"]+\")(.)(\")/g, function() {
            var arr = [];
            for (var i = 1, len = 5; i <= len; i++) {
                arr.push(arguments[i]);
            }
            arr[1] = arr[3].charCodeAt(0);
            return arr.join("");
        });
        fs.writeFileSync(filePath, str);
    });
}

很方便也很简单:-)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • bugly中批量隐藏版本

    App项目使用Bugly的内测分发功能进行整包的测试,但日积月累之后,版本就会特别多。而线上同时跑的版本可能不过三个左右,那么多版本会干扰到查看崩溃、选择版本,...

    meteoric
  • Android调用浏览器打开网址遇到的问题

    我自己的手机(一加一代,升级了氢OS),然后在点击游戏内一个“隐私政策”-- 需要打开一个网页,然后就crash了。出错的信息如下:

    meteoric
  • 7z压缩与解压命令

    在写很多工具的时候,可能会用到7z命令来进行压缩与解压操作。这里记录二个比较常用的操作:压缩、解压。

    meteoric
  • String引发的提问,我差点跪了

    面试官:下面代码执行结果是什么?String t0 = "helloworld";String t1 = new String("helloworld");Sy...

    JavaQ
  • Spring 框架基础(04):AOP切面编程概念,几种实现方式演示

    将增强添加到目标类的具体连接点上的过程。编译期织入,这要求使用特殊编译器;类装载期织入,这要求使用特殊的类加载器;动态代理织入,在运行期为目标类添加增强生成子类...

    知了一笑
  • 科学瞎想系列之六十五 双馈风力发电机组的双模控制

    1 概述 目前并网型风力发电机组存在两大主流技术路线,一是所谓的双馈型风电机组;二是所谓直驱型风电机组。二者各具千秋,在激烈的市场竞争中并驾齐驱难分高...

    标准答案
  • 以非典数据为参考,这次疫情到了哪个阶段?

    1960年,庚子鼠年。不断扩大的旱情造成的全国性粮食短缺,让中国人在一场饥荒中开始了新的一年。

    Rocky0429
  • Marketing Cloud Launchpad动态tile显示数字刷新的触发逻辑

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    Jerry Wang
  • 聊聊skywalking的lettuce-plugin

    skywalking-6.6.0/apm-sniffer/optional-plugins/lettuce-5.x-plugin/src/main/resour...

    codecraft
  • 聊聊skywalking的lettuce-plugin

    skywalking-6.6.0/apm-sniffer/optional-plugins/lettuce-5.x-plugin/src/main/resour...

    codecraft

扫码关注云+社区

领取腾讯云代金券