diff --git a/README.md b/README.md index 5fb4313..2170675 100644 --- a/README.md +++ b/README.md @@ -145,9 +145,9 @@ ConvertX-CN 支援以下語言: | 語言代碼 | 語言名稱 | | -------- | ---------------- | -| en | English(預設) | -| zh-TW | 繁體中文 | +| zh-TW | 繁體中文(預設) | | zh-CN | 简体中文 | +| en | English | | ja | 日本語 | | ko | 한국어 | diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..d499718 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,65 @@ +# 環境變數設定 + +所有環境變數皆為選填,建議至少設定 `JWT_SECRET`。 + +## 安全性設定 + +| 變數名稱 | 預設值 | 說明 | +| -------------------- | ------------ | ---------------------------------- | +| JWT_SECRET | randomUUID() | 用於簽署 JWT 的密鑰字串(請務必更改) | +| ACCOUNT_REGISTRATION | false | 是否允許註冊新帳號 | +| HTTP_ALLOWED | false | 是否允許 HTTP 連線(僅本地使用) | +| ALLOW_UNAUTHENTICATED| false | 是否允許未登入使用 | + +## 檔案管理 + +| 變數名稱 | 預設值 | 說明 | +| ------------------------- | ------ | ------------------------------------ | +| AUTO_DELETE_EVERY_N_HOURS | 24 | 自動刪除超過 N 小時的檔案(0 = 停用) | + +## 進階設定 + +| 變數名稱 | 預設值 | 說明 | +| ------------------ | ------ | -------------------------------------- | +| WEBROOT | | 子路徑部署,例如 `/convertx` | +| FFMPEG_ARGS | | FFmpeg 輸入參數,例如 `-hwaccel vaapi` | +| FFMPEG_OUTPUT_ARGS | | FFmpeg 輸出參數,例如 `-preset veryfast` | +| HIDE_HISTORY | false | 隱藏歷史紀錄頁面 | +| MAX_CONVERT_PROCESS| 0 | 最大同時轉換數(0 = 無限制) | + +## 本地化設定 + +| 變數名稱 | 預設值 | 說明 | +| -------- | ------ | ----------------------------- | +| LANGUAGE | en | 日期格式語言(BCP 47 格式) | +| TZ | UTC | 時區設定,例如 `Asia/Taipei` | + +## 範例配置 + +### 開發環境 + +```yaml +environment: + - HTTP_ALLOWED=true + - ACCOUNT_REGISTRATION=true +``` + +### 生產環境 + +```yaml +environment: + - JWT_SECRET=your-very-long-and-random-secret-key + - ACCOUNT_REGISTRATION=false + - HTTP_ALLOWED=false + - TZ=Asia/Taipei + - AUTO_DELETE_EVERY_N_HOURS=48 +``` + +### 公開服務(允許匿名使用) + +```yaml +environment: + - ALLOW_UNAUTHENTICATED=true + - HIDE_HISTORY=true + - AUTO_DELETE_EVERY_N_HOURS=1 +``` diff --git a/docs/converters.md b/docs/converters.md new file mode 100644 index 0000000..826e2a7 --- /dev/null +++ b/docs/converters.md @@ -0,0 +1,49 @@ +# 支援的轉換器 + +ConvertX-CN 完整版已內建以下所有轉換器,開箱即用。 + +## 轉換器列表 + +| 轉換器 | 用途 | 輸入格式數 | 輸出格式數 | +| ------ | ---- | ---------- | ---------- | +| [Inkscape](https://inkscape.org/) | 向量圖形 | 7 | 17 | +| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 | +| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 | +| [Vips](https://github.com/libvips/libvips) | 圖片 | 45 | 23 | +| [libheif](https://github.com/strukturag/libheif) | HEIF | 2 | 4 | +| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 | +| [Calibre](https://calibre-ebook.com/) | 電子書 | 26 | 19 | +| [LibreOffice](https://www.libreoffice.org/) | 文件 | 41 | 22 | +| [Dasel](https://github.com/TomWright/dasel) | 資料檔案 | 5 | 4 | +| [Pandoc](https://pandoc.org/) | 文件 | 43 | 65 | +| [msgconvert](https://github.com/mvz/email-outlook-message-perl) | Outlook | 1 | 1 | +| VCF to CSV | 聯絡人 | 1 | 1 | +| [dvisvgm](https://dvisvgm.de/) | 向量圖形 | 4 | 2 | +| [ImageMagick](https://imagemagick.org/) | 圖片 | 245 | 183 | +| [GraphicsMagick](http://www.graphicsmagick.org/) | 圖片 | 167 | 130 | +| [Assimp](https://github.com/assimp/assimp) | 3D 模型 | 77 | 23 | +| [FFmpeg](https://ffmpeg.org/) | 影音 | ~472 | ~199 | +| [Potrace](https://potrace.sourceforge.net/) | 點陣轉向量 | 4 | 11 | +| [VTracer](https://github.com/visioncortex/vtracer) | 點陣轉向量 | 8 | 1 | +| [Markitdown](https://github.com/microsoft/markitdown) | 文件 | 6 | 1 | + +> 註:FFmpeg 的格式數量包含部分重複格式。 + +## 內建依賴 + +ConvertX-CN 完整版已預載: + +| 類別 | 內建內容 | +| ---- | -------- | +| **文件轉換** | LibreOffice (headless)、Pandoc | +| **LaTeX** | TexLive Full(完整版,支援所有 LaTeX 需求) | +| **OCR 識別** | Tesseract OCR + 繁體中文、簡體中文、日文、韓文、英文、德文語言包 | +| **CJK 字型** | Noto CJK(中日韓)、Noto Emoji、微軟核心字型、標楷體 | +| **影音轉換** | FFmpeg、ImageMagick、GraphicsMagick | +| **向量圖形** | Inkscape、Potrace、VTracer、resvg | +| **電子書** | Calibre | +| **其他** | Ghostscript、MuPDF、Poppler、libheif、libjxl 等 | + +## 新增轉換器 + +如需新增轉換器支援,請至 [GitHub Issues](https://github.com/pi-docket/ConvertX-CN/issues) 提出需求或直接發送 Pull Request。 diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..483f2e3 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,68 @@ +# 快速開始 + +## 系統需求 + +- Docker 20.10+ +- Docker Compose v2+(推薦) +- 至少 4GB RAM(建議 8GB) +- 10GB 磁碟空間(用於 Docker image) + +## 安裝方式 + +### 方法一:Docker Run + +```bash +docker run -d \ + --name convertx-cn \ + -p 3000:3000 \ + -v ./data:/app/data \ + -e TZ=Asia/Taipei \ + convertx/convertx-cn:latest +``` + +### 方法二:Docker Compose(推薦) + +建立 `docker-compose.yml`: + +```yaml +services: + convertx: + image: convertx/convertx-cn:latest + container_name: convertx-cn + restart: unless-stopped + ports: + - "3000:3000" + volumes: + - ./data:/app/data + environment: + - TZ=Asia/Taipei + - JWT_SECRET=請更換為一個長且隨機的字串 + - ACCOUNT_REGISTRATION=false + - HTTP_ALLOWED=false + - AUTO_DELETE_EVERY_N_HOURS=24 +``` + +啟動服務: + +```bash +docker compose up -d +``` + +## 首次設定 + +1. 開啟瀏覽器訪問 `http://localhost:3000` +2. 建立第一個帳號(此帳號即為管理員) +3. 登入後即可開始使用 + +## 更新版本 + +```bash +docker compose pull +docker compose up -d +``` + +## 下一步 + +- 查看 [環境變數設定](./configuration.md) +- 瞭解 [支援的轉換器](./converters.md) +- 閱讀 [多語言設定](./i18n.md) diff --git a/docs/i18n.md b/docs/i18n.md new file mode 100644 index 0000000..dd58938 --- /dev/null +++ b/docs/i18n.md @@ -0,0 +1,89 @@ +# 多語言支援(i18n) + +ConvertX-CN 支援多種語言的使用者介面。 + +## 支援語言 + +| 語言代碼 | 語言名稱 | +| -------- | ---------------- | +| zh-TW | 繁體中文(預設) | +| zh-CN | 简体中文 | +| en | English | +| ja | 日本語 | +| ko | 한국어 | + +## 語言切換 + +1. 在網站右上角的導航列可看到語言選擇器(圖示) +2. 點擊後選擇偏好語言 +3. 語言偏好會自動保存到 Cookie 中 +4. 下次訪問時會記住您的偏好 + +## 自動偵測 + +首次訪問時,系統會根據瀏覽器的語言設定自動選擇最適合的語言。 + +優先順序: + +1. Cookie 中儲存的語言偏好 +2. 瀏覽器的 `Accept-Language` 標頭 +3. 預設語言(繁體中文) + +## 新增語言 + +如要添加新語言支援,請按以下步驟: + +### 1. 建立翻譯檔案 + +在 `src/locales/` 目錄新增語言檔案,例如 `fr.json`(法文): + +```json +{ + "common": { + "appName": "ConvertX-CN", + "poweredBy": "Propulsé par", + ... + }, + "nav": { + "history": "Historique", + ... + } +} +``` + +### 2. 註冊語言 + +在 `src/i18n/index.ts` 中: + +```typescript +// 導入新語言檔案 +import fr from "../locales/fr.json"; + +// 在 supportedLocales 陣列中添加 +export const supportedLocales: LocaleConfig[] = [ + // ... 現有語言 + { code: "fr", name: "French", nativeName: "Français" }, +]; + +// 在 translations 物件中註冊 +const translations: Record = { + // ... 現有翻譯 + fr: fr as TranslationData, +}; +``` + +### 3. 更新類型定義 + +在 `SupportedLocale` 類型中添加新語言代碼: + +```typescript +export type SupportedLocale = "zh-TW" | "zh-CN" | "en" | "ja" | "ko" | "fr"; +``` + +## 貢獻翻譯 + +歡迎提交 Pull Request 來新增或改進翻譯!請確保: + +- 翻譯完整(包含所有 key) +- 語法正確 +- 遵循現有的翻譯風格 diff --git a/src/components/base.tsx b/src/components/base.tsx index 8eeb1db..c37291b 100644 --- a/src/components/base.tsx +++ b/src/components/base.tsx @@ -3,7 +3,7 @@ import { type SupportedLocale, defaultLocale, getTranslations } from "../i18n"; export const BaseHtml = ({ children, - title = "ConvertX", + title = "ConvertX-CN", webroot = "", locale = defaultLocale, }: { @@ -33,17 +33,17 @@ export const BaseHtml = ({ {children} diff --git a/src/components/header.tsx b/src/components/header.tsx index be090f9..ab4daa2 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -106,7 +106,7 @@ export const Header = ({ diff --git a/src/i18n/index.ts b/src/i18n/index.ts index b8ae4e8..3ecbc9e 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -13,14 +13,16 @@ export interface LocaleConfig { } export const supportedLocales: LocaleConfig[] = [ - { code: "en", name: "English", nativeName: "English" }, { code: "zh-TW", name: "Chinese (Traditional)", nativeName: "繁體中文" }, { code: "zh-CN", name: "Chinese (Simplified)", nativeName: "简体中文" }, + { code: "en", name: "English", nativeName: "English" }, { code: "ja", name: "Japanese", nativeName: "日本語" }, { code: "ko", name: "Korean", nativeName: "한국어" }, ]; -export const defaultLocale: SupportedLocale = "en"; +// Default to zh-TW, fallback to en +export const defaultLocale: SupportedLocale = "zh-TW"; +export const fallbackLocale: SupportedLocale = "en"; // Type definitions for translation keys export type TranslationData = typeof en; diff --git a/src/i18n/service.ts b/src/i18n/service.ts index 3d5629b..4019c76 100644 --- a/src/i18n/service.ts +++ b/src/i18n/service.ts @@ -1,8 +1,7 @@ -import { Elysia, t } from "elysia"; +import { Elysia } from "elysia"; import { type SupportedLocale, createTranslator, - defaultLocale, detectLocale, getLocale, } from "../i18n"; @@ -12,29 +11,31 @@ import { * Adds locale detection and translator to the context */ export const localeService = new Elysia({ name: "locale/service" }) - .model({ - localeSession: t.Cookie({ - locale: t.Optional(t.String()), - }), - }) - .derive({ as: "global" }, ({ cookie, request }) => { - // Get locale from cookie or detect from Accept-Language header - const cookieLocale = cookie.locale?.value; + .derive({ as: "global" }, ({ request }) => { + // Get locale from cookie (parsed from headers) or detect from Accept-Language header + const cookieHeader = request.headers.get("cookie") ?? ""; const acceptLanguage = request.headers.get("accept-language") ?? undefined; + // Parse locale from cookie string + let cookieLocale: string | undefined; + const localeMatch = cookieHeader.match(/locale=([^;]+)/); + if (localeMatch) { + cookieLocale = localeMatch[1]; + } + let locale: SupportedLocale; - if (cookieLocale) { + if (cookieLocale && typeof cookieLocale === "string") { locale = getLocale(cookieLocale); } else { locale = detectLocale(acceptLanguage); } - const t = createTranslator(locale); + const translator = createTranslator(locale); return { locale, - t, + t: translator, }; }); diff --git a/src/locales/en.json b/src/locales/en.json index 884422a..62dcd82 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,6 +1,6 @@ { "common": { - "appName": "ConvertX", + "appName": "ConvertX-CN", "poweredBy": "Powered by", "version": "v{version}", "loading": "Loading...", @@ -84,7 +84,7 @@ "noCookies": "No auth cookie, perhaps your browser is blocking cookies." }, "setup": { - "welcome": "Welcome to ConvertX!", + "welcome": "Welcome to ConvertX-CN!", "createYourAccount": "Create your account", "reportIssues": "Report any issues on", "github": "GitHub" diff --git a/src/locales/ja.json b/src/locales/ja.json index 74530f6..cc6ce69 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1,6 +1,6 @@ { "common": { - "appName": "ConvertX", + "appName": "ConvertX-CN", "poweredBy": "Powered by", "version": "v{version}", "loading": "読み込み中...", @@ -84,7 +84,7 @@ "noCookies": "認証Cookieが見つかりません。ブラウザがCookieをブロックしている可能性があります。" }, "setup": { - "welcome": "ConvertXへようこそ!", + "welcome": "ConvertX-CNへようこそ!", "createYourAccount": "アカウントを作成", "reportIssues": "問題があれば報告", "github": "GitHub" diff --git a/src/locales/ko.json b/src/locales/ko.json index 6af0b9c..042f1f8 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1,6 +1,6 @@ { "common": { - "appName": "ConvertX", + "appName": "ConvertX-CN", "poweredBy": "Powered by", "version": "v{version}", "loading": "로딩 중...", @@ -84,7 +84,7 @@ "noCookies": "인증 쿠키를 찾을 수 없습니다. 브라우저에서 쿠키를 차단하고 있을 수 있습니다." }, "setup": { - "welcome": "ConvertX에 오신 것을 환영합니다!", + "welcome": "ConvertX-CN에 오신 것을 환영합니다!", "createYourAccount": "계정 생성", "reportIssues": "문제가 있으시면 보고해 주세요", "github": "GitHub" diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 086d9cc..cd2d7f6 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -1,6 +1,6 @@ { "common": { - "appName": "ConvertX", + "appName": "ConvertX-CN", "poweredBy": "由", "version": "v{version}", "loading": "加载中...", @@ -84,7 +84,7 @@ "noCookies": "无法获取验证 Cookie,您的浏览器可能阻止了 Cookie。" }, "setup": { - "welcome": "欢迎使用 ConvertX!", + "welcome": "欢迎使用 ConvertX-CN!", "createYourAccount": "创建您的账户", "reportIssues": "在以下网址报告问题", "github": "GitHub" diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 401e157..6dceb85 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -1,6 +1,6 @@ { "common": { - "appName": "ConvertX", + "appName": "ConvertX-CN", "poweredBy": "由", "version": "v{version}", "loading": "載入中...", @@ -84,7 +84,7 @@ "noCookies": "無法取得驗證 Cookie,您的瀏覽器可能阻擋了 Cookie。" }, "setup": { - "welcome": "歡迎使用 ConvertX!", + "welcome": "歡迎使用 ConvertX-CN!", "createYourAccount": "建立您的帳戶", "reportIssues": "在以下網址回報問題", "github": "GitHub" diff --git a/src/pages/history.tsx b/src/pages/history.tsx index 8ac49bd..61fe03f 100644 --- a/src/pages/history.tsx +++ b/src/pages/history.tsx @@ -33,7 +33,7 @@ export const history = new Elysia().use(userService).use(localeService).get( userJobs = userJobs.filter((job) => job.num_files > 0); return ( - + <>
{ return ( - + <>
+ <>
+
{t("setup", "github")} @@ -134,7 +134,7 @@ export const user = new Elysia() } return ( - + <>
+ <>
+ <>