博客

LunariaJS 高级配置:打造企业级本地化管理方案

探索 LunariaJS 的高级配置选项和自定义本地化策略,学习多仓库支持、自定义状态规则、复杂文件结构处理等企业级场景的解决方案。

LibDoc Team 2026年3月6日 LunariaJS 专栏 102 分钟阅读
#LunariaJS #高级配置 #企业级 #自定义策略 #Monorepo

LunariaJS 高级配置:打造企业级本地化管理方案

在上一篇文章中,我们深入剖析了 @lunariajs/core 的源码架构。今天,让我们将这些知识应用到实践中,探索 LunariaJS 的高级配置选项,解决企业级场景中的复杂本地化管理需求。

💡 官方文档LunariaJS 中文文档 - 高级配置

企业级场景挑战

典型企业级需求

企业级项目通常面临以下本地化挑战:

挑战描述影响
多仓库管理代码分布在多个 Git 仓库翻译状态难以统一追踪
复杂文件结构不同模块有不同的目录结构配置复杂,容易出错
自定义工作流需要适配现有的开发流程标准配置无法满足需求
权限控制不同团队有不同的访问权限需要细粒度的权限管理
大规模文件数百甚至数千个翻译文件性能成为瓶颈
多源语言不同模块可能有不同的源语言状态计算逻辑复杂

本章学习目标

通过本章,你将学会:

  • ✅ 配置复杂的多仓库项目
  • ✅ 设计自定义状态判断规则
  • ✅ 优化大型项目的性能
  • ✅ 集成企业工具和系统
  • ✅ 实现高级安全和权限控制

高级配置选项

自定义状态规则

LunariaJS 允许你自定义翻译状态的判断逻辑:

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 自定义状态判断
  statusRules: {
    // 自定义"过时"的判定条件
    outdated: {
      // 天数阈值:源文件比翻译新多少天才算过时
      dayThreshold: 7,  // 默认 0,这里设为 7 天

      // 或者使用自定义函数
      customCheck: async (source, translation) => {
        // 检查源文件是否有重大变更
        const changes = await getSignificantChanges(source.path);
        return changes.length > 0;
      },
    },

    // 自定义"缺失"的判定条件
    missing: {
      // 忽略某些文件模式
      ignorePatterns: ['**/internal/**', '**/draft/**'],

      // 或者使用自定义函数
      customCheck: (sourcePath, translationPath) => {
        // 检查是否需要翻译(某些文件可能不需要翻译)
        const content = fs.readFileSync(sourcePath, 'utf-8');
        return !content.includes('<!-- no-translate -->');
      },
    },
  },

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',
    },
  ],
});

复杂路径映射

处理复杂的项目结构:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  files: [
    // 文档文件
    {
      sourcePath: 'docs/guides/{slug}.md',
      localizationPath: 'docs/{lang}/guides/{slug}.md',
      include: ['**/*.md'],
      exclude: ['**/draft/**'],
    },

    // API 参考文档
    {
      sourcePath: 'docs/api/{category}/{slug}.md',
      localizationPath: 'docs/{lang}/api/{category}/{slug}.md',
    },

    // UI 翻译文件(JSON)
    {
      sourcePath: 'src/locales/en/{module}.json',
      localizationPath: 'src/locales/{lang}/{module}.json',
    },

    // 配置文件
    {
      sourcePath: 'config/en/{slug}.yaml',
      localizationPath: 'config/{lang}/{slug}.yaml',
    },
  ],
});

多源文件配置

某些项目可能有多个源语言目录:

// lunaria.config.ts
export default defineConfig({
  // 主源语言
  sourceLanguage: 'en',

  // 次要源语言(用于特定模块)
  secondarySourceLanguages: {
    'packages/japanese-sdk': 'ja',  // 这个包的源语言是日语
  },

  languages: ['en', 'ja', 'zh-cn', 'ko'],

  files: [
    // 主项目 - 英语为源语言
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'docs/{lang}/{slug}.md',
      sourceLanguage: 'en',  // 显式指定
    },

    // Japanese SDK - 日语为源语言
    {
      sourcePath: 'packages/japanese-sdk/docs/{slug}.md',
      localizationPath: 'packages/japanese-sdk/docs/{lang}/{slug}.md',
      sourceLanguage: 'ja',  // 覆盖全局配置
    },
  ],
});

条件化处理

根据条件动态调整配置:

// lunaria.config.ts
const isProduction = process.env.NODE_ENV === 'production';
const isCI = process.env.CI === 'true';

