Unreal Engine 数据源数据同步流程
数据源创建
一个Repository对应一个数据源,目前已有内容的Repository,不支持更换数据源。 对于 Unreal Engine(下文简称 UE) 的数据源来说,需要配合UE Plugins使用,以下简称UE Connector。 当定义一个UE数据源时,用户(通常是游戏制作人或本地化负责人的角色)在Flow 2 系统中会创建一个 Access Token,一个 Access Token 对应一个 Repository,然后把Access Token分发给使用UE的用户(一般是开发者),UE用户需要 UE Connector 中需要配置Flow 2的API URL和Access Token,然后自定义一个用户自主区分用的Friendly Name,UE Connector 可以在多个定义中切换。
数据推送
数据推送通过HTTP API方式进行,接口支持单条或批量推送。 推送参数主要有以下:
- Access Token
- List[FText]
- Namespace
- Key
- Value
- Identifier (Optional)
允许分多次推送,入库顺序会按照推送顺序,如果有Namespace,Key,Value都一致的话,会忽略这条数据,如果仅Value有变化会更新,其它情况会新增,推送时,如果没有传递Identifier,返回时会返回一个新的Identifier,Identifier将是一个唯一的哈希值,并且会附带一个人类可读的友好名称,格式为"三个英文单词"(例如:"elegant-purple-whale")。后续的推送如果传递这个Identifier,则会在这个Identifier的基础上进行数据追加,不返回新的Identifier。
统计返回
推送数据时,会返回统计信息,包括新增、更新、忽略的文本数量。
标记完成
在推送完成时,使用Identifier作为参数,调用标记完成接口,表示本次推送完成,标记完成后如果推送时再次使用这个Identifier,会返回错误。
状态查询
推送完成后,使用Identifier和目标语作为参数,调用状态查询接口,可以查询到本次推送的数据集的本地化进度。
数据拉取
使用Identifier作为参数,目标语可选,调用数据拉取接口,可以返回指定目标语或全部目标语本地化后的数据。
标识符列表
使用Access Token作为参数,调用标识符列表接口,可以返回所有Identifier列表和是否已经标记为已完成,无分页。
按命名空间获取当前翻译
使用Access Token作为参数,调用按命名空间获取当前翻译接口,可以返回指定的命名空间(多个)的最新的本地化数据,无分页。
流程图
接口设计
API前缀
所有API调用均使用统一前缀。当前系统的API前缀为 /api-key-auth。在以下API文档中,将使用 /{API_PREFIX} 作为变量来表示此前缀。
1. 数据推送接口
接口路径:/{API_PREFIX}/ue/push
请求方式:POST
Content-Type:application/json
请求头参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-Apikey | String | 是 | 访问令牌 |
请求体参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| identifier | String | 否 | 标识符,用于追加数据 |
| texts | Array | 否 | 待本地化的文本列表 |
texts数组元素结构:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| namespace | String | 否 | 文本命名空间 |
| key | String | 否 | 文本键名 |
| value | String | 否 | 文本内容 |
请求示例:
// 请求头
X-Apikey: your-access-token
// 请求体
{
"identifier": "optional-previous-identifier",
"texts": [
{
"namespace": "Game",
"key": "DIALOG_WELCOME",
"value": "Welcome to our game!"
},
{
"namespace": "UI",
"key": "BUTTON_START",
"value": "Start Game"
}
]
}curl 命令示例:
curl -X POST "https://api.example.com/{API_PREFIX}/ue/push" \
-H "Content-Type: application/json" \
-H "X-Apikey: your-access-token" \
-d '{
"identifier": "optional-previous-identifier",
"texts": [
{
"namespace": "Game",
"key": "DIALOG_WELCOME",
"value": "Welcome to our game!"
},
{
"namespace": "UI",
"key": "BUTTON_START",
"value": "Start Game"
}
]
}'响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Object | 返回数据 |
data结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| identifier | String | 本次推送的唯一标识符 |
| friendlyName | String | 人类可读的名称(例如:"elegant-purple-whale") |
| statistics | Object | 推送统计信息 |
statistics结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| addedCount | Number | 新增文本数量 |
| updatedCount | Number | 更新文本数量 |
| ignoredCount | Number | 忽略文本数量 |
响应示例:
{
"code": 200,
"msg": "success",
"data": {
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"friendlyName": "elegant-purple-whale",
"statistics": {
"addedCount": 1,
"updatedCount": 1,
"ignoredCount": 0
}
}
}2. 标记完成接口
接口路径:/{API_PREFIX}/ue/complete
请求方式:POST
Content-Type:application/json
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| identifier | String | 否 | 标识符 |
| importType | String | 否 | 导入类型,可选值:"sync"、"update", sync 会删除已有数据全部重新导入,update 在之前的基础上追加数据 |
请求示例:
{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"importType": "sync"
}curl 命令示例:
curl -X POST "https://api.example.com/{API_PREFIX}/ue/complete" \
-H "Content-Type: application/json" \
-d '{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"importType": "sync"
}'响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Object | 返回数据 |
data结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| identifier | String | 标识符 |
| completedStatus | String | 完成状态:已完成completed未完成incomplete |
| completedTime | String | 完成时间 |
响应示例:
{
"code": 200,
"msg": "success",
"data": {
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"completedStatus": "completed",
"completedTime": "2023-11-20 14:30:45"
}
}3. 状态查询接口
接口路径:/{API_PREFIX}/ue/status
请求方式:POST
Content-Type:application/json
请求头参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-Apikey | String | 是 | 访问令牌 |
请求体参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| identifier | String | 否 | 标识符 |
| targetLanguage | String | 否 | 目标语言代码,如"zh-CN" |
请求示例:
// 请求头
X-Apikey: your-access-token
// 请求体
{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"targetLanguage": "zh-CN"
}curl 命令示例:
curl -X POST "https://api.example.com/{API_PREFIX}/ue/status" \
-H "Content-Type: application/json" \
-H "X-Apikey: your-access-token" \
-d '{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"targetLanguage": "zh-CN"
}'响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Object | 返回数据 |
data结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| identifier | String | 标识符 |
| languages | Array | 各语言进度信息 |
languages数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | String | 语言代码 |
| progress | Number | 翻译进度百分比(0-100) |
响应示例:
{
"code": 200,
"msg": "success",
"data": {
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"languages": [
{
"code": "zh-CN",
"progress": 80
},
{
"code": "ja-JP",
"progress": 20
}
]
}
}4. 数据拉取接口
接口路径:/{API_PREFIX}/ue/pull
请求方式:POST
Content-Type:application/json
请求头参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-Apikey | String | 是 | 访问令牌 |
请求体参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| identifier | String | 否 | 标识符 |
| targetLanguage | String | 否 | 目标语言代码,不填则返回所有语言 |
请求示例:
// 请求头
X-Apikey: your-access-token
// 请求体
{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"targetLanguage": "zh-CN"
}curl 命令示例:
curl -X POST "https://api.example.com/{API_PREFIX}/ue/pull" \
-H "Content-Type: application/json" \
-H "X-Apikey: your-access-token" \
-d '{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"targetLanguage": "zh-CN"
}'响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Object | 返回数据 |
data结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| identifier | String | 标识符 |
| data | Array | 翻译数据列表 |
data数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| namespace | String | 文本命名空间 |
| key | String | 文本键名 |
| sourceValue | String | 源文本内容 |
| translations | Array | 翻译列表 |
translations数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| language | String | 语言代码 |
| value | String | 翻译文本内容 |
响应示例:
{
"code": 200,
"msg": "success",
"data": {
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"data": [
{
"namespace": "Game",
"key": "DIALOG_WELCOME",
"sourceValue": "Welcome to our game!",
"translations": [
{
"language": "zh-CN",
"value": "欢迎来到我们的游戏!"
}
]
},
{
"namespace": "UI",
"key": "BUTTON_START",
"sourceValue": "Start Game",
"translations": [
{
"language": "zh-CN",
"value": "开始游戏"
}
]
}
]
}
}5. 标识符列表接口
接口路径:/{API_PREFIX}/ue/identifiers
请求方式:POST
Content-Type:application/x-www-form-urlencoded
请求头参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-Apikey | String | 是 | API密钥 |
请求示例:
// 请求头
X-Apikey: your-access-tokencurl 命令示例:
curl -X POST "https://api.example.com/{API_PREFIX}/ue/identifiers" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-Apikey: your-access-token"响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Object | 返回数据 |
data结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| success | Boolean | 请求是否成功 |
| total | Number | 标识符总数 |
| identifiers | Array | 标识符列表 |
identifiers数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| identifier | String | 唯一标识符 |
| friendlyName | String | 人类可读的名称(例如:"elegant-purple-whale") |
| isMarkAsCompleted | Boolean | 是否已经标记为已完成 |
响应示例:
{
"code": 200,
"msg": "success",
"data": {
"success": true,
"total": 42,
"identifiers": [
{
"identifier": "a1b2c3d4e5f6g7h8i9j0",
"friendlyName": "elegant-purple-whale",
"isMarkAsCompleted": true
},
{
"identifier": "k1l2m3n4o5p6q7r8s9t0",
"friendlyName": "brave-orange-tiger",
"isMarkAsCompleted": false
}
]
}
}6. 按命名空间获取当前翻译接口
接口路径:/{API_PREFIX}/ue/current-translations
请求方式:POST
Content-Type:application/json
请求头参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-Apikey | String | 是 | 访问令牌 |
请求体参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| namespaces | Array | 否 | 要检索翻译的命名空间列表 |
| targetLanguages | Array | 否 | 目标语言代码列表,不填则返回所有可用语言 |
请求示例:
// 请求头
X-Apikey: your-access-token
// 请求体
{
"namespaces": ["Game", "UI"],
"targetLanguages": ["zh-CN", "ja"]
}curl 命令示例:
curl -X POST "https://api.example.com/{API_PREFIX}/ue/current-translations" \
-H "Content-Type: application/json" \
-H "X-Apikey: your-access-token" \
-d '{
"namespaces": ["Game", "UI"],
"targetLanguages": ["zh-CN", "ja"]
}'响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Object | 返回数据 |
data结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| data | Array | 按命名空间分组的当前本地化文本数据 |
data数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| namespace | String | 文本命名空间 |
| strings | Array | 此命名空间中的字符串列表 |
strings数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| key | String | 文本键名 |
| sourceValue | String | 源文本内容 |
| translations | Array | 翻译列表 |
| lastUpdated | String | 最后更新时间 |
translations数组元素结构:
| 参数名 | 类型 | 说明 |
|---|---|---|
| language | String | 语言代码 |
| value | String | 翻译值 |
响应示例:
{
"code": 200,
"msg": "success",
"data": {
"data": [
{
"namespace": "Game",
"strings": [
{
"key": "DIALOG_WELCOME",
"sourceValue": "Welcome to our game!",
"lastUpdated": "2023-11-20 14:30:45",
"translations": [
{
"language": "zh-CN",
"value": "欢迎来到我们的游戏!"
},
{
"language": "ja",
"value": "ゲームへようこそ!"
}
]
},
{
"key": "DIALOG_GOODBYE",
"sourceValue": "Thanks for playing!",
"lastUpdated": "2023-11-20 14:30:45",
"translations": [
{
"language": "zh-CN",
"value": "感谢您的游玩!"
},
{
"language": "ja",
"value": "プレイしていただきありがとうございます!"
}
]
}
]
},
{
"namespace": "UI",
"strings": [
{
"key": "BUTTON_START",
"sourceValue": "Start Game",
"lastUpdated": "2023-11-20 14:30:45",
"translations": [
{
"language": "zh-CN",
"value": "开始游戏"
},
{
"language": "ja",
"value": "ゲームを始める"
}
]
}
]
}
]
}
}7. 获取数据源的目标语言列表接口
接口路径:/{API_PREFIX}/ue/target-languages
请求方式:GET
Content-Type:application/json
请求头参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| X-Apikey | String | 是 | API密钥 |
请求示例:
// 请求头
X-Apikey: your-access-tokencurl 命令示例:
curl -X GET "https://api.example.com/{API_PREFIX}/ue/target-languages" \
-H "X-Apikey: your-access-token"响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| code | Number | 状态码 |
| msg | String | 状态消息 |
| data | Array | 可用的目标语言列表 |
响应示例:
{
"code": 200,
"msg": "success",
"data": [
"zh-CN",
"en-US"
]
}错误响应
当请求失败时,所有接口返回统一的错误格式:
错误响应参数:
| 参数名 | 类型 | 说明 |
|---|---|---|
| success | Boolean | 固定为false |
| errorCode | String | 错误代码 |
| errorMessage | String | 错误描述 |
错误响应示例:
{
"success": false,
"errorCode": "invalid_token",
"errorMessage": "The provided access token is invalid or expired"
}常见错误码:
| 错误码 | 说明 |
|---|---|
| invalid_token | 访问令牌无效或已过期 |
| invalid_identifier | 标识符无效 |
| identifier_completed | 标识符已标记为完成,不能再添加数据 |
| empty_data | 没有提供文本数据 |
| server_error | 服务器内部错误 |