- CLAUDE.md: 项目定位与治理规则 - API reference: 补充 multisite 参数文档 - Multisite blog_id 参数支持 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
290 lines
7.5 KiB
Markdown
290 lines
7.5 KiB
Markdown
# GlotPress REST API 文档
|
||
|
||
## 概述
|
||
|
||
`gp-rest-api` 是部署在 `wpfanyi.com` 的 WordPress mu-plugin,为 GlotPress 翻译平台提供 REST API 端点,支持通过 Application Password 认证进行程序化翻译管理。
|
||
|
||
- 部署位置:`/www/wwwroot/wpfanyi.com/wp-content/mu-plugins/gp-rest-api.php`
|
||
- 服务器:feicode-prod (45.117.8.70)
|
||
- 源码仓库:`~/Projects/gp-rest-api/`(待推送 feicode)
|
||
- 版本:1.0.0
|
||
- GlotPress 版本:4.0.3
|
||
|
||
## 认证
|
||
|
||
使用 WordPress Application Password(Basic Auth)。
|
||
|
||
### 创建 Application Password
|
||
|
||
1. 登录 wpfanyi.com 后台
|
||
2. 用户 → 个人资料 → Application Passwords
|
||
3. 输入名称(如 `ai-translate-pipeline`),点击"添加新的应用程序密码"
|
||
4. 记录生成的密码(只显示一次)
|
||
|
||
### 请求认证
|
||
|
||
```bash
|
||
curl -u "用户名:应用密码" "https://wpfanyi.com/wp-json/gp/v1/..."
|
||
```
|
||
|
||
或使用 Authorization header:
|
||
```
|
||
Authorization: Basic base64(用户名:应用密码)
|
||
```
|
||
|
||
## 多站点说明
|
||
|
||
wpfanyi.com 是 WordPress 多站点架构,67 个子站各自运行独立的 GlotPress 实例。所有端点需要传 `blog_id` 参数指定目标子站。
|
||
|
||
### 子站 blog_id 对照表(Top 15)
|
||
|
||
| blog_id | 路径 | 产品 |
|
||
|---------|------|------|
|
||
| 2 | /woocommerce/ | WooCommerce |
|
||
| 3 | /buddypress/ | BuddyPress |
|
||
| 4 | /bbpress/ | bbPress |
|
||
| 5 | /mainwp/ | MainWP |
|
||
| 8 | /elementor/ | Elementor |
|
||
| 9 | /learndash/ | LearnDash |
|
||
| 10 | /gravityforms/ | GravityForms |
|
||
|
||
完整列表可通过 WP 网络管理后台查看。
|
||
|
||
## 端点
|
||
|
||
### 1. GET /wp-json/gp/v1/projects/{path}
|
||
|
||
获取项目信息、翻译集列表和翻译进度统计。
|
||
|
||
**参数**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| path | string | 是 | URL 路径参数,GP 项目路径 |
|
||
| blog_id | integer | 否 | 子站 ID,默认 0(当前站) |
|
||
|
||
**示例**
|
||
|
||
```bash
|
||
curl -u "user:pass" \
|
||
"https://wpfanyi.com/wp-json/gp/v1/projects/woocommerce?blog_id=2"
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"name": "WooCommerce",
|
||
"slug": "woocommerce",
|
||
"path": "woocommerce",
|
||
"description": "...",
|
||
"parent_project_id": 0,
|
||
"translation_sets": [
|
||
{
|
||
"id": 2,
|
||
"locale": "zh-cn",
|
||
"slug": "default",
|
||
"name": "Chinese (China)",
|
||
"stats": {
|
||
"all": 8378,
|
||
"current": 8378,
|
||
"waiting": 0,
|
||
"fuzzy": 0,
|
||
"untranslated": 0
|
||
},
|
||
"percent": 100
|
||
}
|
||
],
|
||
"sub_projects": [
|
||
{
|
||
"id": 2,
|
||
"name": "WooCommerce - Stable",
|
||
"slug": "stable",
|
||
"path": "woocommerce/stable"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 2. GET /wp-json/gp/v1/originals
|
||
|
||
查询项目原文及其翻译状态,支持按状态过滤和分页。
|
||
|
||
**参数**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| project_path | string | 是 | - | GP 项目路径 |
|
||
| locale | string | 是 | - | 语言代码,如 `zh-cn` |
|
||
| blog_id | integer | 否 | 0 | 子站 ID |
|
||
| slug | string | 否 | `default` | 翻译集 slug |
|
||
| status | string | 否 | `untranslated` | 过滤状态:`untranslated`/`current`/`fuzzy`/`waiting`/`all` |
|
||
| per_page | integer | 否 | 50 | 每页数量(1-200) |
|
||
| page | integer | 否 | 1 | 页码 |
|
||
|
||
**示例**
|
||
|
||
```bash
|
||
# 获取 WooCommerce 未翻译的原文
|
||
curl -u "user:pass" \
|
||
"https://wpfanyi.com/wp-json/gp/v1/originals?project_path=woocommerce&locale=zh-cn&blog_id=2&status=untranslated&per_page=10"
|
||
|
||
# 获取已翻译的原文
|
||
curl -u "user:pass" \
|
||
"https://wpfanyi.com/wp-json/gp/v1/originals?project_path=woocommerce&locale=zh-cn&blog_id=2&status=current&per_page=5"
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"project": "woocommerce",
|
||
"locale": "zh-cn",
|
||
"slug": "default",
|
||
"total": 8378,
|
||
"page": 1,
|
||
"per_page": 5,
|
||
"items": [
|
||
{
|
||
"original_id": 1,
|
||
"singular": "Nuevo León",
|
||
"plural": null,
|
||
"context": null,
|
||
"references": ["i18n/states.php:943"],
|
||
"translation_id": 1,
|
||
"translation_0": "新莱昂",
|
||
"gp_status": "current"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 3. POST /wp-json/gp/v1/translations
|
||
|
||
批量提交翻译。单次最多 100 条。
|
||
|
||
**权限要求**:用户必须拥有目标翻译集的 GP `edit` 权限。
|
||
|
||
**状态逻辑**:
|
||
- 用户有 `approve` 权限 → 翻译直接设为 `current`(已批准)
|
||
- 用户无 `approve` 权限 → 翻译设为 `waiting`(待审核)
|
||
|
||
**请求体(JSON)**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| project_path | string | 是 | GP 项目路径 |
|
||
| locale | string | 是 | 语言代码 |
|
||
| blog_id | integer | 否 | 子站 ID |
|
||
| slug | string | 否 | 翻译集 slug,默认 `default` |
|
||
| translations | array | 是 | 翻译数组(最多 100 条) |
|
||
|
||
**translations 数组项**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| original_id | integer | 是 | 原文 ID |
|
||
| translation_0 | string | 是 | 单数翻译 |
|
||
| translation_1~5 | string | 否 | 复数形式翻译 |
|
||
|
||
**示例**
|
||
|
||
```bash
|
||
curl -X POST -u "user:pass" \
|
||
"https://wpfanyi.com/wp-json/gp/v1/translations" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"blog_id": 4,
|
||
"project_path": "bbpress",
|
||
"locale": "zh-cn",
|
||
"translations": [
|
||
{"original_id": 100, "translation_0": "论坛"},
|
||
{"original_id": 101, "translation_0": "主题"}
|
||
]
|
||
}'
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"summary": {
|
||
"submitted": 1,
|
||
"skipped": 1,
|
||
"errors": 0
|
||
},
|
||
"results": [
|
||
{
|
||
"original_id": 100,
|
||
"status": "created",
|
||
"translation_id": 5678,
|
||
"gp_status": "current",
|
||
"warnings": false
|
||
},
|
||
{
|
||
"original_id": 101,
|
||
"status": "skipped",
|
||
"translation_id": 1234,
|
||
"message": "Identical current translation exists."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**结果状态说明**
|
||
|
||
| status | 含义 |
|
||
|--------|------|
|
||
| `created` | 翻译已创建 |
|
||
| `skipped` | 已存在相同的 current 翻译,跳过 |
|
||
| `error` | 创建失败(附 message 说明原因) |
|
||
|
||
## 内部机制
|
||
|
||
### 翻译提交流程
|
||
|
||
1. 通过 `GP::$project->by_path()` 定位项目
|
||
2. 通过 `GP::$translation_set->by_project_id_slug_and_locale()` 定位翻译集
|
||
3. 检查 GP 权限(`edit` on `translation-set`)
|
||
4. 对每条翻译:
|
||
- 验证 `original_id` 存在且属于目标项目
|
||
- 重复检测:比较所有复数字段(translation_0~5),相同则跳过
|
||
- 运行 `GP::$translation_warnings->check()` 质量检查
|
||
- 调用 `GP::$translation->create()` 创建翻译
|
||
- 调用 `$translation->set_status()` 设置状态(处理 old/current 状态机)
|
||
|
||
### 安全特性
|
||
|
||
- Application Password 认证(WP 5.6+ 内置)
|
||
- GP 权限层自动继承(基于 `wp_get_current_user()`)
|
||
- 隐藏优先级原文(priority -2)对非项目管理员不可见
|
||
- 翻译质量警告自动检查
|
||
- 单次请求上限 100 条
|
||
|
||
### 多站点处理
|
||
|
||
使用 `switch_to_blog()` / `restore_current_blog()` 切换子站上下文,并重新初始化 GP 表名前缀(`wp_N_gp_projects` 等)。
|
||
|
||
## 环境配置
|
||
|
||
### nginx
|
||
|
||
`/www/server/nginx/conf/fastcgi.conf` 需包含:
|
||
```
|
||
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
|
||
```
|
||
|
||
### Application Password over HTTP
|
||
|
||
插件只在 `local` 和 `development` 环境覆盖 `wp_is_application_passwords_available`,用于反向代理终止 SSL 但 PHP 仍看到 HTTP 的开发环境。生产环境继续使用 WordPress 默认的 HTTPS 检查。
|
||
|
||
## 错误码
|
||
|
||
| code | HTTP | 说明 |
|
||
|------|------|------|
|
||
| `rest_forbidden` | 401 | 未认证或无权限 |
|
||
| `gp_project_not_found` | 404 | 项目路径不存在 |
|
||
| `gp_set_not_found` | 404 | 翻译集不存在 |
|
||
| `gp_too_many` | 400 | 超过 100 条限制 |
|
||
| `rest_missing_callback_param` | 400 | 缺少必填参数 |
|