export default defineConfig({
  sourceLanguage: 'en',

  // 根据环境调整语言列表
  languages: isProduction
    ? ['en', 'zh-cn', 'ja', 'ko', 'es', 'fr', 'de']
    : ['en', 'zh-cn'],  // 开发环境只追踪两种语言

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',

      // CI 环境排除草稿文件
      exclude: isCI ? ['**/draft/**', '**/wip/**'] : [],
    },
  ],

  dashboard: {
    outputDir: isProduction ? 'dist/i18n-status' : 'public/i18n-status',
    title: isProduction ? 'Production i18n Status' : 'Development i18n Status',
  },
});

多仓库本地化管理

Monorepo 场景配置

对于 Monorepo 项目(如使用 pnpm workspaces 或 Turborepo):

my-monorepo/
├── packages/
│   ├── core/
│   │   ├── docs/
│   │   │   └── en/
│   │   └── i18n/
│   │       ├── zh-cn/
│   │       └── ja/
│   ├── cli/
│   │   ├── docs/
│   │   │   └── en/
│   │   └── i18n/
│   │       ├── zh-cn/
│   │       └── ja/
│   └── ui/
│       ├── docs/
│       │   └── en/
│       └── i18n/
│           ├── zh-cn/
│           └── ja/
├── lunaria.config.ts
└── package.json

配置示例

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';
import { glob } from 'glob';

// 自动发现所有包
const packages = await glob('packages/*');

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 为每个包生成独立的文件配置
  files: packages.flatMap(pkg => [
    {
      sourcePath: `${pkg}/docs/{slug}.md`,
      localizationPath: `${pkg}/i18n/{lang}/{slug}.md`,
      // 使用包名作为分组
      group: pkg.replace('packages/', ''),
    },
  ]),

  dashboard: {
    outputDir: 'docs/i18n-status',
    title: 'My Monorepo - Translation Status',

    // 按包分组显示
    groupBy: 'group',
  },
});

多仓库统一仪表板

对于多个独立的 Git 仓库,可以创建一个”聚合”项目:

i18n-aggregator/
├── lunaria.config.ts
├── package.json
└── repos/
    ├── main-docs/        # 主文档仓库
    ├── api-docs/         # API 文档仓库
    └── blog-content/     # 博客内容仓库

聚合配置

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 聚合多个仓库
  repositories: [
    {
      name: 'main-docs',
      path: './repos/main-docs',
      files: [
        {
          sourcePath: 'docs/{slug}.md',
          localizationPath: 'i18n/{lang}/{slug}.md',
        },
      ],
    },
    {
      name: 'api-docs',
      path: './repos/api-docs',
      files: [
        {
          sourcePath: 'src/content/docs/{slug}.md',
          localizationPath: 'src/content/docs/{lang}/{slug}.md',
        },
      ],
    },
    {
      name: 'blog-content',
      path: './repos/blog-content',
      files: [
        {
          sourcePath: 'posts/{slug}.md',
          localizationPath: 'posts/{lang}/{slug}.md',
        },
      ],
    },
  ],

  dashboard: {
    outputDir: 'public/i18n-status',
    title: 'All Projects - Translation Status',
  },
});

跨仓库依赖处理

处理跨仓库的翻译依赖:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 定义翻译依赖
  dependencies: {
    // api-docs 的翻译依赖于 main-docs 的翻译
    'api-docs': ['main-docs'],

    // blog 的翻译依赖于 main-docs 和 api-docs
    'blog-content': ['main-docs', 'api-docs'],
  },

  // 依赖检查
  checkDependencies: true,

  // 依赖不满足时的处理
  onDependencyMissing: 'warn', // 'warn' | 'error' | 'ignore'

  files: [
    // ...
  ],
});

自定义本地化策略

策略接口设计

定义自定义本地化策略的接口:

// src/strategies/types.ts
import { TranslationStatus, FileInfo } from '@lunariajs/core';

/**
 * 本地化策略接口
 */
export interface LocalizationStrategy {
  /** 策略名称 */
  name: string;

  /** 策略描述 */
  description: string;

  /**
   * 计算翻译状态
   */
  calculateStatus(
    source: FileInfo,
    translation: FileInfo | null,
    context: StrategyContext
  ): Promise<TranslationStatus>;

  /**
   * 是否需要翻译
   */
  needsTranslation?(
    source: FileInfo,
    context: StrategyContext
  ): Promise<boolean>;

  /**
   * 获取翻译优先级
   */
  getPriority?(
    source: FileInfo,
    translation: FileInfo | null,
    context: StrategyContext
  ): Promise<'high' | 'medium' | 'low'>;
}

/**
 * 策略上下文
 */
export interface StrategyContext {
  /** 语言代码 */
  language: string;

