This page was machine-translated. Spotted a mistake?Help improve it.
Skip to content

번역 가이드

SnapOtter는 기본적으로 21개 언어를 제공합니다. i18n 시스템은 TypeScript로 로케일 완전성을 강제하고 동적 코드 분할을 지원하는 가볍고 자체 제작한 런타임을 사용합니다.

지원 언어

CodeLanguageNative NameDirection
en영어EnglishLTR
zh-CN중국어(간체)简体中文LTR
zh-TW중국어(번체)繁體中文LTR
ja일본어日本語LTR
ko한국어한국어LTR
es스페인어EspañolLTR
fr프랑스어FrançaisLTR
it이탈리아어ItalianoLTR
pt-BR포르투갈어(브라질)Português (Brasil)LTR
de독일어DeutschLTR
nl네덜란드어NederlandsLTR
sv스웨덴어SvenskaLTR
ru러시아어РусскийLTR
pl폴란드어PolskiLTR
uk우크라이나어УкраїнськаLTR
ar아랍어العربيةRTL
tr터키어TürkçeLTR
hi힌디어हिन्दीLTR
vi베트남어Tiếng ViệtLTR
id인도네시아어Bahasa IndonesiaLTR
th태국어ไทยLTR

언어 감지가 작동하는 방식

SnapOtter는 3단계 해석 순서를 사용합니다:

  1. 사용자 환경설정 - localStorage("snapotter-locale")에 저장되며, 인증된 경우 사용자 설정과 동기화됩니다
  2. 브라우저 자동 감지 - BCP 47 접두사 매칭으로 navigator.languages 배열을 순회합니다
  3. 인스턴스 기본값 - 관리자의 DEFAULT_LOCALE 환경 변수(GET /api/v1/config/locale에서 가져옴)
  4. 영어 폴백 - 항상 사용 가능

사용자는 다음에서 언어를 변경할 수 있습니다:

  • 푸터 지구본 선택기(데스크톱, 항상 표시)
  • 로그인 페이지 언어 선택기(인증 전)
  • 설정 > 일반 섹션(사용자별 환경설정)
  • 모바일 사이드바 언어 드롭다운
  • 설정 > 시스템 섹션에서 인스턴스 전체 기본값 설정(관리자 전용)

번역이 작동하는 방식

모든 UI 문자열은 packages/shared/src/i18n/에 있습니다. 참조 파일은 en.ts이며, 앱이 사용하는 모든 문자열(~1500개 키)을 담은 타입이 지정된 객체를 내보냅니다. 다른 언어는 동일한 형태를 내보내는 별도 파일(예: de.ts, fr.ts)입니다.

TranslationKeys 타입은 DeepStringRecord을 사용해 키 구조를 강제하면서 임의의 문자열 값을 허용합니다. TypeScript는 어떤 번역 파일에서든 누락된 키를 컴파일 타임에 잡아냅니다.

런타임에는 활성 로케일만 동적 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을 직접 환영합니다. 기존 로케일을 개선하거나 새 로케일을 추가할 수 있습니다.

코드를 제출하지 않고 오역을 신고하려면, 언어와 잘못된 문자열, 제안하는 수정안을 담아 GitHub Issue를 열어 주세요.

TIP

번역 PR은 사전 승인이 필요하지 않습니다. 저장소를 포크하고 변경한 뒤 PR을 여세요. 전체 PR 절차와 CLA 요구 사항은 기여 가이드를 참고하세요.

번역을 만들거나 업데이트하는 방법

1. 포크 및 클론

bash
git clone https://github.com/<your-username>/snapotter.git
cd snapotter
pnpm install

2. 참조 파일 복사(새 언어만)

기존 번역을 개선하는 경우 이 단계를 건너뛰세요.

bash
cp packages/shared/src/i18n/en.ts packages/shared/src/i18n/XX.ts

3. 문자열 번역

새 파일을 열고 모든 문자열 값을 번역하세요. 객체 구조와 키는 정확히 동일하게 유지하세요.

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를 끝에 유지하세요
  • ./en.js에서 TranslationKeys을 임포트하고 내보내기에 타입을 지정하세요
  • {variable} 플레이스홀더는 정확히 그대로 유지하세요
  • 배열(rotatingPhrases, progressMessages)은 항목 수가 동일해야 합니다
  • SnapOtter, JPEG, PNG, WebP, EXIF, API 및 기타 기술 용어는 번역하지 마세요

4. 로케일 등록(새 언어만)

packages/shared/src/i18n/index.tsSUPPORTED_LOCALES에 로케일을 추가하세요:

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 correctly

6. 제출

