Crowdin API 翻译下载实战:从接口调用到 Vue3 可视化实现

461 阅读5分钟

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);

SCR-20250423-rccz.png

核心 API 接口

Crowdin 的 API 多得很,但咱主要用这几个:

  1. 项目列表projectsGroupsApi.listProjects()
  2. 文件列表sourceFilesApi.listFiles(projectId)
  3. 文件翻译translationsApi.buildProjectFileTranslation()
  4. 项目翻译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/          # 类型定义

SCR-20250423-rbfa.png

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. 实际应用示例

来看看我们的工具怎么用,操作简单得很,跟点外卖一样方便:

SCR-20250423-rbdj.png

选择项目和文件

  1. 打开应用,首页看到项目列表
  2. 点击翻译按钮进入翻译下载页面
  3. 选择你要的项目、语言和文件(文件可选,不选就是下载整个项目)

这就像去面馆点餐,先选主食(项目),再选配料(语言),最后选调料(文件),一步步来,不会错!

翻译下载与预览

点击下载翻译按钮后:

  • 如果选了具体文件,会直接显示翻译内容预览
  • 如果是整个项目,会提供下载链接

预览界面可以看到原始数据和处理后的内容,还能直接保存为 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. 项目部署与使用

环境配置

  1. 克隆代码库并安装依赖:
git clone https://github.com/yourusername/crowdin-http.git
cd crowdin-http
yarn install
  1. 配置环境变量,复制 .env.example.env
CROWDIN_API_TOKEN=你的API令牌
CROWDIN_ORGANIZATION=你的组织名(可选)
PORT=23501

启动服务

开发环境:

# 同时启动前后端
yarn dev

生产环境:

# 构建
yarn build

# 启动服务
yarn start

部署好后,前端访问 http://localhost:5173,后端 API 服务在 http://localhost:23501。咋样,跟吃现成的饺子一样简单吧?


通过这个工具,咱们彻底解放了翻译下载的繁琐过程,不用再伸手向翻译要文件,不用再手动复制粘贴,一切都自动化了!

就像老陕西人常说的:省心省力,何乐不为? 开发这点小工具,给团队节省的时间可不是一星半点,值得!