Crowdin API 翻译下载实战:从接口调用到 Vue3 可视化实现
1. 引言
各位开发老铁们,有没有被国际化项目里的翻译管理折磨得够呛?那种每天手动导出翻译文件然后复制粘贴到项目里的日子,咋一想起来就浑身难受,就像吃了个没熟透的柿子,涩得慌!
Crowdin 作为当下最火的翻译管理平台之一,它家的 API 简直就是咱开发者的救命稻草。今天俺就带大家一起整一个能通过 API 自动下载翻译的小工具,让咱们从繁琐的手动操作中解放出来,再也不用伸手要翻译了!
咱这个项目不仅有后端接口,还有个漂亮的 Vue3 前端界面,让产品同学点点鼠标就能搞定翻译下载,那叫一个痛快!
2. Crowdin API 基础
Crowdin API 是个啥?
说白了,Crowdin API 就是让你能用代码来操作 Crowdin 平台的一套接口。你能用它来管理项目、上传文件、下载翻译,比手动操作方便多了!
API 令牌咋整?
先登录你的 Crowdin 账号,找到个人设置 → API 和 Webhooks → 新建个人访问令牌,起个名字(比如翻译下载工具),选好权限,然后保存下来。记住啊,这个令牌就跟你家钥匙似的,可不敢乱丢!
// 这样初始化 Crowdin API 客户端
import { CrowdinApi } from '@crowdin/crowdin-api-client';
const credentials = {
token: '你的API令牌',
organization: '你的组织名' // 企业版才需要
};
const crowdinApi = new CrowdinApi(credentials);
核心 API 接口
Crowdin 的 API 多得很,但咱主要用这几个:
- 项目列表:
projectsGroupsApi.listProjects() - 文件列表:
sourceFilesApi.listFiles(projectId) - 文件翻译:
translationsApi.buildProjectFileTranslation() - 项目翻译:
translationsApi.buildProjectTranslation()
这几个接口咱组合起来用,就能实现从查看项目到下载翻译的全流程自动化!
3. 技术架构设计
咱这个项目架构很清晰,前后端分离,就像陕西的油泼面和辣子,分开放但一起吃才香!
graph TD
%% 前端部分
subgraph "前端 (Vue3)"
A[Dashboard 项目列表视图] ---|路由导航| B[TranslationView 翻译下载视图]
A & B ----> C[Pinia 状态管理]
C ----> D[Axios HTTP 客户端]
end
%% 前后端通信
D <--"HTTP 请求/响应"--> E[Express 服务器]
%% 后端部分
subgraph "后端 (Node.js)"
E --> F[API 路由]
F --> G[控制器层]
G --> H[服务层]
subgraph "服务层组件"
H --> I[Crowdin 服务\nAPI 客户端]
H --> J[翻译处理服务\n数据增强]
end
I & J --> K[工具层\n日志/配置/文件操作]
end
%% 外部服务
subgraph "外部服务和存储"
I <--"API 调用"--> L[Crowdin API\n翻译源]
K --> M[文件系统存储\n翻译输出]
end
%% 样式
classDef frontend fill:#d4f1f9,stroke:#87ceeb
classDef backend fill:#ffe6cc,stroke:#ffb366
classDef external fill:#e6ffcc,stroke:#99cc00
class A,B,C,D frontend
class E,F,G,H,I,J,K backend
class L,M external
后端服务 (Node.js + Express)
后端用 Node.js 和 Express 搭建,TypeScript 做类型检查,主要负责:
- 和 Crowdin API 打交道
- 处理翻译数据
- 提供 RESTful 接口给前端调用
前端界面 (Vue3 + Element Plus)
前端基于 Vue3 全家桶,用 Element Plus 做 UI 组件,功能有:
- 展示项目和文件列表
- 选择翻译语言
- 下载并预览翻译内容
- 保存翻译文件
为啥选这套技术栈?
咱为啥选这套技术栈?优势明显吧:
- Node.js 处理 API 请求快得很,跟闪电似的
- TypeScript 让代码更可靠,错误早发现
- Vue3 组合式 API 写起来舒坦,代码可维护性杠杠的
- Element Plus 组件多,界面好看,开发效率高
就跟吃羊肉泡馍一样,面先撕好,汤要熬足,羊肉要嫩,配菜要新鲜,缺一样都不行!
4. 后端实现关键点
API 客户端配置
首先咱得配置 Crowdin API 客户端,关键代码如下:
// 从环境变量加载配置
import dotenv from 'dotenv';
dotenv.config();
class CrowdinService {
private api: ReturnType<typeof CrowdinApi>;
constructor(credentials: CrowdinCredentials = config.crowdin) {
this.api = new CrowdinApi(credentials);
logger.info('Crowdin服务初始化完成');
}
// 后续方法...
}
环境变量配置样例:
# Crowdin API 配置
CROWDIN_API_TOKEN=your_api_token_here
CROWDIN_ORGANIZATION=your_organization_name
这就跟你去饭馆吃饭,先得告诉服务员你要吃啥菜一个道理,API 客户端得知道找哪个 Crowdin 账号要数据。
获取项目信息
async getProjects(): Promise<ProjectInfo[]> {
try {
logger.info('获取项目列表');
const response = await this.api.projectsGroupsApi.listProjects();
return response.data.map(project => ({
id: project.data.id,
name: project.data.name,
identifier: project.data.identifier,
description: project.data.description,
public: project.data.visibility === 'open',
sourceLanguageId: project.data.sourceLanguageId,
targetLanguageIds: project.data.targetLanguageIds || []
}));
} catch (error) {
logger.error('获取项目列表失败', { error });
throw error;
}
}
文件翻译下载核心实现
这个方法是整个后端的核心,就像做肉夹馍的关键是肉要炖得烂、馍要烙得酥一样:
async downloadFileTranslation(request: FileTranslationRequest): Promise<EnhancedTranslation> {
const { projectId, fileId, languageId } = request;
try {
// 构建文件翻译
const buildResponse = await this.api.translationsApi.buildProjectFileTranslation(
projectId,
fileId,
{
targetLanguageId: languageId
}
);
// 获取下载链接
const downloadUrl = buildResponse.data.url;
// 下载翻译内容
const response = await fetch(downloadUrl);
if (!response.ok) {
throw new Error(`下载失败: HTTP状态 ${response.status}`);
}
// 解析翻译数据
const translationData = await response.json();
// 处理翻译数据
const enhancedTranslation = processTranslationData(
translationData,
languageId,
projectId,
fileId
);
// 保存到本地(可选)
this.saveTranslationToFile(enhancedTranslation, languageId, fileId);
return enhancedTranslation;
} catch (error) {
logger.error(`下载文件翻译失败`, { error });
throw error;
}
}
错误处理与日志
程序必须要有好的错误处理,就像做菜必须要有备用食材一样,以防万一:
// 日志配置
const logger = winston.createLogger({
level: config.logger.level,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console({
format: winston.format.colorize()
}),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// API 控制器中的错误处理
export const downloadFileTranslation = async (req: Request, res: Response) => {
try {
// 业务逻辑...
} catch (error) {
logger.error('下载文件翻译失败', { error });
const response: ApiResponse<null> = {
success: false,
error: (error as Error).message
};
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json(response);
}
};
5. 前端 Vue3 实现
项目布局与组件设计
前端用 Vite 搭建 Vue3 项目,整体结构清晰,组件划分合理,就像一桌丰盛的饭菜,每个菜都有自己的位置:
client/
├── src/
│ ├── components/ # 组件
│ ├── views/ # 页面
│ ├── stores/ # 状态管理
│ ├── services/ # API 服务
│ └── types/ # 类型定义
Pinia 状态管理
使用 Pinia 管理状态,比 Vuex 更简洁,使用 TypeScript 提供完整类型支持:
export const useCrowdinStore = defineStore('crowdin', () => {
// 状态
const projects = ref<ProjectInfo[]>([]);
const selectedProject = ref<ProjectInfo | null>(null);
const files = ref<FileInfo[]>([]);
const languages = ref<LanguageInfo[]>([]);
const translation = ref<EnhancedTranslation | null>(null);
// 动作
async function fetchProjects() {
loading.value = true;
try {
const response = await api.getProjects();
if (response.success && response.data) {
projects.value = response.data;
}
} catch (err) {
error.value = (err as Error).message;
} finally {
loading.value = false;
}
}
async function downloadFileTranslation() {
// 下载实现...
}
// 返回状态和方法
return {
projects,
selectedProject,
// ...其他状态
fetchProjects,
downloadFileTranslation,
// ...其他方法
};
});
这样写状态管理,就像西安的泡馍馆,啥料都准备好了,客人来了直接点菜就行,方便得很!
翻译下载流程界面
翻译下载界面设计得简单明了,分为三个步骤:选择项目、选择语言和文件、下载翻译:
<template>
<el-form label-width="100px">
<el-form-item label="项目">
<el-select v-model="selectedProjectId" @change="handleProjectChange">
<el-option v-for="project in projects" :key="project.id"
:label="project.name" :value="project.id" />
</el-select>
</el-form-item>
<el-form-item label="语言">
<el-select v-model="selectedLanguageId" @change="handleLanguageChange">
<el-option v-for="language in targetLanguages" :key="language.id"
:label="`${language.name} (${language.id})`" :value="language.id" />
</el-select>
</el-form-item>
<el-form-item label="文件">
<el-select v-model="selectedFileId" clearable @change="handleFileChange">
<el-option v-for="file in files" :key="file.id"
:label="`${file.name} (${file.type})`" :value="file.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleDownload">下载翻译</el-button>
</el-form-item>
</el-form>
</template>
6. 实际应用示例
来看看我们的工具怎么用,操作简单得很,跟点外卖一样方便:
选择项目和文件
- 打开应用,首页看到项目列表
- 点击翻译按钮进入翻译下载页面
- 选择你要的项目、语言和文件(文件可选,不选就是下载整个项目)
这就像去面馆点餐,先选主食(项目),再选配料(语言),最后选调料(文件),一步步来,不会错!
翻译下载与预览
点击下载翻译按钮后:
- 如果选了具体文件,会直接显示翻译内容预览
- 如果是整个项目,会提供下载链接
预览界面可以看到原始数据和处理后的内容,还能直接保存为 JSON 文件,比 Excel 表格方便多了!
实用场景:自动化部署
这个工具最适合集成到 CI/CD 流程中。比如你可以:
# 在构建前自动获取最新翻译
curl -X POST http://localhost:23501/api/crowdin/translations/file \
-H "Content-Type: application/json" \
-d '{"projectId": 123, "fileId": 456, "languageId": "zh-CN"}'
再也不用手动更新翻译文件了,跟吃自助餐一样,想吃啥拿啥,想更新就更新,那叫一个痛快!
7. 项目部署与使用
环境配置
- 克隆代码库并安装依赖:
git clone https://github.com/yourusername/crowdin-http.git
cd crowdin-http
yarn install
- 配置环境变量,复制
.env.example为.env:
CROWDIN_API_TOKEN=你的API令牌
CROWDIN_ORGANIZATION=你的组织名(可选)
PORT=23501
启动服务
开发环境:
# 同时启动前后端
yarn dev
生产环境:
# 构建
yarn build
# 启动服务
yarn start
部署好后,前端访问 http://localhost:5173,后端 API 服务在 http://localhost:23501。咋样,跟吃现成的饺子一样简单吧?
通过这个工具,咱们彻底解放了翻译下载的繁琐过程,不用再伸手向翻译要文件,不用再手动复制粘贴,一切都自动化了!
就像老陕西人常说的:省心省力,何乐不为? 开发这点小工具,给团队节省的时间可不是一星半点,值得!