Add full Simplified Chinese (zh-CN) i18n support

- Add zh-CN locale with 1000+ translations to config/locales/i18n.yml
- Add zh translations (106 keys) to submission_form Vue component
- Add zh translations (221 keys) to template_builder Vue component
- Unify LOCALE_OPTIONS in accounts_controller with available_locales
- Add Chinese mirror sources to Dockerfile for faster builds in China
- Lock PostgreSQL and Caddy versions in docker-compose.yml
- Add deployment guide in Simplified Chinese
pull/677/head
new985211 1 month ago
parent 528a1216f8
commit 5c88f1a73f

@ -2,7 +2,8 @@ FROM ruby:4.0.1-alpine AS download
WORKDIR /fonts
RUN apk --no-cache add wget && \
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
apk --no-cache add wget && \
wget https://github.com/satbyy/go-noto-universal/releases/download/v7.0/GoNotoKurrent-Regular.ttf && \
wget https://github.com/satbyy/go-noto-universal/releases/download/v7.0/GoNotoKurrent-Bold.ttf && \
wget https://github.com/impallari/DancingScript/raw/master/fonts/DancingScript-Regular.otf && \
@ -20,12 +21,16 @@ ENV NODE_ENV=production
WORKDIR /app
RUN apk add --no-cache nodejs yarn git build-base && \
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
apk add --no-cache nodejs yarn git build-base && \
gem sources --remove https://rubygems.org/ && \
gem sources -a https://gems.ruby-china.com/ && \
gem install shakapacker
COPY ./package.json ./yarn.lock ./
RUN yarn install --network-timeout 1000000
RUN yarn config set registry https://registry.npmmirror.com && \
yarn install --network-timeout 1000000
COPY ./bin/shakapacker ./bin/shakapacker
COPY ./config/webpack ./config/webpack
@ -48,7 +53,8 @@ ENV OPENSSL_CONF=/etc/openssl_legacy.cnf
WORKDIR /app
RUN apk add --no-cache libpq vips redis vips-heif onnxruntime
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
apk add --no-cache libpq vips redis vips-heif onnxruntime
RUN addgroup -g 2000 docuseal && adduser -u 2000 -G docuseal -s /bin/sh -D -h /home/docuseal docuseal
@ -66,7 +72,10 @@ activate = 1' >> /etc/openssl_legacy.cnf
COPY --chown=docuseal:docuseal ./Gemfile ./Gemfile.lock ./
RUN apk add --no-cache build-base git libpq-dev yaml-dev && bundle install && apk del --no-cache build-base git libpq-dev yaml-dev && rm -rf ~/.bundle /usr/local/bundle/cache && ruby -e "puts Dir['/usr/local/bundle/**/{spec,rdoc,resources/shared,resources/collation,resources/locales,resources/unicode_data/properties}'] + Dir['/usr/local/bundle/gems/*/{test,tests,examples,sample,misc,doc,docs}'] + Dir['/usr/local/bundle/gems/*/ext/**/*.{c,h,o,S}']" | xargs rm -rf && ln -sf /usr/lib/libonnxruntime.so.1 $(ruby -e "print Dir[Gem::Specification.find_by_name('onnxruntime').gem_dir + '/vendor/*.so'].first")
RUN apk add --no-cache build-base git libpq-dev yaml-dev && \
bundle config set --global mirror.https://rubygems.org https://gems.ruby-china.com && \
bundle install && \
apk del --no-cache build-base git libpq-dev yaml-dev && rm -rf ~/.bundle /usr/local/bundle/cache && ruby -e "puts Dir['/usr/local/bundle/**/{spec,rdoc,resources/shared,resources/collation,resources/locales,resources/unicode_data/properties}'] + Dir['/usr/local/bundle/gems/*/{test,tests,examples,sample,misc,doc,docs}'] + Dir['/usr/local/bundle/gems/*/ext/**/*.{c,h,o,S}']" | xargs rm -rf && ln -sf /usr/lib/libonnxruntime.so.1 $(ruby -e "print Dir[Gem::Specification.find_by_name('onnxruntime').gem_dir + '/vendor/*.so'].first")
COPY --chown=docuseal:docuseal ./bin ./bin
COPY --chown=docuseal:docuseal ./app ./app