feat(i18n): add Swedish translation 또는 fix(i18n): correct German typos 같은 제목으로 main에 PR을 여세요. 첫 기여 시 CLA 봇이 서명을 요청합니다.

새 번역 키 추가하기

새 UI 문자열이 필요한 새 기능을 추가할 때:

  1. 먼저 en.ts(참조 파일)에 새 키를 추가하세요
  2. pnpm typecheck을 실행하세요 - 새 키가 누락된 모든 로케일 파일이 실패합니다
  3. 모든 로케일 파일에 새 키를 추가하세요(영어를 임시 폴백으로 사용)

구성

환경 변수로 인스턴스 기본 언어를 설정하세요:

yaml
DEFAULT_LOCALE: "de"  # German as the default for all new users

파일 참조

FilePurpose
packages/shared/src/i18n/en.ts영어 문자열(참조 로케일, ~1500개 키)
packages/shared/src/i18n/index.tsSUPPORTED_LOCALES, loadTranslations(), 타입 내보내기
packages/shared/src/i18n/<locale>.ts언어별 번역 파일
apps/web/src/contexts/i18n-context.tsxI18nProvider, useTranslation()
apps/web/src/lib/format.tsformat(), plural(), formatFileSize() 헬퍼
apps/api/src/routes/config.tsGET /api/v1/config/locale 공개 엔드포인트

웹사이트, 문서, API 레퍼런스 번역하기

위의 21개 언어 지원은 을 다룹니다. 공개 웹사이트(snapotter.com), 이 문서 사이트, REST API 레퍼런스도 별도의 해시 게이트 파이프라인으로 21개 언어 전체로 번역되며, 이 파이프라인은 packages/shared/src/i18n의 동일한 도구 이름과 설명을 재사용하므로 용어가 어디서나 일관되게 유지됩니다.

기본적으로 기계 번역됨

웹사이트와 문서의 모든 비영어 페이지는 첫 번역에서 기계 번역되며(제3자 서비스가 아니라 Claude Code 세션으로), 그 사실을 알리는 작고 닫을 수 있는 배너와 여기로 돌아오는 링크가 함께 표시됩니다. 이는 의도적입니다: 21개 언어를 빠르고 정직하게 제공한 다음, 가장 중요한 페이지를 다듬도록 커뮤니티를 초대합니다. 기계 번역은 의미를 전달하고, 사람의 검토는 자연스럽게 읽히도록 만듭니다.

파이프라인이 무엇을 번역할지 결정하는 방식

영어 원본의 번역 가능한 각 단위는 해시로 처리되고, 그 해시는 번역 옆에 저장됩니다. 실행할 때마다 파이프라인은:

  • 아직 번역이 없는 단위를 번역하고,
  • 저장된 해시가 여전히 영어 원본과 일치하는 단위는 건너뛰며,
  • 영어 원본이 변경되면 machine 단위를 다시 번역하고,
  • human이 다듬은 단위는 영어 원본이 변경되면 작업을 덮어쓰지 않고 stale(검토 필요)으로 표시합니다.

PR로 웹 번역 다듬기

웹사이트, 문서, API 레퍼런스 번역을 개선하는 방법은 앱 로케일을 개선하는 방법과 같습니다: 생성된 파일을 편집하고 PR을 엽니다.

  1. 해당 언어에 대해 생성된 번역을 찾으세요:
    • 웹사이트 UI 문자열: apps/landing/src/i18n/<locale>.json
    • 문서 페이지: apps/docs/<locale>/**.md
    • API 레퍼런스: apps/api/src/openapi.<locale>.yaml
  2. 텍스트를 편집하세요. 코드, 링크, {placeholders}, 그리고 모든 ⸤I18N…⸥ 마커는 정확히 그대로 유지하세요; 파이프라인의 검증기는 이를 누락하거나 순서를 바꾼 번역을 거부합니다.
  3. PR을 여세요. 단위를 편집하면 그 출처가 machine에서 human로 바뀌므로, 파이프라인은 이후 실행에서 절대 덮어쓰지 않습니다. 이후 영어 원본이 변경되면, 해당 단위는 조용히 교체되지 않고 검토를 위해 stale로 표시됩니다.

코드를 제출하지 않고 오역을 신고하려면, 페이지 URL과 언어, 잘못된 텍스트, 제안하는 수정안을 담아 GitHub Issue를 열어 주세요.

TIP

번역 파이프라인은 메인테이너가 실행하므로, 기여하는 데 API 키가 필요하지 않습니다. 생성된 파일을 편집하고 PR만 열면 됩니다. 파이프라인이 실행되는 방식은 scripts/i18n/README.md를 참고하세요.