  /** 项目配置 */
  config: LunariaConfig;

  /** Git 信息 */
  git: {
    lastCommit: string;
    author: string;
    date: Date;
  };

  /** 自定义数据 */
  data?: Record<string, unknown>;
}

实现自定义策略

创建基于内容复杂度的策略:

// src/strategies/content-complexity.ts
import { LocalizationStrategy, StrategyContext, FileInfo } from './types';

/**
 * 基于内容复杂度的策略
 * 根据源文件的复杂度动态调整"过时"判定标准
 */
export const ContentComplexityStrategy: LocalizationStrategy = {
  name: 'content-complexity',
  description: '根据内容复杂度调整翻译状态判定标准',

  async calculateStatus(
    source: FileInfo,
    translation: FileInfo | null,
    context: StrategyContext
  ) {
    // 如果翻译不存在,返回缺失
    if (!translation) {
      return { status: 'missing', reason: 'Translation file not found' };
    }

    // 计算内容复杂度
    const complexity = calculateComplexity(source.content);

    // 根据复杂度确定天数阈值
    const dayThreshold = getDayThreshold(complexity);

    // 检查时间差
    const sourceTime = source.lastModified.getTime();
    const translationTime = translation.lastModified.getTime();
    const daysDiff = (sourceTime - translationTime) / (1000 * 60 * 60 * 24);

    if (daysDiff > dayThreshold) {
      return {
        status: 'outdated',
        reason: `Source is ${daysDiff.toFixed(0)} days newer (threshold: ${dayThreshold})`,
        complexity,
      };
    }

    return {
      status: 'done',
      complexity,
    };
  },

  async getPriority(source: FileInfo, translation: FileInfo | null) {
    const complexity = calculateComplexity(source.content);

    if (complexity > 0.8) return 'high';
    if (complexity > 0.5) return 'medium';
    return 'low';
  },
};

/**
 * 计算内容复杂度(0-1)
 */
