scratch3.0之语言翻译国际化

这一篇要写的是scratch-gui的多语言国际化翻译。LLK团队为了让非英语地区的同学参与scratch,构建了scratch-l10n项目,当然scratch-gui的语言国际化采用的便是scratch-l10n,然后通过react-intl国家化组件格式化。简单的说,就是scratch-l10n项目导出各种语言的翻译文件,scratch-gui导入并配置再由react-intl组件来格式化成用户所选择的语言。下面我们削微了解一下,之后做点有用的事情。

scratch-gui如何实现多语言

  1. 引入react-intl
    ”src\containers\gui.jsx”是从gui入口我们遇到的第一个用到多语言的地方,它引入了
import {defineMessages, injectIntl, intlShape} from 'react-intl';

然后还需要在组件中添加:

  • defineMessages定义一个多语言对象,id相当于一个key,将来在scratch-l10导出的多语言文件中查找翻译字符串,defaultMessage表示默认显示,如果id没有对应的值,就显示默认的,description表示属性描述。
const messages = defineMessages({
    defaultProjectTitle: {
        id: 'gui.gui.defaultProjectTitle',
        description: 'Default title for project',
        defaultMessage: 'Scratch Project'
    }
});
  • 在组件中注入 injectIntl
const ConnectedGUI = injectIntl(connect(
    mapStateToProps,
    mapDispatchToProps,
)(GUI));
  1. 通过 LocalizationHOC 高阶组件为GUI提供本地化状态
    react-int包还需要用 <IntlProvider />包裹在组件的最外层,scratch-gui中通过LocalizationHOC高阶组件来封装:
const WrappedGui = compose(
    LocalizationHOC, // 提供本地化状态的高阶组件。
    ErrorBoundaryHOC('Top Level App'), // 提供封装组件错误边界的高阶组件,传入一个string类型的action,用于GA跟踪错误标签
    FontLoaderHOC, // 加载字体高阶组件
    QueryParserHOC, // 从URL查询字符串获取参数并初始化redux状态的高阶组件
    ProjectFetcherHOC, // 提供通过id加载项目的行为的高阶组件。如果没有id,则加载默认的项目。
    ProjectSaverHOC, // 提供项目保存功能的高阶组件
    vmListenerHOC, // 监听VM事件的高阶组件
    vmManagerHOC, // 发送VM事件的高阶组件
    cloudManagerHOC // 连接云服务器的高阶组件
)(ConnectedGUI);

看下LocalizationHOC组件:

import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';

import ConnectedIntlProvider from './connected-intl-provider.jsx';

/*
 * Higher Order Component to provide localiztion state. Creates a nested IntlProvider
 * to handle Gui intl context. The component accepts an onSetLanguage callback that is
 * called when the locale chagnes.
 * @param {React.Component} WrappedComponent - component to provide state for
 * @returns {React.Component} component with intl state provided from redux
 */
// 提供本地化状态的高阶组件。创建一个嵌套的IntlProvider来处理Gui intl上下文。组件接受一个onSetLanguage回调函数,该函数在语言环境chagnes时调用。
const LocalizationHOC = function (WrappedComponent) {
    class LocalizationWrapper extends React.Component {
        componentDidUpdate (prevProps) {
            if (prevProps.locale !== this.props.locale) {
                this.props.onSetLanguage(this.props.locale);
            }
        }
        render () {
            const {
                locale, // eslint-disable-line no-unused-vars
                onSetLanguage, // eslint-disable-line no-unused-vars
                ...componentProps
            } = this.props;
            return (
                <ConnectedIntlProvider>
                    <WrappedComponent {...componentProps} />
                </ConnectedIntlProvider>
            );
        }
    }
    LocalizationWrapper.propTypes = {
        locale: PropTypes.string,
        onSetLanguage: PropTypes.func
    };

    LocalizationWrapper.defaultProps = {
        onSetLanguage: () => {}
    };

    const mapStateToProps = state => ({
        locale: state.locales.locale
    });

    const mapDispatchToProps = () => ({});

    return connect(
        mapStateToProps,
        mapDispatchToProps
    )(LocalizationWrapper);
};

