Search K
คู่มือการแปล
SnapOtter มาพร้อมกับ 21 ภาษาตั้งแต่ต้น ระบบ i18n ใช้รันไทม์แบบกำหนดเองที่มีขนาดเบา พร้อมการบังคับความสมบูรณ์ของ locale ด้วย TypeScript และการแยกโค้ดแบบไดนามิก
ภาษาที่รองรับ
| Code | Language | Native Name | Direction |
|---|---|---|---|
en | English | English | LTR |
zh-CN | Chinese (Simplified) | 简体中文 | LTR |
zh-TW | Chinese (Traditional) | 繁體中文 | LTR |
ja | Japanese | 日本語 | LTR |
ko | Korean | 한국어 | LTR |
es | Spanish | Español | LTR |
fr | French | Français | LTR |
it | Italian | Italiano | LTR |
pt-BR | Portuguese (Brazil) | Português (Brasil) | LTR |
de | German | Deutsch | LTR |
nl | Dutch | Nederlands | LTR |
sv | Swedish | Svenska | LTR |
ru | Russian | Русский | LTR |
pl | Polish | Polski | LTR |
uk | Ukrainian | Українська | LTR |
ar | Arabic | العربية | RTL |
tr | Turkish | Türkçe | LTR |
hi | Hindi | हिन्दी | LTR |
vi | Vietnamese | Tiếng Việt | LTR |
id | Indonesian | Bahasa Indonesia | LTR |
th | Thai | ไทย | LTR |
การตรวจจับภาษาทำงานอย่างไร
SnapOtter ใช้ลำดับการแก้ไขแบบสามชั้น:
- การตั้งค่าของผู้ใช้ - จัดเก็บใน
localStorage("snapotter-locale")และซิงค์ไปยังการตั้งค่าผู้ใช้เมื่อเข้าสู่ระบบแล้ว - การตรวจจับอัตโนมัติของเบราว์เซอร์ - ไล่ไปตามอาร์เรย์
navigator.languagesด้วยการจับคู่คำนำหน้าแบบ BCP 47 - ค่าเริ่มต้นของอินสแตนซ์ - ตัวแปรสภาพแวดล้อม
DEFAULT_LOCALEของผู้ดูแลระบบ (ดึงมาจากGET /api/v1/config/locale) - การถอยกลับเป็นภาษาอังกฤษ - พร้อมใช้งานเสมอ
ผู้ใช้สามารถเปลี่ยนภาษาได้จาก:
- ตัวเลือกลูกโลกที่ส่วนท้าย (เดสก์ท็อป มองเห็นได้เสมอ)
- ตัวเลือกภาษาที่ หน้าเข้าสู่ระบบ (ก่อนการยืนยันตัวตน)
- ส่วน Settings > General (การตั้งค่าเฉพาะผู้ใช้)
- เมนูดรอปดาวน์ภาษาใน แถบข้างบนมือถือ
- ส่วน Settings > System ตั้งค่าเริ่มต้นทั่วทั้งอินสแตนซ์ (เฉพาะผู้ดูแลระบบ)
การแปลทำงานอย่างไร
สตริง UI ทั้งหมดอยู่ใน packages/shared/src/i18n/ ไฟล์อ้างอิงคือ en.ts ซึ่งส่งออกอ็อบเจกต์ที่มีการกำหนดชนิดข้อมูล พร้อมทุกสตริงที่แอปใช้ (~1500 คีย์) ภาษาอื่นๆ เป็นไฟล์แยกต่างหาก (เช่น de.ts, fr.ts) ที่ส่งออกในรูปแบบเดียวกัน
ชนิด TranslationKeys ใช้ DeepStringRecord เพื่อยอมรับค่าสตริงใดๆ ในขณะที่บังคับโครงสร้างคีย์ TypeScript จะตรวจจับคีย์ที่ขาดหายไปในไฟล์แปลใดๆ ณ เวลาคอมไพล์
จะโหลดเฉพาะ locale ที่ใช้งานอยู่เท่านั้น ณ รันไทม์ผ่าน import() แบบไดนามิก เพื่อให้บันเดิลหลักมีขนาดเล็ก
การใช้คำแปลในคอมโพเนนต์
tsx
import { useTranslation } from "@/contexts/i18n-context";
import { format, plural } from "@/lib/format";
function MyComponent() {
const { t, locale, setLocale } = useTranslation();
return (
<div>
<h1>{t.common.settings}</h1>
<p>{format(t.settings.people.deleteConfirm, { username: "admin" })}</p>
<p>{plural(count, t.automate.fileCount, t.automate.fileCountPlural)}</p>
</div>
);
}การมีส่วนร่วมในการแปล
เรายินดีรับ PR การแปลโดยตรง คุณสามารถปรับปรุง locale ที่มีอยู่หรือเพิ่ม locale ใหม่ได้
หากต้องการรายงานคำแปลที่ผิดโดยไม่ส่งโค้ด ให้เปิด GitHub Issue พร้อมระบุภาษา สตริงที่ไม่ถูกต้อง และการแก้ไขที่แนะนำ
TIP
PR การแปลไม่จำเป็นต้องได้รับการอนุมัติล่วงหน้า ให้ fork repo ทำการเปลี่ยนแปลงของคุณ แล้วเปิด PR ดู Contributing Guide สำหรับกระบวนการ PR ทั้งหมดและข้อกำหนด CLA
วิธีสร้างหรืออัปเดตคำแปล
1. Fork และ clone
bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install2. คัดลอกไฟล์อ้างอิง (สำหรับภาษาใหม่เท่านั้น)
ข้ามขั้นตอนนี้หากคุณกำลังปรับปรุงคำแปลที่มีอยู่
bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts3. แปลสตริง
เปิดไฟล์ใหม่ของคุณและแปลค่าสตริงทุกค่า คงโครงสร้างอ็อบเจกต์และคีย์ให้เหมือนกันทุกประการ
ts
import type { TranslationKeys } from "./en.js";
export const xx: TranslationKeys = {
common: {
upload: "Your translation here",
// ... translate all entries
},
// ... translate all sections
} as const;กฎ:
- อย่าแปลคีย์ของอ็อบเจกต์ แปลเฉพาะค่าสตริงเท่านั้น
- คง
as constไว้ที่ท้าย - นำเข้า
TranslationKeysจาก./en.jsและกำหนดชนิดข้อมูลให้กับ export ของคุณ - คงตัวแทน
{variable}ไว้ตามเดิมทุกประการ - อาร์เรย์ (
rotatingPhrases,progressMessages) ต้องมีจำนวนรายการเท่ากัน - อย่าแปล: SnapOtter, JPEG, PNG, WebP, EXIF, API และคำศัพท์ทางเทคนิคอื่นๆ
4. ลงทะเบียน locale (สำหรับภาษาใหม่เท่านั้น)
เพิ่ม locale ของคุณลงใน SUPPORTED_LOCALES ใน packages/shared/src/i18n/index.ts:
ts
{ code: "xx", name: "Language Name", nativeName: "Native Name", dir: "ltr" },5. ตรวจสอบ
bash
pnpm typecheck # catches missing or mistyped keys
pnpm lint # formatting check
pnpm dev # manually verify strings appear correctly6. ส่ง
เปิด PR ต่อ main ด้วยชื่อเรื่องอย่างเช่น feat(i18n): add Swedish translation หรือ fix(i18n): correct German typos บอต CLA จะขอให้คุณลงนามในการมีส่วนร่วมครั้งแรกของคุณ
การเพิ่มคีย์คำแปลใหม่
เมื่อเพิ่มฟีเจอร์ใหม่ที่ต้องการสตริง UI ใหม่:
- เพิ่มคีย์ใหม่ลงใน
en.tsก่อน (ไฟล์อ้างอิง) - รัน
pnpm typecheck- ไฟล์ locale ทุกไฟล์จะล้มเหลวหากขาดคีย์ใหม่ - เพิ่มคีย์ใหม่ลงในไฟล์ locale ทั้งหมด (ใช้ภาษาอังกฤษเป็นค่าถอยกลับชั่วคราว)
การกำหนดค่า
ตั้งค่าภาษาเริ่มต้นของอินสแตนซ์ผ่านตัวแปรสภาพแวดล้อม:
yaml
DEFAULT_LOCALE: "de" # German as the default for all new usersการอ้างอิงไฟล์
| File | Purpose |
|---|---|
packages/shared/src/i18n/en.ts | สตริงภาษาอังกฤษ (locale อ้างอิง, ~1500 คีย์) |
packages/shared/src/i18n/index.ts | SUPPORTED_LOCALES, loadTranslations(), การส่งออกชนิดข้อมูล |
packages/shared/src/i18n/<locale>.ts | ไฟล์แปลแยกตามภาษา |
apps/web/src/contexts/i18n-context.tsx | I18nProvider, ฮุก useTranslation() |
apps/web/src/lib/format.ts | ตัวช่วย format(), plural(), formatFileSize() |
apps/api/src/routes/config.ts | จุดปลายทางสาธารณะ GET /api/v1/config/locale |
การแปลเว็บไซต์ เอกสาร และการอ้างอิง API
การรองรับ 21 ภาษาข้างต้นครอบคลุม แอป เว็บไซต์สาธารณะ (snapotter.com) เว็บไซต์เอกสารนี้ และการอ้างอิง REST API ก็ได้รับการ แปลเป็นทั้ง 21 ภาษาเช่นกัน โดยไปป์ไลน์ที่กำกับด้วยแฮชแยกต่างหากซึ่งนำ ชื่อและคำอธิบายเครื่องมือชุดเดียวกันจาก packages/shared/src/i18n มาใช้ซ้ำ ดังนั้น คำศัพท์จึงสอดคล้องกันทุกที่
แปลด้วยเครื่องเป็นค่าเริ่มต้น
ทุกหน้าที่ไม่ใช่ภาษาอังกฤษบนเว็บไซต์และเอกสารจะได้รับการ แปลด้วยเครื่อง ใน รอบแรก (โดยเซสชัน Claude Code ไม่ใช่บริการภายนอก) และมี แบนเนอร์เล็กๆ ที่ปิดได้แจ้งเรื่องนี้ พร้อมลิงก์กลับมาที่นี่ นั่นเป็นความตั้งใจ: มันจัดส่งทั้ง 21 ภาษาอย่างรวดเร็วและตรงไปตรงมา จากนั้นเชิญชวนชุมชนให้ ปรับปรุงหน้าที่สำคัญที่สุด การแปลด้วยเครื่องสื่อความหมายได้ครบถ้วน การตรวจทานโดยมนุษย์ทำให้อ่านได้เป็นธรรมชาติ
ไปป์ไลน์ตัดสินใจว่าจะแปลอะไรอย่างไร
แต่ละหน่วยของต้นฉบับภาษาอังกฤษที่แปลได้จะถูกแฮช และแฮชจะถูกจัดเก็บ ไว้ข้างคำแปลของมัน ในการรันแต่ละครั้ง ไปป์ไลน์จะ:
- แปลหน่วยใดๆ ที่ยังไม่มีคำแปล
- ข้ามหน่วยใดๆ ที่แฮชที่จัดเก็บไว้ยังตรงกับต้นฉบับภาษาอังกฤษ
- แปลใหม่สำหรับหน่วยที่เป็น machine เมื่อต้นฉบับภาษาอังกฤษเปลี่ยนแปลง
- และตั้งค่าสถานะหน่วยที่ผ่านการปรับปรุงโดย human เป็น
stale(ต้องตรวจทาน) เมื่อต้นฉบับ ภาษาอังกฤษเปลี่ยนแปลง แทนที่จะเขียนทับงานของคุณ
การปรับปรุงคำแปลเว็บด้วย PR
คุณปรับปรุงคำแปลของเว็บไซต์ เอกสาร หรือการอ้างอิง API ด้วยวิธีเดียวกับที่คุณ ปรับปรุง locale ของแอป: โดยแก้ไขไฟล์ที่ถูกสร้างขึ้นและเปิด PR
- ค้นหาคำแปลที่ถูกสร้างขึ้นสำหรับภาษาของคุณ:
- สตริง UI ของเว็บไซต์:
apps/landing/src/i18n/<locale>.json - หน้าเอกสาร:
apps/docs/<locale>/**.md - การอ้างอิง API:
apps/api/src/openapi.<locale>.yaml
- สตริง UI ของเว็บไซต์:
- แก้ไขข้อความ คงโค้ด ลิงก์
{placeholders}และเครื่องหมาย⸤I18N…⸥ใดๆ ไว้ตามเดิมทุกประการ ตัวตรวจสอบของไปป์ไลน์จะปฏิเสธคำแปลที่ทำ เครื่องหมายเหล่านั้นหายไปหรือสลับลำดับ - เปิด PR การแก้ไขหน่วยจะพลิกที่มาของมันจาก
machineเป็นhumanดังนั้น ไปป์ไลน์จะ ไม่มีวันเขียนทับมัน ในการรันครั้งต่อไป หากต้นฉบับภาษาอังกฤษ เปลี่ยนแปลงในภายหลัง หน่วยของคุณจะถูกตั้งค่าสถานะเป็นstaleเพื่อตรวจทานแทนที่จะถูก แทนที่อย่างเงียบๆ
หากต้องการรายงานคำแปลที่ผิดโดยไม่ส่งโค้ด ให้เปิด GitHub Issue พร้อมระบุ URL ของหน้า ภาษา ข้อความที่ไม่ถูกต้อง และการแก้ไขที่คุณแนะนำ
TIP
ผู้ดูแลจะรันไปป์ไลน์การแปล คุณไม่จำเป็นต้องมี API key เพื่อ มีส่วนร่วม เพียงแก้ไขไฟล์ที่ถูกสร้างขึ้นและเปิด PR ดู scripts/i18n/README.md สำหรับวิธีการทำงานของไปป์ไลน์
