前言
Mind+从V1.6.5开始开放实时模式用户库,内部兼容Scratch扩展语法,,本文档将介绍如何开发Mind+实时模式用户库,阅读此文档需要你掌握JavaScript的基础知识以及了解一些ES6的新特性。
如果你在阅读过程中产生疑惑, 请加入官方交流群(671877416)。
在开发之前, 你需要知道能做出什么东西.
-
添加不依赖硬件的模块 可以很方便的使用第三方JS库
-
为mind+中已有的主板/套件增加硬件小模块
-
添加自己的硬件套件并通信
- [x] 串口
- [ ] wifi(暂不支持)
- [ ] 蓝牙(暂不支持)
准备
-
下载并安装node.js (推荐下载.msi安装包),使用默认选项安装直到完成即可。
-
点击下载示例开发包并解压 : MindPlusOnlineExtensionDevelopPackage
-
在开发包所在目录文件夹窗口输入cmd打开命令提示符窗,输入
npm install
回车,会自动安装所有依赖包(电脑保持联网),等待进度条走完光标再次闪烁即为安装完成。
注意:一定要在webpack.config这个文件所在目录下打开cmd,否则无法安装。
![](https://img.dfrobot.com.cn/wiki/none/b8e7d5ab166a343cb8e1b81c09aaa656.png)
![](https://img.dfrobot.com.cn/wiki/none/4f27b0fc3e1bbd596bbf07268634ed05.png)
-
安装一个ide,推荐**VSCode**,打开开发包
开发包说明
-
build
打包文件的输出路径,每次编译后重新生成,开发时Mind+中导入此路径下的config.json方便刷新 -
examples
其他参考示例程序 -
node_modules
运行初始化install之后安装的依赖包,用户无需操作
-
scripts
编译引导文件,用户无需操作
-
src
编译源文件及相关库文件,用户无需操作
-
template
用户库模板文件夹connfig.json
用户库配置文件javascript
_images
文件夹 存放用于展示扩展的图片和icon_locales
翻译文件index.js
在这里编写你的积木功能代码
---
开发流程
准备工作你已经做好了吧!
跟着下面的步骤, 一步一步创建属于你的用户库.
1.导入Mind+
- 打开Mind+,切换到实时模式,打开扩展,选择micro:bit,然后切换到用户库,选择build目录下的config.json,加载示例库。
注意:build目录仅做导入,如果要修改则应该修改temple目录下的文件。
接下来介绍和尝试修改这个库。
2. 配置用户库
==template 文件夹==是一个用户库模板, 在这里配置你的用户库
-
配置
config.json
调整config.json中的字段为自己想修改的内容。
{
"name": {
"zh-cn": "示例",
"en": "Example"
},
"description": {
"zh-cn": "示例程序的描述",
"en": "Description of the example"
},
"author": "dfrobot",
"email": "xxxx@dfrobot.com",
"license": "MIT",
"isBoard": false,
"id": "example0729",
"version": "0.0.1",
"platform": ["win"],
"asset": {
"javascript": {
"version": "0.0.1",
"dir": "javascript/",
"board": ["arduino", "microbit"],
"main": "main.js"
}
}
}
-
配置选项
-
name
object- ==必需==; 在扩展库中显示的名称 -
description
object- ==必需==; 在扩展库中显示的描述 -
author
string- ==必需==; 作者名, 使用英文字母表示, 尽量个性化. -
email
string- 可选; 当版本更新需要修改用户库或用户反馈,将通过邮件通知开发者(预留功能). -
license
string- ==必需==; 默认MIT协议. -
isBoard
boolean- 当前扩展是否为主控(预留功能, 默认false). -
id
string- ==必需==; 同一作者的不同模块需要设置不同的id,建议使用英文和数字符号命名. -
version
string- ==必需==; 版本信息(版本控制预留功能). -
platform
array- ==必需==; "win" | "mac" | "web" 表示Mind+的windows桌面版/mac桌面版/网页版, 当前仅支持 "win". -
asset
object- ==必需==; 资源文件夹.-
javascript
object- ==必需==; Mind+实时模式的配置信息.-
version
string- ==可选==; js库版本. -
dir
string- ==必需==; 文件夹名称,默认即可. -
board
array- 可选; 所支持的主板如果是不依赖主板的扩展, 则留空. -
main
string- ==必需==; 入口文件,默认即可.
-
-
-
-
添加扩展图片
将图片复制到到template/_images
文件夹, ==必须命名为featured.png==(600*372, png格式)
没有图片, 则显示默认图片 -
添加积木icon
将svg格式icon复制到到template/_images
文件夹, ==必须命名为icon.svg==,推荐在https://www.iconfont.cn/iconfont下载白色的icon文件。
3. 定义扩展(Extension)
在 template/javascript/index.js 文件中编辑你的代码.
扩展可以被定义成一个class, 必须有一个getInfo
函数.
class YourExtension {
constructor(runtime) {
this.runtime = runtime;
}
getInfo () {
// ...
}
}
module.exports = YourExtension;
getInfo
返回一个包含积木块和扩展本身配置信息的对象
import blockIconURI from './image/icon.svg';
class YourExtension {
// ...
getInfo () {
return {
name: 'Extension Test',
color:'#8b92e8',
blockIconURI: blockIconURI,
blocks: [
// ...
]
}
}
}
getInfo
可返回的参数:
-
name
string- 可选; 扩展的名称. -
color
string- 可选; 积木颜色. -
blockIconURI
string- 可选; 积木块的图标(推荐40*40).
showStatusButton
boolean- 可选; 是否显示连接图标,用于串口连接.
blocks
array- ==必需==; 积木块列表.menus
object- 可选; 积木块的下拉菜单列表.blockIconWidth
number- 可选; 设置图标的宽度, 默认是40.blockIconHeight
number- 可选; 设置图标的高度, 默认是40.
定义积木块
const ArgumentType = require('./extension-support/argument-type');
const BlockType = require('./extension-support/block-type');
import blockIconURI from './image/icon.svg';
class YourExtension {
// ...
getInfo () {
return {
// ...
blocks: [
{
opcode: 'myAdd',
blockType: BlockType.REPORTER,
// block描述: []包裹的是参数
text: '[NUM1] + [NUM2]',
arguments: {
NUM1: {
// 参数类型
type: ArgumentType.STRING,
// 默认显示的值
defaultValue: 1
},
NUM2: {
type: ArgumentType.STRING,
defaultValue: 2
}
}
}
]
}
}
}
module.exports = YourExtension;
还要为每个opcode
定义一个功能同名函数,积木被执行的时候调用此函数:
class YourExtension {
// ...
myAdd(args) {
return parseInt(args.NUM1) + parseInt(args.NUM2);
}
}
blockType
: 积木类型详细文档.
ArgumentType
: 参数类型详细文档.
定义下拉菜单(menu)
return {
// ...
blocks: [
{
// ...
arguments: {
WEEK: {
type: ArgumentType.STRING,
menu: 'week',
defaultValue: '+'
}
}
}
],
menus: {
week: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',]
}
}
使用如下格式可实现下拉菜单支持多国语言,通过text字符绑定翻译值,下拉菜单的显示会随用户的语言环境改变, 但实际的值必须保持不变.
return {
// ...
menus: {
week: [
{
text: formatMessage('Sunday')
value: 'Sunday'
},
{
text: formatMessage('Monday')
value: 'Monday'
},
{
text: formatMessage('Tuesday')
value: 'Tuesday'
},
// ...
]
}
}
完整的例子请参考examples/
下的例子.
4. 打包导入
开发包中环境已经配置好了, 直接使用即可.
-
编译。每次修改代码后均需要重新编译,在cmd命令行或vscode终端下运行
npm run build
如果编译后没有生成build目录说明有错误产生请查看错误信息调整代码。
小技巧:输入过一次命令后,再次按键盘上键即可自动填充历史命令。 -
导入。编译完成后会在
build
目录下生成一个用户库,在Mind+中导入build目录下项目中的config.json,这里前面已经导入则点击刷新即可。
---
积木类型
HAT 帽子型
class HatBlcok {
// ...
getInfo () {
return {
// ...
blocks: [
{
opcode: 'myEvent',
blockType: BlockType.HAT,
// 默认值为true
isEdgeActivated: false,
shouldRestartExistingThreads: false,
// ...
}
]
}
}
myEvent (args) {
// ...
// 返回true: 运行该事件头下的积木块
return true;
}
}
isEdgeActivated
: 如果为true, runtime每隔17ms调用一次myEvent
函数; 如果为false, 必须调用 runtime.extensionStartHats
函数触发myEvent
.
shouldRestartExistingThreads
: 如果为false, 该事件头下的积木块正在运行时, 不会被下一次事件触发所打断.
BOOLEAN 菱形
布尔类型, 返回布尔值
REPORTER 圆形
报告类型, 返回 String 和 Number
COMMAND 方形
命令类型, 没有返回值.
checkboxInFlyout勾选框
积木可以设置勾选框,勾选之后舞台实时显示返回值,并且积木自动执行。
{
opcode: 'rec',
blockType: BlockType.REPORTER,
checkboxInFlyout:true,
text: 'checkbox test'
}
}
空行
{
opcode: 'get_elements_item',
blockType: BlockType.REPORTER,
text: '获取列表第[NUM]项',
arguments: {
NUM:{
type: ArgumentType.NUMBER,
defaultValue: 1
}
}
},
'---', //空行
{
opcode: 'selection_sort',
blockType: BlockType.COMMAND,
text: '将列表数字进行排序'
},
输入控件类型
STRING 文本
代码示例:
// ...
STRING: {
type: ArgumentType.STRING,
defaultValue: "文本输入框",
}
参数说明:
type:必填。固定格式;
defaultValue: 可选。默认显示内容;
NUMBER 数字
代码示例:
// ...
NUMBER: {
type: ArgumentType.NUMBER,
defaultValue: 123,
}
参数说明:
type:必填。固定格式;
defaultValue: 可选。默认显示内容;
RANGE 带输入范围的数字
代码示例:
COL2:{
type: ArgumentType.RANGE,
defaultValue: 123,
inputParams:{
rangeMax:200,
rangeMin:100
}
}
参数说明:
type:必填。固定格式;
defaultValue: 可选。默认显示内容;
BOOLEAN 布尔值
代码示例:
// ...
BOOLEAN: {
type: ArgumentType.BOOLEAN
}
参数说明:
type:必填。固定格式;
可拖入圆形积木的下拉框
代码示例:
return {
// ...
blocks: [
{
// ...
MENUDROPROUND: {
type: ArgumentType.NUMBER,
menu: 'exMenu',
defaultValue: b
}
}
],
menus: {
exMenu: {
items: [{text: "选项1" ,value:'a'}, {text: "选项2" ,value:'b'}, {text: "选项3" ,value:'c'}]
}
}
}
参数说明:
type:必填。固定格式;
不可拖入圆形积木的下拉框
代码示例:
const menu_test = [["QQ", "1"],["WW", "2"],["EE", "3"], ["RR", "4"]];
// ...
MENUDROP: {
type: ArgumentType.STRING,
onlyField: true,
options: menu_test,
defaultValue: menu_test[1][1]
}
参数说明:
type:必填。固定格式;
onlyField: 必填。固定为true,控制输入框不可拖入圆形积木,需要与options一起使用,不可与menu一起使用;
options:下拉框数据,数组。前者为显示内容,后者为对应的值(数字类型请使用引号);
defaultValue :默认选中的数据;
ANGLE 角度
代码示例:
// ...
ANGLE: {
type: ArgumentType.ANGLE,
defaultValue: 90
}
参数说明:
type:必填。固定格式;
defaultValue:可选。默认参数;
COLORPICKER 取色器
代码示例:
// ...
COLORPICKER: {
type: ArgumentType.COLORPICKER,
defaultValue: "#FF0000"
}
参数说明:
type:必填。固定格式;
defaultValue:可选。默认颜色,颜色格式为十六进制;
COLORPALETTE 色板
代码示例:
// ...
COLORPALETTE: {
type: ArgumentType.COLORPALETTE,
defaultValue: "#0000FF",
inputParams: {
//colours: ["#fff", "#f00", "#0f0", "#00f", "#ff0", "#0ff", "#f0f", "#000"],
columns: 6
},
}
参数说明:
type:必填。固定格式;
defaultValue:可选。默认颜色,颜色格式为十六进制;
inputParams:
colours:可选。可以选择的颜色数组,颜色格式为十六进制。默认为mind+提供的色块。
columns:可选。每行显示的色块个数。
NOTE 内置琴键
代码示例:
// ...
PIANO: {
type: ArgumentType.NOTE,
defaultValue: 60,
}
参数说明:
type:必填。固定格式;
defaultValue:可选。默认值。
PIANO 钢琴键
参数说明:
type:必填。固定格式;9.琴键
代码示例:
// ...
PIANO: {
type: ArgumentType.PIANO
}
参数说明:
type:必填。固定格式;
MATRIXICONS 点阵
代码示例:
const matrixList = [{matrix: "0101011111111110111000100", value: "HEART", isBuiltIn: true }];
// ...
MATRIXICONS: {
type: ArgumentType.MATRIXICONS,
defaultValue: "0101010101100010101000100",
inputParams: {
colour: "#ff0000",
row: 8,
column: 8,
isSplit: false,
builtinMatrixs: matrixList
}
}
参数说明:
type:必填。固定格式;
defaultValue:必填。点阵列表中,1表示点亮0熄灭;
inputParams:
color: 可选。控件默认的底色;
row: 可选。点阵的列数,取值范围为[5, 16],默认为5;
column: 可选。点阵的行数,取值范围为[5, 32],默认为5;
isSplit:可选。按列数平分,左右隔开显示,true或false;
builtinMatrixs:可选。控件内置的点阵。使用mind+默认提供的图案(V1.7.1以上版本),参数固定为arguments[0]
。如需设置为自定义的点阵,具体格式参照代码中的matrixList
;
builtinFunc:可选。控件内置的点阵函数。如设置,需返回数组,格式与matrixList
一致;
SETTINGS 设置框
代码示例:
const getComponentInfoForLoad = ()=>{
return {
//icon: "SETTING.SVG",
content: [
{
description: "名字",
saveId: 'name_id',
type: 'text',
value: '张衡',
IFAdd: true,
addLength: 6
},
{
description: "年龄",
saveId: 'age_id',
type: 'text',
value: 23
}
]
}
}
// ...
SETTING: {
type: ArgumentType.SETTINGS,
inputParams: {
componentInfo: getComponentInfoForLoad(),
color: "#FF0000"
}
}
参数说明:
type:必填。固定格式;
inputParams:
componentInfo: 必填。函数,返回值为文本框结构描述。
color: 控件的基础色。
函数getComponentInfoForLoad说明:
icon: 可选。控件图标,传入格式为base64或url,仅支持svg,jpg和svg图片。默认为mind+提供的齿轮图标。
content: 必填。包含的输入框,数组中的元素描述文本框。
description: 文本框名称;
saveId: 必填。可根据id获取对应输入框的值;
type: 必填。文本框类型,支持text,password,file(文件夹), single_file(文件),number和textarea;
value: 默认值;
IFAdd: 可选。控制增加输入框,默认为false。
addLength: IFAdd为true时必填。控制增加输入框的最多个数。
设置框控件返回各元素ID与值组成的JSON格式的字符串。
API
1.runtime
runtime
提供了积木运行、获取内置主板和串口的方法.
-
runtime.getLocale()
- 返回:
<string>
获取 Mind+ 当前设置的语言环境.
console.log(runtime.getLocale()); // 'en' 'zh-cn' ...
在使用时将其绑定到
formatMessage
就可以了.formatMessage = formatMessage.bind(null, runtime.getLocale);
- 返回:
-
runtime.extensionStartHats(requestedHatOpcode, optMatchFields, optTarget)
requestedHatOpcode
<string>
由id
和opcode
构成optMatchFields
<object>
传递给block的参数, 若没有参数则为空optTarget
<string>
运行指定target中的block, 若为空, 运行所有target中的block(精灵和舞台都作为target)
触发
HAT
(帽子形状)积木块的运行{ id: 'extensionTest1', ... blocks: [ { // 事件类型 opcode: 'whenReceive', blockType: BlockType.HAT, ... arguments: { TEXT: { ... } } } ] } ... runtime.extensionStartHats(`${this.getInfo().id}.whenReceive`, {TEXT: '123'})
获取
id
的方法
考虑到用户命名id
会有重名的情况, 所以 Mind+ 在内部生成一个唯一id
, 并通过构造函数传参以及重新封装getInfo
方法.this.getInfo().id
或:
class YourExtension { constructor (runtime, id) { ... } }
-
runtime.getBoardName()
- 返回:
<string>
当前选择的主板或套件名称, 若没有返回''
console.log(runtime.getBoardName()); // 'microbit' 'maqueen' 'arduino' 'esp32' 'maixduino' 'arduinonano' // 'leonardo' 'arduinounor3' 'max' 'maxbot' 'romeo' 'vortex' // 'mega2560' 'firebeetleesp32' 'telloesp32' 'calliope'
- 返回:
-
runtime.getBoard()
- 返回:
<object>
当前选择的主板或套件对象, 若没有返回null
- 返回:
-
runtime.isBuildinBoardConnected()
- 返回:
<boolean>
当前主板或套件是否连接
- 返回:
-
runtime.requestBreakThreads()
和util.yield()
配合使用, 跳过本次执行, 再下一次事件循环继续执行doSomething () { if (...) { do ... // 串口未被占用, 执行某些操作 } else { // 串口被占用 util.yield(); // yield出去, 等待下次事件循环执行 runtime.requestBreakThreads(); } }
底层会以
17ms
的间隔循环扫描工作区的所有block, 运行符合条件(被点击/事件被触发)的顶部block, 当遇到yield
或promise
时, 就暂停执行该block后面的程序, 等待下一次循环再来判断条件是否达成, 执行后面的block或继续等待下一次循环. -
runtime.registerPeripheralExtension(extensionId, extension)
extensionId
<string>
扩展的唯一id
extension
<object>
外设对象
注册外设对象, 该对象必须包含连接处理的相关方法
class YourExtension { constructor (runtime, id) { runtime.registerPeripheralExtension(id, extension); ... } }
extension
对象必须包含scan
,connect
,disconnect
,isConnected
四个方法class Extension { constructor (runtime, id) { runtime.registerPeripheralExtension(id, extension); ... } scan () { // 扫描设备 ... // 扫描到的设备列表 const peripherals = [{ name: `xxx`, // 显示的名称 peripheralId: 'xxxx' // 传递的参数 }, ...] this._runtime.changePeripheralStatus(PERIPHERAL_LIST_UPDATE, peripherals); } connect (peripheralId) { // peripheralId 接口的id(COM2 ...) ... } disconnect () { // 断开当前设备的连接 ... } isConnected () { // 当前设备是否连接 ... } }
-
runtime.getSerialport()
- 返回:
<object>
返回 Mind+ 中内置的serialport
对象
Mind+ 中内置的serialport
是5.0.0
版本
详细文档
- https://github.com/EmergingTechnologyAdvisors/node-serialport/blob/5.0.0-beta3/README.md
- 百度搜索
Serialport@5.0.0 文档
npm i serialport@5
查看相关源码
- 返回:
-
runtime.changePeripheralStatus(event, ...args)
event
<object>
触发相应事件, 改变设备连接状态'PERIPHERAL_CONNECTED'
设备已连接'PERIPHERAL_DISCONNECTED'
设备断开连接'PERIPHERAL_SCAN_TIMEOUT'
扫描设备超时'PERIPHERAL_LIST_UPDATE'
更新设备列表'PERIPHERAL_REQUEST_ERROR'
设备连接出错
详细用法查看
/examples/util-serialport
例程
2.board对象
-
board.digitalRead(pin)
pin
<number>
引脚- 返回:
<number>
高电平为1
低电平为0
-
board.analogRead(analogPin)
analogPin
<number>
A0
为0
- 返回:
<number>
-
board.digitalWrite(pin, value)
pin
<number>
引脚value
<number>
高电平为1
低电平为0
-
board.analogWrite(pin, value)
analogPin
<number>
A0
为0
value
<number>
-
board.serialWriteIsAvailable()
- 返回:
<boolean>
串口写是否被占用
当两段积木块同时运行且都有串口操作时, 就会发生混乱, 所以需要serialWriteIsAvailable
和setSerialWriteBusy
来保证串口写互斥使用.
- 返回:
-
board.setSerialWriteBusy(flag)
flag
<boolean>
设置串口写被占用,true
为占用,false
解除占用
-
board.i2cConfig(options)
options
<object>
delay
<number>
address
<number>
- i2c初始化,相当于arduinoC下的
Wire.begin()
board.i2cConfig(); // 可以忽略参数
-
board.i2cWrite(address, registerOrData, inBytes)
address
<number>
i2c
地址registerOrData
<number|array>
寄存器地址或数据bufinBytes
<array>
-
用户库中javascript语法的写法:
// 1. 往一个寄存器写值 board.i2cWrite(i2caddr, reg, value); // 2. 往多个寄存器写值 board.i2cWrite(i2caddr, reg, [value, value2]); // 2. 往i2c设备写值, 不指定寄存器地址 board.i2cWrite(i2caddr, [value, value2, value3]);
-
对比ArduinoC下的代码:
// 1. 往一个寄存器写值 Wire.beginTransmission((uint8_t)i2caddr); // i2c地址 Wire.write((uint8_t)reg); // 寄存器地址 Wire.write((uint8_t)value); // 写入的值 Wire.endTransmission(); // 2. 往多个寄存器写值 Wire.beginTransmission((uint8_t)i2caddr); // i2c地址 Wire.write((uint8_t)reg); // 起始寄存器地址 Wire.write((uint8_t)value); // 写入的值 Wire.write((uint8_t)value2); // 写入的值 Wire.endTransmission(); // 2. 往i2c设备写值, 不指定寄存器地址 Wire.beginTransmission((uint8_t)i2caddr); // i2c地址 Wire.write((uint8_t)value); // 写入的值 Wire.write((uint8_t)value2); // 写入的值 Wire.write((uint8_t)value3); // 写入的值 Wire.endTransmission();
board.sendI2CReadRequest(address, numBytes, callback)
address
<number>
i2c
地址numBytes
<number>
请求的字节数callback
<function>
收到数据的回调函数
-
用户库中javascript语法的写法:
```JavaScript // 注:读i2c设备为异步读取,可以配合Promise达到积木返回的效果,详情参考示例程序中i2c的读取代码 // 1. 请求3个字节的数据 board.sendI2CReadRequest(i2caddr, 3, function callback(data/*Array*/) { }) // 2. 从某个寄存器地址请求3个字节的数据 i2cRead(i2caddr, reg, 3, function callback(data/*Array*/) { }) ```
-
对比ArduinoC下的代码:
// 1. 请求3个字节的数据 Wire.requestFrom((uint8_t)i2caddr, (byte)3); // 请求数据 Wire.read(); // 读取 Wire.read(); Wire.read(); // 2. 从某个寄存器地址请求3个字节的数据 Wire.beginTransmission((uint8_t)i2caddr); // i2c地址 Wire.write((uint8_t)reg); // 起始寄存器地址 Wire.endTransmission(); Wire.requestFrom((uint8_t)i2caddr, (byte)3); // 请求数据 Wire.read(); // 读取 Wire.read(); Wire.read();
-
board.i2cWriteReg(address, register, byte)
address
<number>
i2c
地址register
<number>
寄存器地址byte
<number>
写入的字节
-
board.i2cReadOnce(address, register, bytesToRead, callback)
address
<number>
i2c
地址register
<number>
寄存器地址bytesToRead
<number>
请求的字节数callback
<function>
收到数据的回调函数
-
board.i2cSetClock(frequency)
frequency
<number>
时钟频率
-
翻译
如何使用翻译
- 导入翻译模块
import * as translation from './translation/index';
let { setLocaleData, formatMessage } = translation;
这种导入方式是为了给 formatMessage
绑定参数
-
初始化翻译内容
setLocaleData(localesData) 初始化翻译内容
localesData 结构{ "en": { "say": "hello" }, "zh-cn": { "say": "你好" } }
-
formatMessage 翻译
formatMessage(getLocale, message)-
第一个参数是获取当前语言环境的方法, 第二个参数是待翻译的文本对象, 如: {"id": "say", "default": "hello"}
-
但是每次传入getLocale方法不太方便, 所以推荐使用前先绑定getLocale
formatMessage = formatMessage.bind(null, getLocale)
-
翻译例子
有少量语句需要翻译
import BlockType from './extension-support/block-type';
// 导入翻译模块的方法
let { setLocaleData, formatMessage } = require('./translation/index');
// 初始化翻译内容
setLocaleData({
"en": {
"extensionName": "Extension Test",
"say": "hello!"
},
"zh-cn": {
"extensionName": "扩展测试",
"say": "你好!"
}
});
class YourExtension {
constructor(runtime) {
this.runtime = runtime;
// 绑定获取语言环境的方法, 这个步骤不可缺少
formatMessage = formatMessage.bind(null, runtime.getLocale);
}
getInfo () {
return {
id: 'extensionTest',
name: formatMessage({
id: 'extensionName',
default: 'Extension Test' // 默认值, 没有翻译时返回此默认值, 可忽略
}),
blocks: [
{
opcode: 'say',
text: formatMessage({
id: 'say',
default: 'hello'
}),
blockType: BlockType.REPORTER,
}
],
}
}
say () {
return 'hello';
}
}
export default YourExtension;
有很多语句需要翻译
当有很多语句需要翻译时, 这种添加翻译语句的方式就不适用了, 不方便管理翻译文件
setLocaleData({
en: {
'extensionName': 'Extension Test',
'say': 'hello!'
},
zh: {
'extensionName': '扩展测试',
'say': '你好!'
}
});
推荐下面的用法:
- 1.增加翻译文件
将翻译文件放在 /template/javascript/_locales 目录下, 命名符合 "en.js"、"zh-cn.js" 这样的格式, 在打包过程中会自动编译成对应文件。
en.json
{
'extensionName': 'Extension Test',
'say': 'hello!'
}
zh-cn.json
{
'extensionName': '扩展测试',
'say': '你好!'
}
- 2.导入 locales
import BlockType from './extension-support/block-type';
// 导入翻译模块的方法
let { setLocaleData, formatMessage } = require('./translation/index');
// 导入translation/locales/index.js
import locales from './translation/locales';
// 初始化翻译内容
setLocaleData(locales);
class YourExtension {
constructor(runtime) {
this.runtime = runtime;
// 绑定获取语言环境的方法, 这个步骤不可缺少
formatMessage = formatMessage.bind(null, runtime.getLocale);
}
getInfo () {
return {
id: 'extensionTest',
name: formatMessage({
id: 'extensionName',
default: 'Extension Test' // 默认值, 没有翻译时返回此默认值, 可忽略
}),
blocks: [
{
opcode: 'say',
text: formatMessage({
id: 'say',
default: 'hello'
}),
blockType: BlockType.REPORTER,
}
],
}
}
say () {
return 'hello';
}
}
export default YourExtension;
语言环境(locales)
const locales = {
'ab': {name: 'Аҧсшәа'},
'ar': {name: 'العربية'},
'am': {name: 'አማርኛ'},
'az': {name: 'Azeri'},
'id': {name: 'Bahasa Indonesia'},
'be': {name: 'Беларуская'},
'bg': {name: 'Български'},
'ca': {name: 'Català'},
'cs': {name: 'Česky'},
'cy': {name: 'Cymraeg'},
'da': {name: 'Dansk'},
'de': {name: 'Deutsch'},
'et': {name: 'Eesti'},
'el': {name: 'Ελληνικά'},
'en': {name: 'English'},
'es': {name: 'Español'},
'es-419': {name: 'Español Latinoamericano'},
'eu': {name: 'Euskara'},
'fa': {name: 'فارسی'},
'fr': {name: 'Français'},
'ga': {name: 'Gaeilge'},
'gd': {name: 'Gàidhlig'},
'gl': {name: 'Galego'},
'ko': {name: '한국어'},
'hy': {name: 'Հայերեն'},
'he': {name: 'עִבְרִית'},
'hr': {name: 'Hrvatski'},
'zu': {name: 'isiZulu'},
'is': {name: 'Íslenska'},
'it': {name: 'Italiano'},
'ka': {name: 'ქართული ენა'},
'sw': {name: 'Kiswahili'},
'ht': {name: 'Kreyòl ayisyen'},
'ku': {name: 'Kurdî'},
'ckb': {name: 'کوردیی ناوەندی'},
'lv': {name: 'Latviešu'},
'lt': {name: 'Lietuvių'},
'hu': {name: 'Magyar'},
'mi': {name: 'Māori'},
'nl': {name: 'Nederlands'},
'ja': {name: '日本語'},
'ja-Hira': {name: 'にほんご'},
'nb': {name: 'Norsk Bokmål'},
'nn': {name: 'Norsk Nynorsk'},
'uz': {name: 'Oʻzbekcha'},
'th': {name: 'ไทย'},
'km': {name: 'ភាសាខ្មែរ'},
'pl': {name: 'Polski'},
'pt': {name: 'Português'},
'pt-br': {name: 'Português Brasileiro'},
'rap': {name: 'Rapa Nui'},
'ro': {name: 'Română'},
'ru': {name: 'Русский'},
'sr': {name: 'Српски'},
'sk': {name: 'Slovenčina'},
'sl': {name: 'Slovenščina'},
'fi': {name: 'Suomi'},
'sv': {name: 'Svenska'},
'vi': {name: 'Tiếng Việt'},
'tr': {name: 'Türkçe'},
'uk': {name: 'Українська'},
'zh-cn': {name: '简体中文'},
'zh-tw': {name: '繁體中文'}
};
---
常见问题
- 暂无