@ -9,7 +9,21 @@ class AccountsController < ApplicationController
'pt-PT' => 'Português',
'de-DE' => 'Deutsch',
'it-IT' => 'Italiano',
'nl-NL' => 'Nederlands'
'nl-NL' => 'Nederlands',
'zh-CN' => '中文 (简体)',
'es' => 'Español',
'it' => 'Italiano',
'de' => 'Deutsch',
'fr' => 'Français',
'nl' => 'Nederlands',
'pl' => 'Polski',
'uk' => 'Українська',
'cs' => 'Čeština',
'pt' => 'Português',
'he' => 'עברית',
'ar' => 'العربية',
'ko' => '한국어',
'ja' => '日本語'
}.freeze
before_action :load_account

@ -1524,6 +1524,115 @@ const ja = {
enter_screen_reader_mode: 'スクリーンリーダーモードを有効にする'
}
const i18n = { en, es, it, de, fr, pl, uk, cs, pt, he, nl, ar, ko, ja }
const zh = {
step: '步骤',
form_progress: '表单进度',
close: '关闭',
uploaded_files: '已上传文件',
signature_drawing_area: '签名绘制区域。使用鼠标或触摸来绘制您的签名。',
kba: 'KBA',
please_upload_an_image_file: '请上传图片文件',
must_be_characters_length: '必须为 {number} 个字符',
complete_all_required_fields_to_proceed_with_identity_verification: '请填写所有必填字段以继续身份验证。',
verify_id: '验证身份',
identity_verification: '身份验证',
complete: '完成',
fill_all_required_fields_to_complete: '填写所有必填字段以完成',
sign_and_complete: '签署并完成',
text: '文本',
by_clicking_you_agree_to_the: '点击"{button}"即表示您同意',
electronic_signature_disclosure: '电子签名披露',
esignature_disclosure: '电子签名披露',
signature: '签名',
initials: '首字母',
sign_on_the_touchscreen: '在触摸屏上签名',
approved: '已批准',
reviewed: '已审核',
other: '其他',
authored_by_me: '由我创作',
invite: '邀请',
email: '电子邮件',
approved_by: '批准人',
reviewed_by: '审核人',
authored_by: '创作人',
select_a_reason: '选择原因',
scan_the_qr_code_with_the_camera_app_to_open_the_form_on_mobile_and_draw_your_signature: '使用相机应用扫描二维码,在手机上打开表单并绘制签名',
date: '日期',
number: '数字',
value_is_invalid: '值无效',
verification_code_is_invalid: '验证码无效',
already_paid: '已支付',
image: '图片',
pay: '支付',
take_photo: '拍照',
number_phone_is_invalid: '{number} 电话号码无效',
file: '文件',
digitally_signed_by: '数字签名人',
reason: '原因',
select: '选择',
checkbox: '复选框',
multiple: '多选',
radio: '单选',
cells: '单元格',
stamp: '印章',
minimize: '最小化',
payment: '支付',
phone: '电话',
start_now: '立即开始',
continue: '继续',
sign_now: '立即签署',
type_here_: '在此输入...',
optional: '可选',
option: '选项',
appears_on: '显示于',
page: '页',
select_your_option: '选择您的选项',
complete_hightlighted_checkboxes_and_click: '勾选高亮的复选框并点击',
submit: '提交',
next: '下一步',
click_to_upload: '点击上传',
or_drag_and_drop_files: '或拖放文件',
send_copy_via_email: '通过电子邮件发送副本',
download: '下载',
clear: '清除',
redraw: '重绘',
draw_initials: '绘制首字母',
type_signature_here: '在此输入签名',
type_initial_here: '在此输入首字母',
form_has_been_completed: '表单已完成!',
document_has_been_signed: '文件已签署!',
documents_have_been_signed: '文件已签署!',
create_a_free_account: '创建免费账户',
powered_by: '由以下提供支持',
please_check_the_box_to_continue: '请勾选复选框以继续。',
open_source_documents_software: '开源文档软件',
verified_phone_number: '验证电话号码',
use_international_format: '使用国际格式:+1xxx',
six_digits_code: '6位数字验证码',
change_phone_number: '更改电话号码',
sending: '发送中...',
resend_code: '重新发送验证码',
verification_code_has_been_resent: '验证码已通过短信重新发送',
please_fill_all_required_fields: '请填写所有必填字段',
set_today: '设为今天',
toggle_multiline_text: '切换多行文本',
draw_signature: '绘制签名',
type_initial: '输入首字母',
draw: '绘制',
type: '输入',
type_text: '输入文本',
email_has_been_sent: '电子邮件已发送',
processing: '处理中',
pay_with_stripe: '使用 Stripe 支付',
reupload: '重新上传',
upload: '上传',
files: '文件',
signature_is_too_small_or_simple_please_redraw: '签名太小或太简单。请重新绘制。',
browser_privacy_settings_block_canvas: '您的浏览器隐私设置限制了绘图画布的使用。请使用其他浏览器或设备,或禁用阻止画布的隐私设置以进行签名。',
wait_countdown_seconds: '请等待 {countdown} 秒',
enter_screen_reader_mode: '进入屏幕阅读器模式'
}
const i18n = { en, es, it, de, fr, pl, uk, cs, pt, he, nl, ar, ko, ja, zh }
export default i18n

