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包还需要用 包裹在组件的最外层,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 (
                
                    
                
            );
        }
    }
    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/.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文件,在这里独立处理中英文翻译。

写的有些啰嗦,溜了。

你可能感兴趣的:(scratch3.0之语言翻译国际化)