export default LocalizationHOC;

  1. 添加 localedata

定义在“src\reducers\locales.js”:

import {addLocaleData} from 'react-intl';

import {localeData} from 'scratch-l10n';
// 国际化只取中英文
import editorMessages from '../locales/editor-msgs';
// import editorMessages from 'scratch-l10n/locales/editor-msgs';
import {isRtl} from 'scratch-l10n';

addLocaleData(localeData);

const UPDATE_LOCALES = 'scratch-gui/locales/UPDATE_LOCALES';
const SELECT_LOCALE = 'scratch-gui/locales/SELECT_LOCALE';

const initialState = {
    isRtl: false,
    locale: 'en',
    messagesByLocale: editorMessages,
    messages: editorMessages.en
};
...

这里我们可以看到 scratch-l10n的多语言文件由node-modlue包的scratch-l10n/locales/editor-msgs.js文件导出,这里我进行了一个处理,后面再介绍

到这里,多语言就配置好了,下面是使用

  1. 应用 FormattedMessage 来格式化多语言
 setReduxTitle (newTitle) {
        if (newTitle === null || typeof newTitle === 'undefined') {
            this.props.onUpdateReduxProjectTitle(
                this.props.intl.formatMessage(messages.defaultProjectTitle)
            );
        } else {
            this.props.onUpdateReduxProjectTitle(newTitle);
        }
    }

这里是gui组件挂载完成后 ,componentDidMount()生命周期函数调用:

  componentDidMount () {
        setIsScratchDesktop(this.props.isScratchDesktop);
        this.setReduxTitle(this.props.projectTitle);
        this.props.onStorageInit(storage);
    }

我们可以看到挂载完成后,设置项目标题,并保存到redux中,在这里用到了多语言
this.props.intl.formatMessage(messages.defaultProjectTitle)。在第一步injectIntl注入到组件后,组件的prop上就有了一个intl对象,然后formatMessage来应用。

添加新的翻译

今天我想添加一下新的翻译,大概看了下官方的方法也查阅了网上各位前辈的方法,大概总结一下吧:

  1. 官方的scratch-l10n库基本是不维护的,而是通过Transifex 来实现自动化构建的(难怪源码里.tx和translations文件夹不知道是干啥的)。也就是说你可以fork一下,然后在Transifex上创建自己的库。这也太吓人了。。。忽略

  2. 官方的wiki里也给另一个方法:

  • 将新资源添加到.tx/config-例如,添加笔扩展名字符串:
    [experimental-scratch.pen]
    file_filter = pen/<lang>.json
    source_file = pen/en.json
    source_lang = en
    type = CHROME
  • 运行tx pull -a -s以下拉所有翻译和源文件。[注意:这还将更新具有新翻译的其他资源。如果您只想更新新资源,则将添加-r 'experimental-scratch.pen'(即资源段)到命令中。
  • 将新组件添加到中的组件列表中build-data.js。脚本假定组件的名称是包含翻译的文件夹的名称。

我没试过,因为我下载的是gui的zip文件,大概意思就是通过这个方式向Transifex提交新的翻译?,不管,我还是觉得麻烦

  1. 网上找到一篇文章,他介绍了如何添加新的翻译,但是我看到他把scratch-l10n项目clone下来操作,感觉还是麻烦。于是我按照我的需求改进了一下:

  2. 新建翻译文件

我参考卡拉的版本魔改,因此只保留中英文,但是每次都要在那么多资源里改,太麻烦,于是新建了一个资源,替换了原来的资源导入:

// 国际化只取中英文
import editorMessages from '../locales/editor-msgs';
// import editorMessages from 'scratch-l10n/locales/editor-msgs';

这样做有两个好处:

  • 新增修改量小,当然如果你要把所有语言都翻译一边,量没差,不过找的也累。。
  • 因为修改了node-module里的文件,git通常会忽略这个文件夹,这样做其实是非常不好的,所以我在src里,新增了locals文件,在这里独立处理中英文翻译。

写的有些啰嗦,溜了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容