@ -1566,4 +1566,228 @@ const nl = {
viewing_revision_from: 'Revisie van {date} bekijken'
}
export { en, es, it, pt, fr, de, nl }
const zh = {
fixed: '固定',
default: '默认',
save_as_custom_field: '保存为自定义字段',
kba: 'KBA',
analyzing_: '分析中...',
download: '下载',
downloading_: '下载中...',
view: '查看',
autodetect_fields: '自动检测字段',
payment_link: '支付链接',
strikeout: '删除线',
draw_strikethrough_the_document: '在文档上绘制删除线',
quantity: '数量',
prefillable: '可预填',
signature_id: '签名 ID',
error_message: '错误信息',
length: '长度',
min: '最小值',
max: '最大值',
font: '字体',
party: '参与方',
date_signed: '签名日期',
method: '方式',
reorder_fields: '重新排序字段',
verify_id: '验证身份',
obtain_qualified_electronic_signature_with_the_trusted_provider_click_to_learn_more: '通过可信提供商获取合格的电子签名 (QeS)。点击了解更多。',
editable: '可编辑',
recurrent: '定期',
one_off: '一次性',
search_field: '搜索字段',
field_not_found: '未找到字段',
clear: '清除',
align: '对齐',
resize: '调整大小',
width: '宽度',
height: '高度',
add_all_required_fields_to_continue: '添加所有必填字段以继续',
uploaded_pdf_contains_form_fields_keep_or_remove_them: '上传的 PDF 包含表单字段。保留还是移除?',
keep: '保留',
left: '左',
heading: '标题',
validation: '验证',
add_blank_page: '添加空白页',
right: '右',
center: '居中',
description: '描述',
display_title: '显示标题',
with_logo: '带 Logo',
unchecked: '未选中',
price: '价格',
type: '类型',
list: '列表',
no_variables: '暂无变量',
no_variables_description: '在文档中添加 [[variable]] 标记以创建动态内容变量。',
type_value: '输入值',
equal: '等于',
not_equal: '不等于',
greater_than: '大于',
less_than: '小于',
contains: '包含',
does_not_contain: '不包含',
not_empty: '非空',
empty: '为空',
select_field_: '选择字段...',
select_value_: '选择值...',
remove_condition: '移除条件',
add_condition: '添加条件',
are_you_sure_: '确定吗?',
sign_yourself: '自行签署',
set_signing_date: '设置签名日期',
signing_date: '签名日期',
signing_date_and_time: '签名日期和时间',
send: '发送',
remove: '移除',
edit: '编辑',
settings: '设置',
up: '上移',
down: '下移',
checked: '已选中',
current_date: '当前日期',
save: '保存',
cancel: '取消',
any: '任意',
drawn: '手绘',
drawn_or_typed: '手绘或输入',
drawn_or_upload: '手绘或上传',
upload: '上传',
formula: '公式',
typed: '输入',
draw_field_on_the_document: '在文档上绘制字段',
click_to_upload: '点击上传',
or_drag_and_drop_files: '或拖放文件',
uploading: '上传中',
processing_: '处理中...',
add_pdf_documents_or_images: '添加 PDF 文档或图片',
add_documents_or_images: '添加文档或图片',
add_a_new_document: '添加新文档',
replace_existing_document: '替换现有文档',
clone_and_replace_documents: '克隆并替换文档',
required: '必填',
default_value: '默认值',
format: '格式',
read_only: '只读',
page: '页',
draw_new_area: '绘制新区域',
copy_to_all_pages: '复制到所有页面',
more: '更多',
add_option: '添加选项',
option: '选项',
options: '选项',
condition: '条件',
make_dynamic: '设为动态',
first_party: '第一方',
second_party: '第二方',
third_party: '第三方',
fourth_party: '第四方',
fifth_party: '第五方',
sixth_party: '第六方',
seventh_party: '第七方',
eighth_party: '第八方',
ninth_party: '第九方',
tenth_party: '第十方',
eleventh_party: '第十一方',
twelfth_party: '第十二方',
thirteenth_party: '第十三方',
fourteenth_party: '第十四方',
fifteenth_party: '第十五方',
sixteenth_party: '第十六方',
seventeenth_party: '第十七方',
eighteenth_party: '第十八方',
nineteenth_party: '第十九方',
twentieth_party: '第二十方',
draw: '绘制',
add: '添加',
or_add_field_without_drawing: '或不绘制直接添加字段',
text: '文本',
number: '数字',
signature: '签名',
initials: '首字母',
date: '日期',
image: '图片',
file: '文件',
select: '选择',
checkbox: '复选框',
multiple: '多选',
radio: '单选',
cells: '单元格',
stamp: '印章',
payment: '支付',
phone: '电话',
text_field: '文本字段',
signature_field: '签名字段',
initials_field: '首字母字段',
date_field: '日期字段',
number_field: '数字字段',
image_field: '图片字段',
file_field: '文件字段',
select_field: '选择字段',
checkbox_field: '复选框字段',
multiple_field: '多选字段',
radio_field: '单选组字段',
cells_field: '单元格字段',
stamp_field: '印章字段',
payment_field: '支付字段',
phone_field: '电话字段',
draw_a_text_field_on_the_page_with_a_mouse: '使用鼠标在页面上绘制文本字段',
drag_and_drop_any_other_field_type_on_the_page: '将其他字段类型拖放到页面上',
click_on_the_field_type_above_to_start_drawing_it: '点击上面的字段类型开始绘制',
please_draw_fields_to_prepare_the_document: '请绘制字段以准备文档。',
only_pdf_and_images_are_supported: '仅支持 PDF 和图片。',
unlock_sms_verified_phone_number_field_with_paid_plan_use_text_field_for_phone_numbers_without_verification: '通过付费计划解锁短信验证的电话号码字段。对于不需要验证的电话号码,请使用文本字段。',
available_only_in_pro: '仅 Pro 版可用',
failed_to_download_files: '下载文件失败',
please_add_fields_for_the_submitter_name_or_remove_the_submitter_name_if_not_needed: '请为 {submitter_name} 添加字段。或者,如果不需要,请移除 {submitter_name}。',
draw_field: '绘制 {field} 字段',
replace: '替换',
uploading_: '上传中...',
add_document: '添加文档',
none: '无',
ssn: 'SSN',
ein: 'EIN',
email: '电子邮件',
url: 'URL',
zip: '邮政编码',
custom: '自定义',
numbers_only: '仅数字',
letters_only: '仅字母',
regexp_validation: '正则表达式验证',
custom_validation: '自定义验证',
length_validation: '长度验证',
number_range: '数字范围',
enter_pdf_password: '输入 PDF 密码',
wrong_password: '密码错误',
currency: '货币',
save_and_preview: '保存并预览',
preferences: '偏好设置',
available_in_pro: 'Pro 版可用',
some_fields_are_missing_in_the_formula: '公式中缺少一些字段。',
learn_more: '了解更多',
and: '和',
or: '或',
start_a_quick_tour_to_learn_how_to_create_and_send_your_first_document: '开始快速导览,学习如何创建和发送您的第一份文档',
start_tour: '开始导览',
or_add_from: '或添加自',
sync: '同步',
syncing: '同步中...',
copy: '复制',
paste: '粘贴',
select_fields: '选择字段',
draw_fields: '绘制字段',
align_left: '左对齐',
align_right: '右对齐',
align_top: '顶部对齐',
align_bottom: '底部对齐',
fields_selected: '已选择 {count} 个字段',
field_added: '已添加 {count} 个字段',
fields_added: '已添加 {count} 个字段',
revisions: '修订版本',
apply: '应用',
no_revisions_yet: '暂无修订版本',
viewing_revision_from: '查看来自 {date} 的修订版本'
}
export { en, es, it, pt, fr, de, nl, zh }