function calculateComplexity(content: string): number {
  const factors = {
    // 代码块数量
    codeBlocks: (content.match(/```/g) || []).length / 10,

    // 链接数量
    links: (content.match(/\[.*?\]\(.*?\)/g) || []).length / 20,

    // 特殊语法
    specialSyntax: (content.match(/\{.*?\}/g) || []).length / 30,

    // 文件长度
    length: content.length / 10000,
  };

  // 加权平均
  return (
    factors.codeBlocks * 0.3 +
    factors.links * 0.2 +
    factors.specialSyntax * 0.3 +
    factors.length * 0.2
  );
}

/**
 * 根据复杂度获取天数阈值
 */
function getDayThreshold(complexity: number): number {
  // 复杂度越高,阈值越大(给翻译者更多时间)
  if (complexity > 0.8) return 30;
  if (complexity > 0.5) return 14;
  return 7;
}

注册自定义策略

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';
import { ContentComplexityStrategy } from './src/strategies/content-complexity';

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 注册自定义策略
  strategies: {
    // 默认策略
    default: 'git-timestamp',

    // 按文件模式应用不同策略
    rules: [
      {
        pattern: 'docs/guides/**/*.md',
        strategy: 'content-complexity',
      },
      {
        pattern: 'docs/api/**/*.md',
        strategy: 'strict',  // API 文档使用严格模式
      },
      {
        pattern: '**/changelog.md',
        strategy: 'lenient',  // 变更日志使用宽松模式
      },
    ],

    // 注册自定义策略
    custom: [ContentComplexityStrategy],
  },

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',
    },
  ],
});

大型项目优化

性能瓶颈分析

大型项目的常见性能瓶颈:

瓶颈原因解决方案
Git 操作过多每个文件都查询 Git 历史批量查询 Git 信息
文件 I/O 过多逐个读取文件并行读取
配置解析慢复杂的 glob 模式缓存 glob 结果
仪表板生成慢大量模板渲染增量渲染

增量构建策略

只处理变更的文件:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 启用增量构建
  incremental: {
    enabled: true,

    // 缓存目录
    cacheDir: '.lunaria/cache',

    // 缓存有效期(秒)
    cacheTTL: 3600,

    // 强制全量构建的条件
    forceRebuild: {
      // 配置文件变更时强制全量
      configChanged: true,
      // 添加新语言时强制全量
      languageAdded: true,
    },
  },

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',
    },
  ],
});

并行处理

启用并行处理加速构建:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 并行处理配置
  parallel: {
    enabled: true,

    // 工作线程数量(默认为 CPU 核心数)
    workers: 4,

    // 每批处理的文件数量
    batchSize: 100,

    // 超时时间(毫秒)
    timeout: 60000,
  },

  files: [
    // ...
  ],
});

缓存机制

配置缓存以加速重复构建:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 缓存配置
  cache: {
    // Git 信息缓存
    git: {
      enabled: true,
      ttl: 86400,  // 24 小时
    },

    // 文件内容缓存
    content: {
      enabled: true,
      ttl: 3600,  // 1 小时
    },

    // 状态计算缓存
    status: {
      enabled: true,
      ttl: 3600,  // 1 小时
    },
  },

  files: [
    // ...
  ],
});

安全与权限

敏感文件处理

排除敏感文件不被追踪:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',

      // 排除敏感文件
      exclude: [
        '**/internal/**',
        '**/.env*',
        '**/secrets/**',
        '**/admin/**',
      ],
    },
  ],

  // 全局排除规则
  globalExclude: [
    '**/node_modules/**',
    '**/.git/**',
    '**/dist/**',
    '**/.env*',
    '**/credentials*',
  ],
});

访问控制

为仪表板添加访问控制:

// lunaria.config.ts
export default defineConfig({
  // ...其他配置

  dashboard: {
    outputDir: 'public/i18n-status',
    title: 'Translation Status',

    // 访问控制
    accessControl: {
      enabled: true,

      // 基本认证
      basicAuth: {
        enabled: process.env.NODE_ENV === 'production',
        username: process.env.I18N_DASHBOARD_USER,
        password: process.env.I18N_DASHBOARD_PASS,
      },

      // IP 白名单
      ipWhitelist: [
        '192.168.1.0/24',
        '10.0.0.0/8',
      ],

      // 或使用自定义认证函数
      customAuth: async (request) => {
        const token = request.headers.get('Authorization');
        return validateToken(token);
      },
    },
  },
});

审计日志

启用审计日志记录:

// lunaria.config.ts
export default defineConfig({
  // ...其他配置

  // 审计日志
  audit: {
    enabled: true,

    // 日志级别
    level: 'info',  // 'debug' | 'info' | 'warn' | 'error'

    // 日志输出
    outputs: [
      { type: 'file', path: 'logs/lunaria.log' },
      { type: 'console' },
    ],

    // 记录的事件
    events: [
      'config.load',
      'status.calculate',
      'dashboard.generate',
      'api.access',
    ],
  },
});

与企业工具集成

翻译管理系统集成

与专业翻译管理平台集成:

// lunaria.config.ts
export default defineConfig({
  // ...其他配置

  // 集成配置
  integrations: {
    // Crowdin 集成
    crowdin: {
      enabled: true,
      projectId: process.env.CROWDIN_PROJECT_ID,
      apiToken: process.env.CROWDIN_API_TOKEN,

      // 同步配置
      sync: {
        direction: 'bidirectional',  // 'push' | 'pull' | 'bidirectional'
        interval: 3600,  // 同步间隔(秒)
      },
    },

    // 或使用 Transifex
    transifex: {
      enabled: false,
      organization: 'your-org',
      project: 'your-project',
      apiToken: process.env.TRANSIFEX_API_TOKEN,
    },
  },
});

CMS 集成

与内容管理系统集成:

// lunaria.config.ts
export default defineConfig({
  // ...其他配置

  integrations: {
    // Contentful CMS
    contentful: {
      enabled: true,
      spaceId: process.env.CONTENTFUL_SPACE_ID,
      accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,

      // 内容映射
      contentMapping: {
        'blogPost': 'posts/{slug}.md',
        'documentation': 'docs/{slug}.md',
      },
    },

    // 或使用 Strapi
    strapi: {
      enabled: false,
      apiUrl: process.env.STRAPI_API_URL,
      apiToken: process.env.STRAPI_API_TOKEN,
    },
  },
});

内部工具定制

为内部工具创建自定义适配器:

// src/adapters/internal-tool.ts
import { Adapter, FileInfo, TranslationStatus } from '@lunariajs/core';

/**
 * 内部工具适配器
 */
export class InternalToolAdapter implements Adapter {
  name = 'internal-tool';

  constructor(private config: InternalToolConfig) {}

  async syncTranslations(
    files: FileInfo[],
    statuses: TranslationStatus[]
  ): Promise<void> {
    // 将状态同步到内部系统
    const response = await fetch(`${this.config.apiUrl}/translations/sync`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${this.config.apiToken}`,
      },
      body: JSON.stringify({
        files,
        statuses,
        timestamp: new Date().toISOString(),
      }),
    });

    if (!response.ok) {
      throw new Error(`Failed to sync: ${response.statusText}`);
    }
  }

  async getTranslationAssignments(
    file: FileInfo,
    language: string
  ): Promise<Assignment[]> {
    // 从内部系统获取翻译分配信息
    const response = await fetch(
      `${this.config.apiUrl}/assignments?file=${file.path}&lang=${language}`
    );
    return response.json();
  }
}

实战案例:大型开源项目配置

Astro 官方文档案例分析

// Astro 官方文档的 LunariaJS 配置示例
import { defineConfig } from '@lunariajs/core';

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko', 'es', 'pt-br', 'de', 'fr'],

  files: [
    // 主要文档
    {
      sourcePath: 'src/content/docs/en/{slug}.md',
      localizationPath: 'src/content/docs/{lang}/{slug}.md',
      include: ['**/*.md', '**/*.mdx'],
      exclude: ['**/reference/**'],  // API 参考自动生成,不需要翻译
    },

    // 示例代码
    {
      sourcePath: 'examples/{slug}/README.md',
      localizationPath: 'examples/{slug}/README.{lang}.md',
    },
  ],

  dashboard: {
    outputDir: 'public/i18n-status',
    title: 'Astro Docs Translation Status',
    description: 'Track the progress of Astro documentation translations',
  },

  // 自定义状态规则
  statusRules: {
    outdated: {
      dayThreshold: 14,  // 14 天以上算过时
    },
  },

  // 排除规则
  globalExclude: [
    '**/node_modules/**',
    '**/.git/**',
    '**/dist/**',
    '**/CHANGELOG.md',
    '**/CONTRIBUTING.md',
  ],
});

配置文件完整示例

// lunaria.config.ts - 企业级完整配置
import { defineConfig } from '@lunariajs/core';
import { ContentComplexityStrategy } from './strategies/content-complexity';

const isProduction = process.env.NODE_ENV === 'production';
const isCI = process.env.CI === 'true';

export default defineConfig({
  // 基础配置
  sourceLanguage: 'en',
  languages: isProduction
    ? ['en', 'zh-cn', 'ja', 'ko', 'es', 'fr', 'de', 'pt-br']
    : ['en', 'zh-cn'],

  // 文件配置
  files: [
    {
      sourcePath: 'src/content/docs/{slug}.mdx',
      localizationPath: 'src/content/docs/{lang}/{slug}.mdx',
      include: ['**/*.mdx'],
      exclude: isCI ? ['**/draft/**', '**/internal/**'] : [],
      group: 'docs',
    },
    {
      sourcePath: 'src/content/blog/{slug}.md',
      localizationPath: 'src/content/blog/{lang}/{slug}.md',
      group: 'blog',
    },
    {
      sourcePath: 'src/locales/en/{module}.json',
      localizationPath: 'src/locales/{lang}/{module}.json',
      group: 'ui',
    },
  ],

  // 状态规则
  statusRules: {
    outdated: { dayThreshold: 7 },
  },

  // 策略配置
  strategies: {
    default: 'git-timestamp',
    rules: [
      { pattern: 'src/content/docs/**/*.mdx', strategy: 'content-complexity' },
    ],
    custom: [ContentComplexityStrategy],
  },

  // 仪表板配置
  dashboard: {
    outputDir: isProduction ? 'dist/i18n-status' : 'public/i18n-status',
    title: 'Enterprise Docs - Translation Status',
    groupBy: 'group',
  },

  // 性能优化
  incremental: { enabled: true },
  parallel: { enabled: true, workers: 4 },
  cache: {
    git: { enabled: true, ttl: 86400 },
    content: { enabled: true, ttl: 3600 },
  },

  // 安全配置
  globalExclude: [
    '**/node_modules/**',
    '**/.git/**',
    '**/dist/**',
    '**/.env*',
    '**/secrets/**',
  ],

  // 审计日志
  audit: {
    enabled: isProduction,
    level: 'info',
    outputs: [{ type: 'file', path: 'logs/lunaria.log' }],
  },
});

总结

本章探讨了 LunariaJS 的高级配置和企业级应用:

主题关键内容
自定义状态规则天数阈值、自定义检查函数
复杂路径映射多文件模式、多源语言
多仓库管理Monorepo、跨仓库依赖
自定义策略策略接口、实现、注册
性能优化增量构建、并行处理、缓存
安全权限敏感文件、访问控制、审计
企业集成TMS、CMS、内部工具

关键要点

  • 灵活运用条件配置适应不同环境
  • 设计自定义策略满足特定业务需求
  • 启用性能优化处理大规模文件
  • 配置安全措施保护敏感信息

下一步

下一篇文章,我们将进行完整的企业级实战,从零构建一个完整的多语言文档平台,综合运用本系列所有知识点。

敬请期待!


💡 推荐阅读