@ -25,7 +25,7 @@ module DocuSeal
config.active_storage.draw_routes = ENV['MULTITENANT'] != 'true'
config.i18n.available_locales = %i[en en-US en-GB es-ES fr-FR pt-PT de-DE it-IT nl-NL
config.i18n.available_locales = %i[en en-US en-GB es-ES fr-FR pt-PT de-DE it-IT nl-NL zh-CN
es it de fr nl pl uk cs pt he ar ko ja]
config.i18n.fallbacks = [:en]

File diff suppressed because it is too large Load Diff

@ -13,7 +13,7 @@ services:
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/docuseal
postgres:
image: postgres:18
image: postgres:17.5
volumes:
- './pg_data:/var/lib/postgresql/18/docker'
environment:
@ -27,7 +27,7 @@ services:
retries: 5
caddy:
image: caddy:latest
image: caddy:2.10-alpine
command: caddy reverse-proxy --from $HOST --to app:3000
ports:
- 80:80

@ -0,0 +1,397 @@
# DocuSeal 简体中文支持部署指南
## 概述
本文档记录了为 DocuSeal 开源电子签名平台添加完整简体中文zh-CN支持的整个过程包括后端 Rails i18n 翻译、前端 Vue 组件翻译、Dockerfile 国内镜像源适配,以及语言选项统一。
## 修改文件清单
| 文件 | 变更量 | 说明 |
|------|--------|------|
| `config/locales/i18n.yml` | +1070 行 | 新增 zh-CN 区域,完整中文翻译 |
| `app/javascript/submission_form/i18n.js` | +111 行 | 新增前端提交表单 zh 翻译对象 |
| `app/javascript/template_builder/i18n.js` | +226 行 | 新增前端模板编辑器 zh 翻译对象 |
| `app/controllers/accounts_controller.rb` | +16 行 | 统一语言选项列表 |
| `config/application.rb` | +1 行 | 添加 zh-CN 到可用语言列表 |
| `Dockerfile` | +19 行 | 添加国内镜像源加速构建 |
| `docker-compose.yml` | 版本锁定 | 锁定 PostgreSQL 和 Caddy 版本 |
---
## 一、i18n 中文翻译详情
### 1.1 后端 Rails 翻译config/locales/i18n.yml
在文件末尾(第 8088 行)新增 `zh-CN` 语言区域,采用 YAML 锚点继承模式:
```yaml
zh-CN:
<<: *en # 退
language_zh-CN: 中文 (简体)
sign_in: 登录
# ... 1000+ 条中文翻译
```
翻译覆盖范围:
- 登录/注册/密码重置流程
- 账户设置、偏好设置
- 文档模板管理(创建、编辑、归档)
- 签署流程(签名请求、提醒、完成通知)
- 邮件模板(签名请求邮件、提醒邮件、完成通知)
- 表单字段类型(文本、签名、日期、选择框等)
- API 参考、嵌入集成
- Webhook、SSO、SAML 配置
- 应用导览、分页
- 订阅计划、账单
- Devise 认证系统
- Doorkeeper OAuth 授权
- 所有系统通知/提示消息
**技术要点:**
- 使用 `<<: *en` 继承英文键,未翻译的键自动回退英文
- 配合 `config.i18n.fallbacks = [:en]` 双重保障
- 保留了所有插值变量(`%{name}`, `%{count}` 等)和 HTML 标签
### 1.2 前端提交表单翻译app/javascript/submission_form/i18n.js
新增 `zh` 翻译对象107 个键),覆盖:
- 表单进度提示
- 签名绘制操作(手绘、输入、上传)
- 字段类型名称
- 身份验证流程(短信/邮件验证码)
- 文件上传操作
- 支付界面
关键代码:
```js
const zh = {
step: '步骤',
sign_and_complete: '签署并完成',
draw_signature: '绘制签名',
// ... 107 个翻译键
}
const i18n = { en, es, it, de, fr, pl, uk, cs, pt, he, nl, ar, ko, ja, zh }
```
**技术要点:**
- 前端 locale 解析:`I18n.locale.to_s.split('-').first` → `"zh"`,匹配 `i18n["zh"]`
- 回退链:`i18n[locale] → i18n[browserLang] → i18n.en → key`
### 1.3 前端模板编辑器翻译app/javascript/template_builder/i18n.js
新增 `zh` 翻译对象221 个键),覆盖:
- 字段绘制和编辑操作
- 参与方管理第1-20方
- 字段属性设置(字体、对齐、验证等)
- 条件逻辑配置
- 公式编辑器
- 修订版本管理
- PDF 密码保护
关键代码:
```js
const zh = {
fixed: '固定',
draw_field_on_the_document: '在文档上绘制字段',
// ... 221 个翻译键
}
export { en, es, it, pt, fr, de, nl, zh }
```
### 1.4 语言选项统一
`config/application.rb``available_locales` 包含 zh-CN完整 22 种语言)。
`accounts_controller.rb``LOCALE_OPTIONS` 从原来的 9 种扩展到 22 种,与 `available_locales` 完全一致:
```ruby
LOCALE_OPTIONS = {
'en-US' => 'English (United States)',
'en-GB' => 'English (United Kingdom)',
'fr-FR' => 'Français',
'es-ES' => 'Español',
'pt-PT' => 'Português',
'de-DE' => 'Deutsch',
'it-IT' => 'Italiano',
'nl-NL' => 'Nederlands',
'zh-CN' => '中文 (简体)',
'es' => 'Español',
'it' => 'Italiano',
'de' => 'Deutsch',
'fr' => 'Français',
'nl' => 'Nederlands',
'pl' => 'Polski',
'uk' => 'Українська',
'cs' => 'Čeština',
'pt' => 'Português',
'he' => 'עברית',
'ar' => 'العربية',
'ko' => '한국어',
'ja' => '日本語'
}.freeze
```
---
## 二、Dockerfile 国内镜像源适配
### 2.1 修改内容
| 构建阶段 | 包管理器 | 原地址 | 替换为国内镜像 |
|---------|---------|--------|---------------|
| download | Alpine apk | `dl-cdn.alpinelinux.org` | `mirrors.tuna.tsinghua.edu.cn` |
| webpack | Alpine apk | `dl-cdn.alpinelinux.org` | `mirrors.tuna.tsinghua.edu.cn` |
| webpack | RubyGems | `rubygems.org` | `gems.ruby-china.com` |
| webpack | Yarn/npm | `registry.yarnpkg.com` | `registry.npmmirror.com` |
| app | Alpine apk | `dl-cdn.alpinelinux.org` | `mirrors.tuna.tsinghua.edu.cn` |
| app | Bundler | `rubygems.org` | `gems.ruby-china.com` |
### 2.2 关键代码位置
**Alpine apk 镜像**3 个阶段均添加):
```dockerfile
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
```
**RubyGems 镜像**webpack 阶段):
```dockerfile
gem sources --remove https://rubygems.org/ && \
gem sources -a https://gems.ruby-china.com/
```
**Bundler 镜像**app 阶段):
```dockerfile
bundle config set --global mirror.https://rubygems.org https://gems.ruby-china.com
```
**Yarn 镜像**webpack 阶段):
```dockerfile
yarn config set registry https://registry.npmmirror.com
```
### 2.3 GitHub 资源下载说明
以下资源仍直接从 GitHub 下载(未走镜像),因为它们通常通过 HTTPS 443 端口可以正常访问:
| 资源 | 大小 | 用途 |
|------|------|------|
| GoNotoKurrent 字体Regular + Bold | ~30MB | 文档默认字体 |
| DancingScript 字体 | ~200KB | 签名字体 |
| ONNX 模型文件 | ~10MB | 表单字段自动检测 |
| PDFium 二进制包 | ~30MB | PDF 渲染引擎 |
如果 GitHub 下载仍然超时,可手动下载后通过 Docker volume 挂载,或使用 `ghproxy.net` 等 GitHub 文件代理。
---
## 三、构建与部署
### 3.1 构建 Docker 镜像
```bash
cd /home/new211/my_project/DocuSeal/docuseal
# 构建镜像(首次约 15-30 分钟)
docker build -t docuseal-zh:latest .
# 如需完全重新构建(不使用缓存)
docker build -t docuseal-zh:latest . --no-cache
```
### 3.2 修改 docker-compose.yml
```yaml
services:
app:
depends_on:
postgres:
condition: service_healthy
image: docuseal-zh:latest # 改为本地构建的镜像
# image: docuseal/docuseal:latest # 原始镜像(无中文)
ports:
- 3000:3000
volumes:
- ./docuseal:/data/docuseal
environment:
- FORCE_SSL=${HOST}
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/docuseal
postgres:
image: postgres:17.5
volumes:
- './pg_data:/var/lib/postgresql/18/docker'
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: docuseal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
```
### 3.3 启动服务
```bash
# 创建数据目录
mkdir -p ./docuseal ./pg_data
# 启动
docker compose up -d
# 查看日志
docker compose logs -f app
```
访问 `http://localhost:3000` 即可使用。
### 3.4 本地开发模式(不依赖 Docker
```bash
# 前提:已安装 Ruby 4.0+、Node.js、PostgreSQL、Yarn
# 安装依赖
bundle install
yarn install
# 编译前端
bin/shakapacker
# 准备数据库
bin/rails db:prepare
# 启动
bin/rails server
```
---
## 四、验证方法
### 4.1 快速验证(无需启动服务)
```bash
# 1. 验证 YAML 格式
ruby -ryaml -e "YAML.load_file('config/locales/i18n.yml')" && echo "YAML 格式正确"
# 2. 验证前端 JS 翻译键数量
node -e "import('./app/javascript/submission_form/i18n.js').then(m => console.log('zh 键数:', Object.keys(m.default.zh).length))"
node -e "import('./app/javascript/template_builder/i18n.js').then(m => console.log('zh 键数:', Object.keys(m.zh).length))"
# 3. 确认 zh-CN 已注册到可用语言列表
grep "zh-CN" config/application.rb
grep "zh-CN" app/controllers/accounts_controller.rb
```
预期输出:
- YAML 格式正确
- submission_form zh 键数: **106**
- template_builder zh 键数: **221**
- 两个 grep 命令均有输出
### 4.2 功能验证清单
启动服务后,按以下清单验证:
| 序号 | 页面 | 操作 | 预期结果 |
|------|------|------|---------|
| 1 | 登录页 `/` | 点击语言下拉菜单 | 显示 **中文 (简体)** 选项 |
| 2 | 登录页 | 选择中文 | 整个界面切换为中文 |
| 3 | 注册页 `/sign_up` | 查看表单 | 字段标签显示中文 |
| 4 | 登录后首页 | 查看导航 | 菜单项显示中文 |
| 5 | 模板创建 | 创建新模板 | 编辑器界面显示中文 |
| 6 | 模板编辑器 | 拖放字段 | 字段名称、设置面板显示中文 |
| 7 | 模板编辑器 | 查看参与方 | 第一方/第二方等显示中文 |
| 8 | 发送文档 | 添加收件人 | 表单和按钮显示中文 |
| 9 | 签署页面 | 以签署者身份打开 | 签名操作、按钮显示中文 |
| 10 | 账户设置 `/settings/account` | 查看语言下拉 | 包含完整 22 种语言选项 |
| 11 | 邮件模板 | 查看邮件设置 | 模板变量说明显示中文 |
| 12 | API 参考 | 查看 API 文档 | 界面显示中文 |
---
## 五、翻译回退机制
当某个翻译键在 `zh-CN` 中未定义时,系统按以下优先级查找:
```
zh-CN 翻译 → 英文翻译en→ 原始键名
```
具体实现:
1. **后端 Rails**`config.i18n.fallbacks = [:en]` + YAML `<<: *en` 锚点继承
2. **前端提交表单**`this.i18n[key] || i18n[lang]?.[key] || i18n[browserLang]?.[key] || i18n.en[key] || key`
3. **前端模板编辑器**:同上逻辑
---
## 六、常见问题
### Q1切换中文后部分文字仍显示英文
**原因**:前端 JS 翻译键缺失。检查对应 Vue 组件的 i18n.js 文件中是否有对应的 `zh` 键。
**解决**:在 `app/javascript/submission_form/i18n.js``template_builder/i18n.js``zh` 对象中补充缺失的键。
### Q2Docker 构建时 GitHub 下载超时?
**原因**:国内网络访问 GitHub 不稳定。
**解决方案(按优先级)**
1. 使用代理构建:
```bash
docker build --build-arg HTTP_PROXY=http://your-proxy:port \
--build-arg HTTPS_PROXY=http://your-proxy:port \
-t docuseal-zh:latest .
```
2. 手动下载资源文件,修改 Dockerfile 使用 `COPY` 替代 `wget`
3. 尝试不同的 GitHub 镜像代理(在 `wget` URL 前加前缀):
- `https://ghproxy.net/``https://ghproxy.com/`
- `https://mirror.ghproxy.com/`
### Q3如何添加更多中文语言变体如 zh-TW 繁体中文)?
```bash
# 1. 复制 zh-CN 翻译作为基础
# 2. 修改为繁体中文
# 3. 在 config/application.rb 的 available_locales 中添加 :'zh-TW'
# 4. 在 accounts_controller.rb 的 LOCALE_OPTIONS 中添加
# 5. 在前端 i18n.js 中添加 zh-TW 翻译对象
```
### Q4如何只构建前端翻译不重新构建整个 Docker 镜像)?
```bash
# 本地开发模式重新编译前端
bin/shakapacker
# 或者只重编译前端资源
yarn install && bin/shakapacker
```
---
## 七、文件变更完整差异
如需查看所有变更的完整差异:
```bash
git diff HEAD -- \
config/locales/i18n.yml \
app/javascript/submission_form/i18n.js \
app/javascript/template_builder/i18n.js \
app/controllers/accounts_controller.rb \
config/application.rb \
Dockerfile
```
---
> **文档版本**1.0
> **最后更新**2026-05-18
> **适用版本**DocuSeal (master branch)
Loading…
Cancel
Save