Browse / github.com/storytold/craft-launcher / patch
Add Korean support
Add Korean support Add Korean language support for ArtCraft.
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- arksky1 (imported from pull request #714, open)
- Base
ba3b0345f0b14f5cb892dcb8b917ca212c124cf9- Size
- +862 −15 in 9 files
- Review
- pending
- Shared
- 2026-10-28
- Id
497e7bf597bbcd0dcee22098
How it went
No other operator has reported using it yet.
Diff
raw · ATTRIBUTION.md README.md crates/ui-egui/src/cjk_font.rs crates/ui-egui/src/details.rs crates/ui-egui/src/i18n/ko.tsv crates/ui-egui/src/i18n/lang.rs crates/ui-egui/src/i18n/tests.rs crates/ui-egui/src/views.rs crates/ui-egui/tests/korean.rs
@@ -51,3 +51,9 @@ licensed like those apps. Refresh them from the source path, never redraw them. |---|---|---|---|---| | `docs/brand/` (all files) | ArtCraft name, wordmark and mark | ArtCraft Team | getartcraft.com (via `craftrules/assets/brand/`) | Not open source; trademarks of the ArtCraft Team, [`docs/brand/LICENSE-brand.txt`](docs/brand/LICENSE-brand.txt) | | README app icons (linked, not copied) | Each Craft app's 64 px icon | ArtCraft Team and each app's contributors | `raw.githubusercontent.com/storytold/<app>/main/assets/app-icon/hicolor/64x64/apps/ai.storyteller.<app>.png` | MIT OR Apache-2.0 (each app's license) |++## Korean interface catalog++| Path | Title | Author | Source | License |+|---|---|---|---|---|+| `crates/ui-egui/src/i18n/ko.tsv` | Korean interface translation | ArtCraft Launcher contributors (AI-assisted with Codex) | Contributor-original translations of this repository's English UI and app descriptions | MIT OR Apache-2.0 |@@ -88,7 +88,7 @@ The web version shows the same catalog in the browser, with the latest download ## Interface languages The interface is in English, Brazilian Portuguese (Português (Brasil)), Czech (Čeština), French-(Français), German (Deutsch), Hungarian (Magyar), Japanese (日本語), Polish (Polski), Russian+(Français), German (Deutsch), Hungarian (Magyar), Japanese (日本語), Korean (한국어), Polish (Polski), Russian (Русский), Simplified Chinese (简体中文) or Spanish (Español). **Auto**, the default, follows the system or browser language; **Settings → Language** picks one and switches at once, and the choice is kept between runs. Czech locales (`cs-CZ`, `cs_CZ.UTF-8`…) select Czech, German ones (`de-DE`,@@ -40,6 +40,7 @@ fn script_name(script: Script) -> &'static str { match script { Script::Chinese => "Chinese", Script::Japanese => "Japanese",+ Script::Korean => "Korean", } } @@ -48,6 +49,7 @@ fn script_families(script: Script) -> &'static str { match script { Script::Chinese => "PingFang SC, Hiragino Sans GB, Heiti SC, Microsoft YaHei, SimSun, Noto Sans CJK SC and WenQuanYi", Script::Japanese => "Hiragino Sans, Yu Gothic, Meiryo, MS Gothic, Noto Sans CJK JP, Noto Sans JP, IPAGothic and TakaoGothic",+ Script::Korean => "Apple SD Gothic Neo, AppleGothic, Malgun Gothic, Gulim, Noto Sans CJK KR, Noto Sans KR and NanumGothic", } } @@ -123,6 +125,7 @@ fn candidates(script: Script) -> Vec<Candidate> { match script { Script::Chinese => chinese_candidates(), Script::Japanese => japanese_candidates(),+ Script::Korean => korean_candidates(), } } @@ -255,6 +258,54 @@ fn japanese_candidates() -> Vec<Candidate> { out } +/// Where each system keeps a Korean font, best first. No font is bundled.+fn korean_candidates() -> Vec<Candidate> {+ let mut out = Vec::new();+ let mut add = |path: PathBuf, family: &'static str| out.push(Candidate { path, family });+ #[cfg(target_os = "macos")]+ {+ add("/System/Library/Fonts/AppleSDGothicNeo.ttc".into(), "Apple SD Gothic Neo");+ for path in mac_font_assets("AppleSDGothicNeo.ttc") {+ add(path, "Apple SD Gothic Neo");+ }+ add("/System/Library/Fonts/Supplemental/AppleGothic.ttf".into(), "AppleGothic");+ }+ #[cfg(target_os = "windows")]+ {+ let root = std::env::var_os("SystemRoot").map_or_else(|| PathBuf::from(r"C:\Windows"), PathBuf::from);+ add(root.join("Fonts").join("malgun.ttf"), "Malgun Gothic");+ add(root.join("Fonts").join("gulim.ttc"), "Gulim");+ }+ #[cfg(all(unix, not(target_os = "macos")))]+ {+ const NOTO_DIRS: [&str; 9] = [+ "/usr/share/fonts/opentype/noto",+ "/usr/share/fonts/truetype/noto",+ "/usr/share/fonts/noto-cjk",+ "/usr/share/fonts/google-noto-cjk",+ "/usr/share/fonts/google-noto-sans-cjk-fonts",+ "/usr/share/fonts/google-noto-sans-cjk-vf-fonts",+ "/usr/share/fonts/noto",+ "/usr/local/share/fonts/noto",+ "/usr/local/share/fonts/noto-cjk",+ ];+ for file in ["NotoSansCJK-Regular.ttc", "NotoSansCJKkr-Regular.otf", "NotoSansCJK-VF.ttc"] {+ for dir in NOTO_DIRS {+ add(Path::new(dir).join(file), "Noto Sans CJK KR");+ }+ }+ for file in ["NotoSansKR-Regular.otf", "NotoSansKR-VF.ttf"] {+ for dir in NOTO_DIRS {+ add(Path::new(dir).join(file), "Noto Sans KR");+ }+ }+ for dir in ["/usr/share/fonts/truetype/nanum", "/usr/share/fonts/nanum", "/usr/local/share/fonts/nanum"] {+ add(Path::new(dir).join("NanumGothic.ttf"), "NanumGothic");+ }+ }+ out+}+ /// `file` in macOS's downloaded font assets: /// `/System/Library/AssetsV2/com_apple_MobileAsset_Font*/*.asset/AssetData/<file>`. #[cfg(target_os = "macos")]@@ -12,6 +12,7 @@ use artcraft_launcher_core::protocol::Command; use egui::{Color32, CornerRadius, Id, Margin, Order, Rect, RichText, Sense, Stroke, Ui, emath::easing, pos2, vec2}; use crate::app::LauncherUi;+use crate::i18n::{current, tr_ctx}; use crate::model::AppEntry; use crate::theme::{self, Tokens, rgb, tint}; use crate::views::date_text;@@ -159,7 +160,7 @@ impl LauncherUi { ui.set_width(left_w); ui.spacing_mut().item_spacing.y = 8.0; for p in details.paragraphs() {- ui.add(egui::Label::new(RichText::new(p).size(14.5).color(t.ink)).wrap());+ ui.add(egui::Label::new(RichText::new(tr_ctx(current(), details.id, p)).size(14.5).color(t.ink)).wrap()); } ui.add_space(14.0); features(ui, details, entry.app, &t);@@ -183,7 +184,7 @@ impl LauncherUi { self.gallery(ui, details, &t, app_color); ui.add_space(8.0); for p in details.paragraphs() {- ui.add(egui::Label::new(RichText::new(p).size(14.5).color(t.ink)).wrap());+ ui.add(egui::Label::new(RichText::new(tr_ctx(current(), details.id, p)).size(14.5).color(t.ink)).wrap()); } }); ui.vertical(|ui| {@@ -201,7 +202,7 @@ impl LauncherUi { self.gallery(ui, details, &t, app_color); ui.add_space(8.0); for p in details.paragraphs() {- ui.add(egui::Label::new(RichText::new(p).size(14.5).color(t.ink)).wrap());+ ui.add(egui::Label::new(RichText::new(tr_ctx(current(), details.id, p)).size(14.5).color(t.ink)).wrap()); } ui.add_space(14.0); features(ui, details, entry.app, &t);@@ -316,7 +317,7 @@ impl LauncherUi { ui.set_width(text_w); ui.spacing_mut().item_spacing.y = 5.0; widgets::wordmark(ui, app, theme::PAGE_TITLE);- ui.add(egui::Label::new(RichText::new(details.headline).size(15.0).color(t.ink)).wrap());+ ui.add(egui::Label::new(RichText::new(tr_ctx(current(), details.id, details.headline)).size(15.0).color(t.ink)).wrap()); // "Version 0.5.0 · Oct 8, 2026", or the installed-versus-latest line. let version = crate::views::version_line(entry, &status); if !version.trim().is_empty() {@@ -361,7 +362,7 @@ impl LauncherUi { (tl!("Version"), Some(r.version.as_ref().map_or_else(|| r.tag.clone(), |v| v.to_string()))), (tl!("Released"), r.published_at.map(date_text)), (tl!("Download"), size),- (tl!("Platforms"), Some(details.platforms.to_string())),+ (tl!("Platforms"), Some(tr_ctx(current(), details.id, details.platforms).to_string())), ] .into_iter() .filter_map(|(label, value)| value.map(|v| (label, v)))@@ -406,7 +407,11 @@ impl LauncherUi { None => placeholder(ui, pic, Id::new("lightbox-placeholder"), failed.as_deref(), t, true), } ui.painter().rect_stroke(pic, CornerRadius::ZERO, Stroke::new(1.0, theme::backdrop_ink(40)), egui::StrokeKind::Outside);- let caption = if count > 1 { format!("{} · {}/{count}", shot.caption, at + 1) } else { shot.caption.to_string() };+ let caption = if count > 1 {+ format!("{} · {}/{count}", tr_ctx(current(), details.id, shot.caption), at + 1)+ } else {+ tr_ctx(current(), details.id, shot.caption).to_string()+ }; let ink = theme::backdrop_ink(190); let galley = widgets::hud_galley(ui, &caption, ink); let caption_at = pos2(rect.center().x - galley.size().x / 2.0, pic.bottom() + 14.0);@@ -461,7 +466,7 @@ impl LauncherUi { ui.painter().rect_stroke(pic, CornerRadius::ZERO, t.hairline(), egui::StrokeKind::Inside); let count = details.shots.len(); if count < 2 {- widgets::hud(ui, current.caption, t.faint);+ widgets::hud(ui, tr_ctx(current(), details.id, current.caption), t.faint); return; } // Previous and next, over the sides of the picture.@@ -474,7 +479,7 @@ impl LauncherUi { if arrow_button(ui, next, Id::new("shot-next"), false, tl!("Next picture (→)"), t) { self.flip_shot(count, true); }- widgets::hud(ui, &format!("{} · {}/{count}", current.caption, self.detail_shot.min(count - 1) + 1), t.faint);+ widgets::hud(ui, &format!("{} · {}/{count}", tr_ctx(current(), details.id, current.caption), self.detail_shot.min(count - 1) + 1), t.faint); let gap = 8.0; let thumb_w = ((w - gap * (count as f32 - 1.0)) / count as f32).floor().max(1.0); ui.horizontal(|ui| {@@ -499,7 +504,7 @@ impl LauncherUi { } let stroke = if selected { Stroke::new(2.0, app_color) } else { t.hairline() }; ui.painter().rect_stroke(resp.rect, CornerRadius::ZERO, stroke, egui::StrokeKind::Inside);- if resp.on_hover_cursor(egui::CursorIcon::PointingHand).on_hover_text(shot.caption).clicked() {+ if resp.on_hover_cursor(egui::CursorIcon::PointingHand).on_hover_text(tr_ctx(current(), details.id, shot.caption)).clicked() { self.detail_shot = i; } }@@ -561,9 +566,9 @@ fn features(ui: &mut Ui, details: &AppDetails, app: &CraftApp, t: &Tokens) { ui.spacing_mut().item_spacing.y = 3.0; ui.horizontal(|ui| { widgets::hud(ui, &format!("{:02}", i + 1), t.app_ink(app));- ui.label(RichText::new(f.title).size(15.0).family(theme::semibold()).color(t.ink_strong));+ ui.label(RichText::new(tr_ctx(current(), details.id, f.title)).size(15.0).family(theme::semibold()).color(t.ink_strong)); });- ui.add(egui::Label::new(RichText::new(f.text).size(13.5).color(t.muted)).wrap());+ ui.add(egui::Label::new(RichText::new(tr_ctx(current(), details.id, f.text)).size(13.5).color(t.muted)).wrap()); }); } }new file mode 100644@@ -0,0 +1,547 @@+ Launcher 런처+ Craft App Suite Craft 앱 모음+ Open Source Tools for Creatives, Makers, and Builders 창작자와 제작자를 위한 오픈 소스 도구+ Settings 설정+ Check for updates 업데이트 확인+ Grid 격자+ Table 표+ Show apps as cards 앱을 카드로 표시+ Show apps as a table 앱을 표로 표시+ All 전체+ Updates 업데이트+ Not installed 미설치+ Search apps 앱 검색+ Sort 정렬+ Suite order 기본 앱 순서+ Name 이름+ Inspired by 참고한 앱+ Status 상태+ Installed version 설치된 버전+ Latest version 최신 버전+ Newest release 최근 출시순+ Discord Discord+ GitHub GitHub+ Website 웹사이트+ Classic apps 기존 앱+ New apps 새 앱+ NEW 신규+ Install 설치+ Update to {v} {v} 버전으로 업데이트+ Open 열기+ Download for {os} {os}용 다운로드+ Download 다운로드+ Coming soon 출시 예정+ Checking… 확인 중…+ Not available 사용 불가+ Offline 오프라인+ Downloading {done} of {total} {total} 중 {done} 다운로드 중+ Downloading… 다운로드 중…+ Reconnecting… 다시 연결 중…+ Verifying… 검증 중…+ Installing, then restarting… 설치 후 다시 시작하는 중…+ Installing… 설치 중…+ Removing… 제거 중…+ Pause download 다운로드 일시정지+ Cancel download 다운로드 취소+ Resume download 다운로드 재개+ Discard download 다운로드 파일 삭제+ Release notes 출시 정보+ Show in Finder Finder에서 보기+ Show in Explorer 탐색기에서 보기+ Show in Files 파일 앱에서 보기+ Reinstall 다시 설치+ Uninstall… 제거…+ All releases 모든 버전+ ArtCraft Launcher {version} is available. ArtCraft Launcher {version} 버전을 사용할 수 있습니다.+ Dismiss 닫기+ Update and restart 업데이트 후 다시 시작+ A new version is available. Would you like to relaunch with the updated version? 새 버전이 있습니다. 업데이트된 버전으로 다시 시작할까요?+ A new version is available. Download it to replace this version? 새 버전이 있습니다. 다운로드하여 현재 버전을 교체할까요?+ Later 나중에+ What's new 새로운 기능+ No notes were published with this release. 이 버전에는 출시 정보가 없습니다.+ Released {date} 출시일: {date}+ Open on GitHub GitHub에서 열기+ Close 닫기+ ArtCraft Launcher {version} ArtCraft Launcher {version}+ Only when I ask 직접 확인할 때만+ Automatically download and install updates 업데이트 자동 다운로드 및 설치+ Notify me when an installed app has an update 설치된 앱의 업데이트 알림 받기+ Include pre-releases (betas and release candidates) 미리 보기 버전 포함(베타 및 출시 후보)+ Installing on Linux Linux 설치+ Installing on Windows Windows 설치+ Install new apps as 새 앱 설치 형식+ System package (recommended) 시스템 패키지(권장)+ Updates always use the format an app was installed with. 업데이트는 앱을 설치할 때 사용한 형식으로 진행합니다.+ Running in the background 백그라운드 실행+ Keep running in the tray when the window is closed 창을 닫아도 트레이에서 계속 실행+ Show the window on the current desktop 현재 데스크톱에 창 표시+ Start ArtCraft Launcher when I log in 로그인할 때 ArtCraft Launcher 시작+ Community 커뮤니티+ Questions, ideas and news from the ArtCraft team ArtCraft 팀에 전하는 질문과 아이디어, 새로운 소식+ Appearance 화면 모양+ Quit ArtCraft Launcher ArtCraft Launcher 종료+ Done 완료+ Uninstall {name}? {name} 앱을 제거할까요?+ The app will be moved to the Trash. Your documents and settings stay. 앱을 휴지통으로 이동합니다. 문서와 설정은 유지됩니다.+ Windows will remove it like any installed program. Your documents and settings stay. Windows에서 다른 설치 프로그램과 같은 방식으로 제거합니다. 문서와 설정은 유지됩니다.+ Your system's package manager will remove it, and may ask for your password. Your documents and settings stay. 시스템 패키지 관리자가 제거하며, 암호를 요청할 수 있습니다. 문서와 설정은 유지됩니다.+ The app will be removed. Your documents and settings stay. 앱을 제거합니다. 문서와 설정은 유지됩니다.+ Uninstall 제거+ Cancel 취소+ Checked {ago} {ago} 확인+ just now 방금+ yesterday 어제+ {month} {day}, {year} {year}년 {month} {day}일+ Automatically 자동+ Every Craft app 모든 Craft 앱+ Apps on this computer 이 컴퓨터에 설치된 앱+ Installed apps with a newer version 새 버전이 있는 설치된 앱+ Apps you can install 설치할 수 있는 앱+ Hide App 앱 숨기기+ Unhide App 앱 숨기기 해제+ Hidden Apps 숨긴 앱+ Apps you hid 숨긴 앱 목록+ Checked {ago} · using {sources} {ago} 확인 · 출처: {sources}+ {a} and {b} {a} 및 {b}+ Join the Discord Discord 참여+ Learn more 자세히 알아보기+ Minimize 최소화+ Maximize 최대화+ Restore 이전 크기로+ New 새로 만들기+ OK 확인+ Open link 링크 열기+ Update 업데이트+ {name} icon {name} 아이콘+ Loading ArtCraft Launcher… ArtCraft Launcher 불러오는 중…+ Cancel update 업데이트 취소+ Stop waiting; the update can be started again later 대기를 중단합니다. 업데이트는 나중에 다시 시작할 수 있습니다.+ Download again 다시 다운로드+ Install cancelled 설치 취소됨+ Install cancelled. The download is kept for the next try. 설치가 취소되었습니다. 다음 시도를 위해 다운로드 파일을 보관합니다.+ Update cancelled 업데이트 취소됨+ Uninstall cancelled 제거 취소됨+ Retry install 설치 다시 시도+ Retry update to {version} {version} 버전 업데이트 다시 시도+ The download is still here. It's checked again, then installed. 다운로드 파일이 남아 있습니다. 다시 검증한 후 설치합니다.+ Waiting for another app… 다른 앱을 기다리는 중…+ Waiting for other installs… 다른 설치를 기다리는 중…+ Installer (.msi) 설치 프로그램(.msi)+ Portable (.zip) 포터블(.zip)+ Disk image (.dmg) 디스크 이미지(.dmg)+ Debian package (.deb) Debian 패키지(.deb)+ RPM package (.rpm) RPM 패키지(.rpm)+ Arch package (.pkg.tar.zst) Arch 패키지(.pkg.tar.zst)+ Flatpak bundle Flatpak 번들+ Tarball (.tar.gz) 압축 파일(.tar.gz)+photocraft Image editing 이미지 편집+vectorcraft Vector illustration 벡터 일러스트레이션+filmcraft Video editing, color and sound 영상 편집, 색보정 및 사운드+lightcraft Photo library and raw development 사진 라이브러리 및 RAW 현상+pdfcraft Reading, organizing and protecting PDFs PDF 읽기, 정리 및 보호+effectcraft Motion graphics and visual effects 모션 그래픽 및 시각 효과+designcraft Page layout and publishing 페이지 레이아웃 및 출판+wordcraft Writing and document design 글쓰기 및 문서 디자인+deckcraft Presentations and slide shows 프레젠테이션 및 슬라이드 쇼+gridcraft Spreadsheets and data 스프레드시트 및 데이터+cadcraft Computer-aided design and drafting 컴퓨터 지원 설계 및 제도+soundcraft Recording, editing and mixing audio 오디오 녹음, 편집 및 믹싱+figcraft Interface design and prototyping 인터페이스 디자인 및 프로토타이핑+month Jan 1월+month Feb 2월+month Mar 3월+month Apr 4월+month May 5월+month Jun 6월+month Jul 7월+month Aug 8월+month Sep 9월+month Oct 10월+month Nov 11월+month Dec 12월+ Not checked yet 아직 확인하지 않음+ GitHub's hourly limit was reached; full details return in {min} min GitHub의 시간당 요청 한도에 도달했습니다. {min}분 후 전체 정보를 다시 확인할 수 있습니다.+ GitHub limit hit; back in {min} min GitHub 요청 한도에 도달했습니다. {min}분 후 재개합니다.+ Installing for {platform} {platform}용 설치 중+ Downloads for {platform} {platform}용 다운로드+ No downloads for this system 이 시스템용 다운로드가 없습니다.+ Join the ArtCraft community on Discord Discord에서 ArtCraft 커뮤니티 참여+ ArtCraft on {name} {name}의 ArtCraft+ Couldn't open {name}: {e} {name}을(를) 열 수 없습니다: {e}+ Download cancelled 다운로드 취소됨+ Available to install 설치 가능+ Available 사용 가능+ Installed · up to date 설치됨 · 최신 버전+ Update available 업데이트 가능+ Installed 설치됨+ installed 설치됨+ Not available here 이 환경에서 사용 불가+ Updating… 업데이트 중…+ Update paused 업데이트 일시정지됨+ Download paused 다운로드 일시정지됨+ {version} installed → {latest} released 설치된 버전 {version} → 출시된 버전 {latest}+ Version {version} 버전 {version}+ {installed} installed · latest {latest} 설치된 버전 {installed} · 최신 버전 {latest}+ Installed · latest {latest} 설치됨 · 최신 버전 {latest}+ Version {version} installed 버전 {version} 설치됨+ Version {latest} 버전 {latest}+ No release published yet 아직 출시된 버전이 없습니다.+ ArtCraft Launcher ArtCraft Launcher+ App 앱+ Latest 최신+ Released 출시일+ Auto 자동+ Language 언어+ Ascending 오름차순+ Descending 내림차순+ Click to reverse the order 클릭하여 순서 뒤집기+ Click to sort by this column 클릭하여 이 열 기준으로 정렬+ Like {name} {name}과(와) 유사+ {done} of {total} {total} 중 {done}+ Downloading 다운로드 중+ Verifying checksum… 체크섬 검증 중…+ Resume 재개+ {done} of {total} downloaded {total} 중 {done} 다운로드됨+ {done} downloaded {done} 다운로드됨+ Checking for updates… 업데이트 확인 중…+ Downloads the new version, replaces this one and opens it again 새 버전을 다운로드하여 현재 버전을 교체한 후 다시 엽니다.+ Couldn't check for updates: {f} 업데이트를 확인할 수 없습니다: {f}+ Website (coming soon) 웹사이트(준비 중)+ GitHub repository GitHub 저장소+ ArtCraft ArtCraft+ Update 1 app 앱 1개 업데이트+ Update all ({count}) 모두 업데이트({count}개)+ ⊞ Grid ⊞ 격자+ ☰ Table ☰ 표+ 🔍 Search apps 🔍 앱 검색+ {name} {version} {name} {version}+ Match system 시스템 설정 따르기+ Dark 어둡게+ Light 밝게+ Full changelog 전체 변경 이력+ link 링크+ No apps match 검색 조건에 맞는 앱이 없습니다.+ Open ArtCraft Launcher to update. 업데이트하려면 ArtCraft Launcher를 여세요.+ {name} {v} is available {name} {v} 버전을 사용할 수 있습니다.+ {count} updates are available 업데이트 {count}개를 사용할 수 있습니다.+ These notes weren't fetched (GitHub's request limit was reached). They're on the release page. GitHub 요청 한도에 도달하여 출시 정보를 가져오지 못했습니다. 출시 페이지에서 확인할 수 있습니다.+ When on, opening the launcher from the menu bar brings its window to the desktop you're on. When off, macOS switches you to the desktop the window was on. 켜면 메뉴 막대에서 런처를 열 때 현재 데스크톱으로 창을 가져옵니다. 끄면 macOS가 창이 있던 데스크톱으로 이동합니다.+ Couldn't change the login item: {e} 로그인 항목을 변경할 수 없습니다: {e}+column App 앱+column Installed 설치된 버전+column Latest 최신 버전+column Released 출시일+filter Installed 설치됨+ App details 앱 상세 정보+ Close (Esc) 닫기(Esc)+ Features 기능+ Latest release 최신 출시 버전+ Version 버전+ Platforms 플랫폼+ Releases 출시 버전+ Enlarge picture 이미지 확대+ Click to enlarge 클릭하여 확대+ Previous picture (←) 이전 이미지(←)+ Next picture (→) 다음 이미지(→)+ Loading pictures… 이미지 불러오는 중…+ Pictures couldn't be loaded. Reopen this page to try again. 이미지를 불러올 수 없습니다. 이 페이지를 다시 열어 재시도하세요.+ {name} details {name} 상세 정보+@plural {n} app|{n} apps 앱 {n}개+@plural {n} min ago|{n} min ago {n}분 전+@plural {n} h ago|{n} h ago {n}시간 전+@plural {n} day ago|{n} days ago {n}일 전+@msg Couldn't open {name}: {e} {name}을(를) 열 수 없습니다: {e}+@msg Download cancelled 다운로드 취소됨+@msg {name} updated to {version} {name}이(가) {version} 버전으로 업데이트되었습니다.+@msg {name} installed: {version} {name} 설치됨: {version}+@msg {name} updated to {version} (this release publishes no checksum, so only its size was checked) {name}이(가) {version} 버전으로 업데이트되었습니다(이 버전은 체크섬이 공개되지 않아 크기만 확인했습니다).+@msg {name} installed: {version} (this release publishes no checksum, so only its size was checked) {name} 설치됨: {version}(이 버전은 체크섬이 공개되지 않아 크기만 확인했습니다).+@msg {name} was removed {name}이(가) 제거되었습니다.+@msg {name} is running. Quit it, then try again. {name}이(가) 실행 중입니다. 종료한 후 다시 시도하세요.+@msg {name} was installed from the {repo} repository or the AUR ({pkg}). Update it with {cmd} or sudo pacman -Syu {name}은(는) {repo} 저장소 또는 AUR({pkg})에서 설치되었습니다. {cmd} 또는 sudo pacman -Syu로 업데이트하세요.+@msg {name} was installed from the {repo} repository or the AUR ({pkg}). Update it with your AUR helper (for example: yay -S {pkg}) or sudo pacman -Syu {name}은(는) {repo} 저장소 또는 AUR({pkg})에서 설치되었습니다. AUR 도우미(예: yay -S {pkg}) 또는 sudo pacman -Syu로 업데이트하세요.+@msg {name} was installed from the AUR ({pkg}). Update it with {cmd} {name}은(는) AUR({pkg})에서 설치되었습니다. {cmd}로 업데이트하세요.+@msg {name} was installed from the AUR ({pkg}). Update it with your AUR helper (for example: yay -S {pkg}) {name}은(는) AUR({pkg})에서 설치되었습니다. AUR 도우미(예: yay -S {pkg})로 업데이트하세요.+@id app.install 설치+@id app.update {v} 버전으로 업데이트+@id app.open 열기+@id app.download {os}용 다운로드+@id app.reveal Finder에서 보기+@id app.reinstall 다시 설치+@id app.uninstall 제거…+@id app.notes 출시 정보+@id app.website 웹사이트+@id app.github GitHub 저장소+@id app.releases 모든 버전+@id launcher.update 업데이트 후 다시 시작+@id launcher.download 다운로드+@id launcher.notes 새로운 기능+@id launcher.dismiss 닫기+@id settings.open 설정+@id settings.quit ArtCraft Launcher 종료+@id view.grid 격자+@id view.table 표+@id filter.all 전체+@id filter.installed 설치됨+@id filter.updates 업데이트+@id filter.notinstalled 미설치+@id sort.suite 기본 앱 순서+@id sort.name 이름+@id sort.inspired 참고한 앱+@id sort.status 상태+@id sort.installed 설치된 버전+@id sort.latest 최신 버전+@id sort.released 최근 출시 버전+@id help.discord Discord+@id help.github GitHub+@id help.website 웹사이트+@id update.check 업데이트 확인+@id update.check_auto 업데이트 자동 확인+@id update.notify 설치된 앱의 업데이트 알림 받기+@id update.prereleases 미리 보기 버전 포함(베타 및 출시 후보)+@id linux.preferred_kind 새 앱 설치 형식+@id tray.close_to_tray 창을 닫아도 트레이에서 계속 실행+@id tray.show_on_current_desktop 현재 데스크톱에 창 표시+@id tray.login_item 로그인할 때 ArtCraft Launcher 시작+@id appearance.theme 화면 모양+ Show ArtCraft Launcher ArtCraft Launcher 표시+ Check for Updates 업데이트 확인+ Visit getartcraft.com getartcraft.com 방문+ ArtCraft on GitHub GitHub의 ArtCraft+ Join the ArtCraft Discord ArtCraft Discord 참여+@plural ArtCraft Launcher · {n} update available|ArtCraft Launcher · {n} updates available ArtCraft Launcher · 업데이트 {n}개 가능+ Install folder 설치 폴더+ Default install folder 기본 설치 폴더+ System default 시스템 기본값+ Browse… 찾아보기…+ Use default 기본값 사용+ New installs use this folder. Updates stay in their current location. 새 앱은 이 폴더에 설치합니다. 업데이트는 기존 설치 위치를 유지합니다.+ Custom folders use AppImage on Linux. Linux에서 사용자 지정 폴더에는 AppImage를 사용합니다.+ On Linux, custom folders use AppImage format. Linux에서 사용자 지정 폴더에 설치할 때는 AppImage 형식을 사용합니다.+ The system manages install locations here. 이 환경에서는 시스템이 설치 위치를 관리합니다.+@msg couldn't use install folder {path}: {reason} 설치 폴더 {path}을(를) 사용할 수 없습니다: {reason}+@msg this release has no AppImage for a custom install folder 이 버전에는 사용자 지정 설치 폴더용 AppImage가 없습니다.+@msg custom install folders aren't supported on this system 이 시스템은 사용자 지정 설치 폴더를 지원하지 않습니다.+@msg {path} already exists and wasn't installed by ArtCraft Launcher {path}이(가) 이미 존재하며 ArtCraft Launcher로 설치된 항목이 아닙니다.+@msg couldn't move {path} to Trash ({reason}). Remove it in Finder instead. {path}을(를) 휴지통으로 이동할 수 없습니다({reason}). Finder에서 직접 제거하세요.+ Apps are placed inside this folder. 앱이 이 폴더 안에 배치됩니다.+ Replace what's there? 기존 항목을 교체할까요?+ {path} already exists. Installing {name} will overwrite it. {path}이(가) 이미 존재합니다. {name}을(를) 설치하면 덮어씁니다.+ Replace 교체+ Recently updated 최근 업데이트순+ Name (A–Z) 이름 오름차순+ Name (Z–A) 이름 내림차순+ Frequently used 자주 사용한 순+ Next app 다음 앱+ Previous app 이전 앱+ Show {n} more changes 변경 사항 {n}개 더 보기+ Last updated: {date} 최근 업데이트: {date}+column Version 버전+ The image editor you already know how to use. 이미 익숙한 방식으로 사용하는 이미지 편집기.+ Layers, masks, adjustment layers, layer styles, type, vectors and brushes in a native app written in Rust. Open source, offline, and yours. 레이어, 마스크, 조정 레이어, 레이어 스타일, 문자, 벡터 및 브러시를 갖춘 Rust 네이티브 앱입니다. 오픈 소스이며 오프라인에서 자유롭게 사용할 수 있습니다.+ PhotoCraft keeps the menus, shortcuts and panels your hands already know, so there is nothing to relearn. PhotoCraft는 손에 익은 메뉴, 단축키, 패널을 그대로 유지하여 새로 배울 필요가 없습니다.+ Native GPU canvas 네이티브 GPU 캔버스+ No Electron and no web view: a fast native compositor. Electron이나 웹 뷰 없이 작동하는 빠른 네이티브 합성 엔진.+ Real layered files 레이어를 유지하는 파일+ Opens and saves layered PSD and PSB documents. 레이어가 있는 PSD 및 PSB 문서를 열고 저장합니다.+ Non-destructive editing 비파괴 편집+ Adjustment layers, live-preview filters, smart objects and layer styles. 조정 레이어, 실시간 미리 보기 필터, 고급 개체 및 레이어 스타일.+ On-device selection 기기 내 선택 기능+ Subject and object selection and mask refinement run locally. 피사체와 개체 선택 및 마스크 다듬기를 로컬에서 실행합니다.+ Serious color 정교한 색상 처리+ 8-, 16- and 32-bit RGB, CMYK, Lab and Grayscale, with ICC color management and soft proofing. 8·16·32비트 RGB, CMYK, Lab 및 회색조와 ICC 색상 관리 및 소프트 교정을 지원합니다.+ Curves and type layers 곡선 및 문자 레이어+ Levels with a live histogram 실시간 히스토그램이 있는 레벨+ Layer styles on live type 편집 가능한 문자에 적용한 레이어 스타일+ Selections and masked adjustments 선택 영역 및 마스크 조정+ Vector illustration, reimagined in pure Rust. 순수 Rust로 새롭게 구현한 벡터 일러스트레이션.+ A native vector illustration app with the pen, panels and shortcuts illustrators expect. Open source, and it also runs in a browser. 일러스트레이터에게 익숙한 펜, 패널 및 단축키를 갖춘 네이티브 벡터 일러스트레이션 앱입니다. 오픈 소스이며 브라우저에서도 실행됩니다.+ The workflow you know 익숙한 작업 방식+ Pen, direct selection, shape booleans, snapping guides and an appearance panel, laid out the way you expect. 펜, 직접 선택, 도형 불리언 연산, 스냅 안내선 및 모양 패널을 익숙한 구성으로 제공합니다.+ Genuinely fast 뛰어난 속도+ Drawings with thousands of shapes stay responsive on a retina display. 수천 개의 도형이 있는 그림도 레티나 디스플레이에서 빠르게 반응합니다.+ Booleans that don't fail 실패하지 않는 불리언 연산+ Shape booleans work on the exact curves, so there is no "cannot perform operation", and undo is unlimited. 도형 불리언 연산을 정확한 곡선에서 수행하여 "작업을 수행할 수 없음" 오류가 없으며 실행 취소도 무제한입니다.+ Live, editable effects 실시간으로 편집하는 효과+ Blends, envelope distort, gradient mesh, repeats, glows and drop shadows all stay editable. 블렌드, 외피 왜곡, 그레이디언트 메시, 반복, 광선 및 그림자를 계속 편집할 수 있습니다.+ Image tracing 이미지 추적+ Turn raster images into clean vector paths, with presets for the common cases. 일반적인 용도에 맞는 사전 설정으로 래스터 이미지를 깔끔한 벡터 패스로 변환합니다.+ Open formats 개방형 형식+ Native JSON documents, SVG and PDF import and export, and PNG, JPEG and WebP output. 기본 JSON 문서, SVG 및 PDF 가져오기·내보내기와 PNG, JPEG 및 WebP 출력을 지원합니다.+ The Neon Drive poster Neon Drive 포스터+ Live blends 실시간 블렌드+ A feature sheet and the Layers panel 기능 소개 시트와 레이어 패널+ Pathfinder and exact booleans 패스파인더 및 정확한 불리언 연산+ Professional video editing, rebuilt from scratch. 처음부터 새롭게 구현한 전문 비디오 편집.+ A native video editor written in pure Rust, with source and program monitors, a real timeline and every trim mode. 소스 및 프로그램 모니터, 정교한 타임라인과 모든 트림 모드를 갖춘 순수 Rust 네이티브 비디오 편집기입니다.+ A timeline you already know 이미 익숙한 타임라인+ Source and program monitors, three-point editing, every trim mode and J/K/L trimming. 소스 및 프로그램 모니터, 3점 편집, 모든 트림 모드 및 J/K/L 트리밍.+ Frame-exact time 프레임 단위로 정확한 시간+ An integer timebase keeps 23.976 and 29.97 drop-frame footage exact, with no drift. 정수 시간 기준으로 23.976 및 29.97 드롭 프레임 푸티지를 오차 누적 없이 정확하게 유지합니다.+ Its own codecs 자체 코덱+ No FFmpeg inside: native support for H.264, HEVC, ProRes, VP9, AAC and Opus. FFmpeg 없이 H.264, HEVC, ProRes, VP9, AAC 및 Opus를 네이티브로 지원합니다.+ Color grading 색상 그레이딩+ Curves, color wheels, HSL secondaries, LUTs and HDR and log workflows, with scopes to check the result. 곡선, 색상 휠, HSL 보조 색상, LUT, HDR 및 로그 작업 방식과 결과 확인용 스코프.+ Effects and keyframes 효과 및 키프레임+ Video effects and transitions, keyframes with value and velocity graphs, and a linear-light GPU compositor. 비디오 효과와 전환, 값 및 속도 그래프가 있는 키프레임과 선형 광원 GPU 합성 엔진.+ Broadcast-ready audio 방송용 오디오+ EBU R128 loudness meters, a mixer with automation and native audio effects. EBU R128 음량 측정기, 오토메이션 믹서 및 네이티브 오디오 효과.+ The Color workspace, with scopes 스코프가 있는 색상 작업 영역+ The Assembly workspace 어셈블리 작업 영역+ Keyframes in Effect Controls 효과 컨트롤의 키프레임+ Your photos. Your pixels. Your machine. 나의 사진. 나의 픽셀. 나의 컴퓨터.+ A fast photo library and non-destructive raw developer, written from scratch in pure Rust. No account, no cloud, no telemetry and no subscription. 처음부터 순수 Rust로 만든 빠른 사진 라이브러리 및 비파괴 RAW 현상 도구입니다. 계정, 클라우드, 사용 정보 수집 및 구독이 없습니다.+ Non-destructive by design 처음부터 비파괴 방식+ A wide-gamut, 32-bit float pipeline that never touches your originals. 원본을 건드리지 않는 광색역 32비트 부동소수점 처리 파이프라인.+ Every control you reach for 필요한 모든 조정 도구+ Light, color, effects, tone curve, a color mixer, color grading wheels and built-in presets. 빛, 색상, 효과, 톤 곡선, 색상 믹서, 색상 그레이딩 휠 및 기본 제공 사전 설정.+ Precise masking 정밀한 마스킹+ Brush, gradient and range masks, plus sky, subject and background masks. 브러시, 그레이디언트, 범위 마스크와 하늘, 피사체 및 배경 마스크.+ Native raw decoding 네이티브 RAW 디코딩+ Pure-Rust decoders for the main camera raw formats: DNG, CR2, ARW, NEF, RAF, RW2, PEF and ORF. 주요 카메라 RAW 형식인 DNG, CR2, ARW, NEF, RAF, RW2, PEF 및 ORF용 순수 Rust 디코더.+ A library that keeps up 빠르게 반응하는 라이브러리+ Albums, ratings, flags and search, with grids that stay smooth on large catalogs. 앨범, 평점, 플래그 및 검색을 제공하며 대규모 카탈로그에서도 격자가 부드럽게 작동합니다.+ Instant feedback 즉각적인 반영+ The GPU pipeline updates the preview as you move a slider, even on large raw files. 대용량 RAW 파일에서도 슬라이더를 움직이면 GPU 파이프라인이 미리 보기를 즉시 업데이트합니다.+ The Edit view 편집 보기+ The tone curve 톤 곡선+ Color grading wheels 색상 그레이딩 휠+ A radial mask over a sky mask 하늘 마스크 위의 방사형 마스크+ The open-source PDF workbench. 오픈 소스 PDF 작업 도구.+ Read, organize, combine, split and secure PDFs in a fast, native app written in Rust from the ground up. No account, no telemetry and no cloud. 처음부터 Rust로 만든 빠른 네이티브 앱에서 PDF를 읽고 구성하고 결합하고 분할하고 보호하세요. 계정, 사용 정보 수집 및 클라우드가 없습니다.+ Faithful rendering 원본에 충실한 렌더링+ World scripts, vertical Japanese, color emoji and transparency. 세계 각지의 문자, 일본어 세로쓰기, 컬러 이모지 및 투명도.+ Search as you type 입력하면서 검색+ Instant find, and text selection that follows the document's real reading order. 즉시 검색하며 문서의 실제 읽기 순서를 따르는 텍스트 선택.+ Organize like cards 카드처럼 구성+ Rotate, delete, insert and reorder pages, with deep undo behind every change. 페이지 회전, 삭제, 삽입 및 순서 변경을 제공하며 모든 변경을 여러 단계로 실행 취소할 수 있습니다.+ Combine, extract, split 결합, 추출, 분할+ Assemble and break apart documents while keeping links, form fields, layers and bookmarks intact. 링크, 양식 필드, 레이어 및 책갈피를 유지하면서 문서를 조합하거나 분리합니다.+ Fearless saves 안심하고 저장+ Incremental, atomic saves with autosave and crash recovery. 자동 저장 및 비정상 종료 복구를 갖춘 증분·원자적 저장.+ Built-in security 내장 보안+ Opens files from RC4 to AES-256 encryption and honors the author's permissions. RC4부터 AES-256까지 암호화된 파일을 열고 작성자가 설정한 권한을 준수합니다.+ Motion graphics and visual effects, in pure Rust. 순수 Rust로 구현한 모션 그래픽 및 시각 효과.+ A native compositor with compositions, layers, keyframes, effects, expressions, 3D cameras and lights, and a render queue. Young, moving fast, and already usable. 컴포지션, 레이어, 키프레임, 효과, 표현식, 3D 카메라와 조명 및 렌더링 대기열을 갖춘 네이티브 합성 앱입니다. 초기 단계지만 빠르게 발전하고 있으며 이미 사용할 수 있습니다.+ Animate the way you know 익숙한 방식의 애니메이션+ Familiar panels and keyframes (linear, bezier, hold and eased), with a graph editor for value and speed curves. 익숙한 패널과 키프레임(선형, 베지어, 고정 및 이즈), 값과 속도 곡선용 그래프 편집기.+ Layers of every kind 다양한 레이어+ Solids, shapes, text, footage, nested compositions, nulls and adjustment layers, with parenting, track mattes and blend modes. 단색, 모양, 텍스트, 푸티지, 중첩 컴포지션, 널 및 조정 레이어와 부모 연결, 트랙 매트 및 혼합 모드.+ A large effects library 풍부한 효과 라이브러리+ Blur, color correction, distortion, generators, keying, particles and simulation, stylize and time effects. 흐림, 색상 교정, 왜곡, 생성기, 키잉, 파티클과 시뮬레이션, 스타일화 및 시간 효과.+ Real 3D 정교한 3D+ 3D layers with cameras, depth of field, and lights that cast soft ray-traced shadows. 카메라, 피사계 심도 및 부드러운 광선 추적 그림자를 만드는 조명이 있는 3D 레이어.+ Expressions 표현식+ JavaScript expressions with wiggle, loops and layer references, edited inline. wiggle, 반복 및 레이어 참조를 제공하며 인라인으로 편집하는 JavaScript 표현식.+ Export anywhere 다양한 형식으로 내보내기+ H.264 and ProRes with alpha, PNG, TIFF and EXR sequences, GIF, and Lottie import and export. H.264, 알파를 포함한 ProRes, PNG·TIFF·EXR 시퀀스, GIF 및 Lottie 가져오기·내보내기.+ An animated composition 애니메이션 컴포지션+ The Graph Editor 그래프 편집기+ Particles and Effect Controls 파티클 및 효과 컨트롤+ 3D layers with cameras and lights 카메라와 조명이 있는 3D 레이어+ Page layout and publishing, rebuilt in pure Rust. 순수 Rust로 새롭게 구현한 페이지 레이아웃 및 출판.+ A native layout app with spreads, parent pages, threaded stories, styles, swatches, text wrap and a paragraph composer. No subscription, no license server and no telemetry. 스프레드, 상위 페이지, 연결된 스토리, 스타일, 색상 견본, 텍스트 감싸기 및 단락 컴포저를 갖춘 네이티브 레이아웃 앱입니다. 구독, 라이선스 서버 및 사용 정보 수집이 없습니다.+ The layout tools you know 익숙한 레이아웃 도구+ Spreads and parent pages, frames and threaded stories, with paragraph and character styles. 스프레드와 상위 페이지, 프레임과 연결된 스토리, 단락 및 문자 스타일.+ Beautiful type 아름다운 타이포그래피+ A Knuth–Plass paragraph composer, dictionary hyphenation, optical margin alignment and baseline grids, with line breaks that match on screen and in PDF. Knuth–Plass 단락 컴포저, 사전 기반 하이픈 넣기, 광학 여백 맞춤 및 기준선 격자를 제공하며 화면과 PDF의 줄 바꿈이 일치합니다.+ Print-ready PDF 인쇄용 PDF+ Real selectable text with embedded fonts, CMYK and spot colors, bleed, crop marks and PDF/A output. 포함된 글꼴을 사용하는 선택 가능한 텍스트, CMYK 및 별색, 도련, 자르기 표시 및 PDF/A 출력.+ Fast 빠른 속도+ Multithreaded rendering, instant undo and cached text composition. 멀티스레드 렌더링, 즉각적인 실행 취소 및 캐시된 텍스트 조판.+ A documented native format, PNG export and layout interchange import and export. 문서화된 기본 형식, PNG 내보내기 및 레이아웃 교환 형식 가져오기·내보내기.+ Built for automation 자동화를 위한 설계+ Every menu item, tool, panel and dialog can be driven over a JSON control channel and an MCP server. 모든 메뉴 항목, 도구, 패널 및 대화 상자를 JSON 제어 채널과 MCP 서버로 조작할 수 있습니다.+ A magazine spread with a threaded story 연결된 스토리가 있는 잡지 스프레드+ The familiar word processor, native and offline. 익숙한 워드 프로세서, 네이티브와 오프라인으로.+ A word processor with the ribbon, shortcuts and behavior you already know, written in Rust. Open source, and built from public specifications. 익숙한 리본, 단축키 및 동작을 갖춘 Rust 워드 프로세서입니다. 공개 사양을 바탕으로 만든 오픈 소스입니다.+ Familiar 익숙한 사용법+ Ribbon tabs and groups, a live Styles gallery and the editing behavior you expect, from list continuation to Format Painter. 리본 탭과 그룹, 실시간 스타일 갤러리와 목록 이어쓰기부터 서식 복사까지 익숙한 편집 동작.+ Your files 자유로운 파일 사용+ Opens and saves .docx, and also .odt, .rtf, .html, .md, .tex and .txt. Opens older .doc files and exports PDF with real, selectable text. .docx와 .odt, .rtf, .html, .md, .tex, .txt를 열고 저장합니다. 이전 .doc 파일을 열고 실제 선택 가능한 텍스트가 있는 PDF로 내보냅니다.+ Paragraph layout is cached, so typing stays responsive even in long documents. 단락 레이아웃을 캐시하여 긴 문서에서도 입력이 빠르게 반응합니다.+ Private 개인정보 보호+ Spelling, grammar and everything else work offline. 맞춤법, 문법 및 모든 기능이 오프라인으로 작동합니다.+ Everywhere 어디서나+ One Rust codebase for macOS, Windows, Linux, BSD and the web. 하나의 Rust 코드베이스로 macOS, Windows, Linux, BSD 및 웹을 지원합니다.+ Built for agents 에이전트를 위한 설계+ Every action is a command, driven from the ribbon, shortcuts, a command-line tool, a JSON control channel or an MCP server. 모든 작업이 명령이며 리본, 단축키, 명령줄 도구, JSON 제어 채널 또는 MCP 서버에서 실행할 수 있습니다.+ The Home tab and the Navigation pane 홈 탭과 탐색 창+ Track Changes and comments 변경 내용 추적 및 댓글+ The Design tab 디자인 탭+ The Layout tab 레이아웃 탭+ Presentations, rebuilt in Rust. Rust로 새롭게 구현한 프레젠테이션.+ A presentation app with slide masters, shapes, animations and a presenter view, written in Rust. In alpha, heading for beta. 슬라이드 마스터, 도형, 애니메이션 및 발표자 보기를 갖춘 Rust 프레젠테이션 앱입니다. 알파 단계에서 베타를 향해 개발 중입니다.+ Make decks 슬라이드 만들기+ Slide masters and layouts with placeholders that inherit formatting, themes, sections, notes and comments. 서식을 상속하는 자리 표시자가 있는 슬라이드 마스터 및 레이아웃, 테마, 구역, 메모 및 댓글.+ Draw 그리기+ Preset shapes with adjust handles, connectors that stay glued to shapes, freeform tools, merged shapes, gradients, shadows and glow. 조정 핸들이 있는 도형, 도형에 연결된 상태를 유지하는 연결선, 자유형 도구, 도형 병합, 그레이디언트, 그림자 및 광선.+ Write 텍스트 작성+ In-place text editing with full font and paragraph formatting, bullets and numbering, columns and autofit. 완전한 글꼴 및 단락 서식, 글머리 기호와 번호 매기기, 단 및 자동 맞춤을 갖춘 제자리 텍스트 편집.+ Add content 내용 추가+ Pictures with crop and corrections, styled tables, charts, audio and video. 자르기와 보정이 가능한 그림, 스타일이 있는 표, 차트, 오디오 및 비디오.+ Present 발표+ Transitions including Morph, animations and motion paths, a full-screen slide show, presenter view and rehearsed timings. 모핑을 포함한 전환, 애니메이션 및 이동 경로, 전체 화면 슬라이드 쇼, 발표자 보기 및 연습한 발표 시간.+ Exchange files 파일 교환+ Opens and saves .pptx presentation files and exports PDF (slides, notes pages and handouts), PNG and JPEG. .pptx 프레젠테이션을 열고 저장하며 PDF(슬라이드, 메모 페이지 및 유인물), PNG 및 JPEG로 내보냅니다.+ Editing a slide, with sections and notes 구역과 메모가 있는 슬라이드 편집+ The Shape Format tab 도형 서식 탭+ The slide sorter 여러 슬라이드 보기+ The Format Shape pane 도형 서식 창+ Spreadsheets you already know how to use. 이미 익숙한 방식으로 사용하는 스프레드시트.+ A spreadsheet with the ribbon, formula bar, sheet tabs and shortcuts you know, on a calculation engine written in Rust. No subscription, no license server and no telemetry. 익숙한 리본, 수식 입력줄, 시트 탭 및 단축키를 Rust 계산 엔진 위에 구현한 스프레드시트입니다. 구독, 라이선스 서버 및 사용 정보 수집이 없습니다.+ The ribbon, formula bar, Name Box, fill handle, point-mode formulas, Format Cells, Paste Special, conditional formatting, tables and charts. 리본, 수식 입력줄, 이름 상자, 채우기 핸들, 클릭으로 참조를 지정하는 수식, 셀 서식, 선택하여 붙여넣기, 조건부 서식, 표 및 차트.+ Compatible 호환성+ XLSX is the native format, with formulas, tables, conditional formats, charts, pivot tables and print settings round-tripping. CSV and TSV too. 기본 형식은 XLSX이며 수식, 표, 조건부 서식, 차트, 피벗 테이블 및 인쇄 설정을 열고 저장할 때 유지합니다. CSV와 TSV도 지원합니다.+ Capable 풍부한 기능+ A dependency-graph calculation engine with dynamic arrays, LET and LAMBDA, structured table references, sort, AutoFilter and Flash Fill. 동적 배열, LET 및 LAMBDA, 구조화된 표 참조, 정렬, 자동 필터 및 빠른 채우기를 갖춘 종속성 그래프 계산 엔진.+ Copy-on-write workbooks, incremental recalculation and a grid that only lays out what is on screen. 쓰기 시 복사 방식의 통합 문서, 증분 재계산 및 화면에 보이는 영역만 배치하는 격자.+ Agent-native 에이전트 기본 지원+ Every ribbon button, menu item, dialog and keystroke is a command, available over a JSON control channel, an MCP server or the CLI. 모든 리본 버튼, 메뉴 항목, 대화 상자 및 키 입력이 명령이며 JSON 제어 채널, MCP 서버 또는 CLI로 사용할 수 있습니다.+ One Rust codebase for the desktop and the web. 하나의 Rust 코드베이스로 데스크톱과 웹을 지원합니다.+ A quarterly sales dashboard 분기별 매출 대시보드+ A household budget 가계부+ A pivot table 피벗 테이블+ Formatting a chart 차트 서식 지정+ Computer-aided drafting, in pure Rust. 순수 Rust로 구현한 컴퓨터 지원 제도.+ A drafting app with the command line, object snaps and workflow that drafters already know. Free, with no account and no subscription. In early, fast development. 제도 작업자에게 익숙한 명령줄, 개체 스냅 및 작업 방식을 갖춘 제도 앱입니다. 무료이며 계정이나 구독이 필요하지 않습니다. 초기 단계에서 빠르게 개발 중입니다.+ Type L, click two points, type @5<45 and press Enter. Prompts with clickable keywords, polar tracking, direct distance entry and grips work the way drafting habit expects. L을 입력하고 두 점을 클릭한 다음 @5<45를 입력하고 Enter를 누르세요. 클릭 가능한 키워드가 있는 프롬프트, 극좌표 추적, 직접 거리 입력 및 그립이 익숙한 제도 방식대로 작동합니다.+ Open files 개방형 파일 지원+ Reads and writes DXF natively (R12 through 2018) and opens and saves DWG (R13 through 2018) with the open-source acadrust library. Exports SVG and PNG. DXF(R12~2018)를 네이티브로 읽고 쓰며 오픈 소스 acadrust 라이브러리로 DWG(R13~2018)를 열고 저장합니다. SVG와 PNG로 내보냅니다.+ Fast and native 빠른 네이티브 앱+ Pure Rust and egui, with no Electron and no web view. Electron이나 웹 뷰 없이 순수 Rust와 egui로 구현했습니다.+ A full drafting toolset 완전한 제도 도구+ Draw, modify, dimension, hatch and block commands, layers with a properties manager, paper-space layouts and PDF plotting. 그리기, 수정, 치수, 해치 및 블록 명령, 속성 관리자가 있는 레이어, 도면 공간 레이아웃 및 PDF 출력.+ Every menu item, tool and prompt is a command, available over MCP, a JSON control channel or the CLI. 모든 메뉴 항목, 도구 및 프롬프트가 명령이며 MCP, JSON 제어 채널 또는 CLI로 사용할 수 있습니다.+ An apartment floor plan 아파트 평면도+ A mechanical bracket drawing 기계용 브래킷 도면+ A layout sheet 레이아웃 시트+ Recording, editing and mixing, in Rust. Rust로 구현한 녹음, 편집 및 믹싱.+ A digital audio workstation that follows the workflow audio engineers already know: an Edit window for arranging and editing, and a Mix window for balancing. Pre-alpha. 오디오 엔지니어에게 익숙한 작업 방식을 따르는 디지털 오디오 워크스테이션입니다. 편집 창에서 배치와 편집을 하고 믹스 창에서 균형을 조정합니다. 프리알파 단계입니다.+ Editing 편집+ Shuffle, Slip, Spot and Grid modes, Smart, Grabber, Trim and other tools, fades and crossfades, playlists with comping lanes and clip gain. 셔플, 슬립, 스팟 및 격자 모드, 스마트·그래버·트림 등의 도구, 페이드와 크로스페이드, 컴핑 레인이 있는 플레이리스트 및 클립 게인.+ Mixing 믹싱+ Faders and pans with automation, sends, busses, aux inputs, VCA masters, routing folders and automatic plugin delay compensation. 오토메이션이 있는 페이더와 팬, 센드, 버스, 보조 입력, VCA 마스터, 라우팅 폴더 및 자동 플러그인 지연 보정.+ Automation 오토메이션+ Volume, pan, mute, send and every plugin parameter, with Write, Touch, Latch and Trim passes recorded live from the faders. 볼륨, 팬, 음소거, 센드 및 모든 플러그인 매개 변수를 페이더에서 실시간으로 기록하며 쓰기, 터치, 래치 및 트림 모드를 지원합니다.+ Plugins 플러그인+ EQ, dynamics, reverbs, modulation effects, a subtractive synth and a drum synth, all original, and CLAP plugins load too. 자체 제작한 EQ, 다이내믹스, 리버브, 모듈레이션 효과, 감산 신시사이저 및 드럼 신시사이저를 제공하며 CLAP 플러그인도 불러올 수 있습니다.+ The Edit window with a nine-track session 9트랙 세션의 편집 창+ The Mix window 믹스 창+ Automation and memory locations 오토메이션 및 메모리 위치+ The Score Editor 악보 편집기+ Interface design and prototyping, in pure Rust. 순수 Rust로 구현한 인터페이스 디자인 및 프로토타이핑.+ Design screens, components and prototypes in a native app that opens instantly and works offline, with no account and no telemetry. Early alpha. 즉시 열리고 오프라인으로 작동하는 네이티브 앱에서 화면, 컴포넌트 및 프로토타입을 디자인하세요. 계정과 사용 정보 수집이 없습니다. 초기 알파 단계입니다.+ Auto layout 자동 레이아웃+ Rows, columns, wrap and grids with padding, gaps and Hug, Fill or Fixed sizing, plus constraints and layout grids. 안쪽 여백과 간격, 내용 맞춤·채우기·고정 크기를 갖춘 행, 열, 줄 바꿈 및 격자와 제약 조건 및 레이아웃 격자.+ Components 컴포넌트+ Instances with overrides, variants and properties, styles, and variables with modes bound to fills, strokes and effects. 재정의, 변형 및 속성이 있는 인스턴스, 스타일과 모드가 있는 변수 및 채우기·선·효과 연결.+ Vectors 벡터+ Real vector networks, the pen and pencil, the shape builder, editable booleans and variable width strokes. 벡터 네트워크, 펜과 연필, 도형 구성 도구, 편집 가능한 불리언 연산 및 가변 너비 선.+ Prototypes 프로토타입+ Connections, triggers and animated transitions, variables with conditionals, flows and a presentation view with device frames. 연결, 트리거 및 애니메이션 전환, 조건부 변수, 흐름 및 기기 프레임이 있는 프레젠테이션 보기.+ Hand off 개발 전달+ Inspect mode with measurements, annotations, ready-for-dev status, and code for CSS, HTML, React, SwiftUI and Compose. 치수, 주석, 개발 준비 상태와 CSS, HTML, React, SwiftUI 및 Compose 코드를 제공하는 검사 모드.+ Plain .figcraft files on your own disk. SVG, PNG, JPEG, WebP and PDF in and out, and .fig files open, best effort. 내 디스크의 일반 .figcraft 파일을 사용합니다. SVG, PNG, JPEG, WebP 및 PDF를 가져오고 내보내며 .fig 파일도 가능한 범위에서 엽니다.+ macOS, Windows, Linux, FreeBSD, web macOS, Windows, Linux, FreeBSD, 웹@@ -13,11 +13,12 @@ use super::detect::system_lang; pub enum Script { Chinese, Japanese,+ Korean, } impl Script { /// Every script a language can be written in.- pub const ALL: [Script; 2] = [Script::Chinese, Script::Japanese];+ pub const ALL: [Script; 3] = [Script::Chinese, Script::Japanese, Script::Korean]; } /// One supported UI language.@@ -94,7 +95,7 @@ pub(super) fn plural_polish(n: u64) -> usize { } /// The registry. English first: it is the fallback and the source language.-pub static LANGUAGES: [LangInfo; 11] = [+pub static LANGUAGES: [LangInfo; 12] = [ LangInfo { code: "en", name: "English",@@ -216,6 +217,17 @@ pub static LANGUAGES: [LangInfo; 11] = [ complete_menus: true, catalog: OnceLock::new(), },+ LangInfo {+ code: "ko",+ name: "한국어",+ english_name: "Korean",+ script: Some(Script::Korean),+ source: include_str!("ko.tsv"),+ plural: plural_none,+ plural_forms: 1,+ complete_menus: true,+ catalog: OnceLock::new(),+ }, ]; impl LangInfo {@@ -254,6 +266,7 @@ impl Lang { pub const PL: Lang = Lang(&LANGUAGES[8]); pub const HU: Lang = Lang(&LANGUAGES[9]); pub const JA: Lang = Lang(&LANGUAGES[10]);+ pub const KO: Lang = Lang(&LANGUAGES[11]); pub fn code(self) -> &'static str { self.0.code@@ -533,3 +533,45 @@ fn complete_catalogs_translate_package_formats() { } } }++#[test]+fn korean_registry_locale_and_single_plural_form() {+ assert_eq!(Lang::from_code("ko"), Some(Lang::KO));+ assert_eq!(Lang::from_code("KO"), Some(Lang::KO));+ assert_eq!(Lang::KO.script(), Some(super::Script::Korean));+ assert!(Lang::KO.complete_menus());+ for tag in ["ko", "ko-KR", "ko_KR.UTF-8", "KO-kr"] {+ assert_eq!(lang_from_tag(tag), Some(Lang::KO), "{tag}");+ }+ for n in [0, 1, 2, 5, 21, 100] {+ assert_eq!((LANGUAGES[Lang::KO.index()].plural)(n), 0);+ }+ let settings = crate::Settings { language: "ko".to_string(), ..crate::Settings::default() };+ let saved = serde_json::to_string(&settings).unwrap();+ let restored: crate::Settings = serde_json::from_str(&saved).unwrap();+ assert_eq!(restored.language, "ko");+}++/// Editorial descriptions are dynamic source data, so the literal scanner cannot see them.+#[test]+fn korean_covers_every_app_description() {+ for details in artcraft_launcher_core::details::DETAILS {+ let mut strings = vec![details.headline, details.platforms];+ strings.extend(details.paragraphs());+ for feature in details.features {+ strings.extend([feature.title, feature.text]);+ }+ strings.extend(details.shots.iter().map(|shot| shot.caption));+ for source in strings {+ assert!(+ LANGUAGES[Lang::KO.index()]+ .catalog()+ .contextual(details.id, source)+ .or_else(|| LANGUAGES[Lang::KO.index()].catalog().plain(source))+ .is_some(),+ "Korean description missing: {} / {source:?}",+ details.id+ );+ }+ }+}@@ -505,7 +505,7 @@ impl LauncherUi { } } if let Some(details) = AppDetails::by_id(id) {- crate::card_parts::blurb(ui, details.headline, t.muted, 4);+ crate::card_parts::blurb(ui, tr_ctx(current(), app.id, details.headline), t.muted, 4); } if let Some(line) = crate::card_parts::last_updated(&entry) { ui.add_space(6.0);new file mode 100644@@ -0,0 +1,183 @@+//! The Korean UI, in its own test binary: the UI language is process-wide, so it can't share one+//! with the English UI tests. Korean is drawn with the system's Korean font; where there is+//! none (CI, minimal Linux installs), the UI stays in English and says why.+//! `ARTCRAFT_LAUNCHER_SNAPSHOTS=<dir>` also saves screenshots (run with `-- --test-threads=1`).++use std::cell::{Cell, RefCell};+use std::rc::Rc;++use artcraft_launcher_core::protocol::{Backend, Capabilities, Command, Event, JobPhase, LatestRelease, Origin, PausedDownload, Source};+use artcraft_launcher_core::release::{Asset, Release};+use artcraft_launcher_core::status::InstallSource;+use artcraft_launcher_core::{Arch, Installed, Os, Platform, SUITE, version};+use artcraft_launcher_ui_egui::settings::ViewMode;+use artcraft_launcher_ui_egui::{Host, LauncherUi, Settings};+use egui_kittest::Harness;+use egui_kittest::kittest::Queryable;++const NOW: i64 = 1_791_500_000;++struct Fake {+ events: Vec<Event>,+ sent: Rc<RefCell<Vec<Command>>>,+ manage: bool,+ clock: Rc<Cell<i64>>,+}++impl Backend for Fake {+ fn send(&mut self, command: Command) {+ self.sent.borrow_mut().push(command);+ }+ fn poll(&mut self) -> Vec<Event> {+ std::mem::take(&mut self.events)+ }+ fn capabilities(&self) -> Capabilities {+ Capabilities { manage_apps: self.manage, reveal_label: "Show in Finder", self_update: self.manage }+ }+ fn platform(&self) -> Option<Platform> {+ Some(Platform { os: Os::MacOs, arch: Arch::Arm64 })+ }+ fn now(&self) -> i64 {+ self.clock.get()+ }+}++struct NoHost;+impl Host for NoHost {}++fn release(id: &str, v: &str, at: i64) -> Release {+ let name = format!("{id}-{v}-macos-universal.dmg");+ Release {+ tag: format!("v{v}"),+ version: version::parse(v),+ name: format!("v{v}"),+ published_at: Some(at),+ html_url: format!("https://github.com/storytold/{id}/releases/tag/v{v}"),+ notes: "## What's Changed\n* Faster things by @a in https://github.com/storytold/x/pull/1".into(),+ prerelease: false,+ assets: vec![Asset {+ name: name.clone(),+ size: 150 << 20,+ url: format!("https://github.com/storytold/{id}/releases/download/v{v}/{name}"),+ sha256: None,+ }],+ }+}++/// A realistic mix: updates, up-to-date apps, a download in progress, one app with no release.+fn events() -> Vec<Event> {+ let mut ev = Vec::new();+ let versions = [+ ("photocraft", "0.5.0"),+ ("vectorcraft", "0.7.0"),+ ("filmcraft", "0.4.0"),+ ("lightcraft", "0.4.0"),+ ("pdfcraft", "0.4.0"),+ ("effectcraft", "0.6.0"),+ ("designcraft", "0.4.0"),+ ("wordcraft", "0.3.0"),+ ("deckcraft", "0.3.0"),+ ("gridcraft", "0.3.0"),+ ("cadcraft", "0.3.0"),+ ];+ for (i, (id, v)) in versions.iter().enumerate() {+ ev.push(Event::Latest {+ app: (*id).into(),+ latest: LatestRelease::Found(release(id, v, NOW - 86_400 - i as i64 * 600)),+ from: Origin::ArtCraftApi,+ });+ }+ ev.push(Event::Latest { app: "soundcraft".into(), latest: LatestRelease::None, from: Origin::ArtCraftApi });+ let installed = [("photocraft", "0.4.1"), ("lightcraft", "0.4.0"), ("wordcraft", "0.3.0"), ("effectcraft", "0.5.0")];+ for app in &SUITE {+ let inst = installed.iter().find(|(id, _)| *id == app.id).map(|(_, v)| Installed {+ version: version::parse(v),+ source: InstallSource::MacApp,+ location: format!("/Applications/{}.app", app.name),+ package: None,+ });+ ev.push(Event::Detected { app: app.id.into(), installed: inst });+ }+ ev.push(Event::Progress { app: "filmcraft".into(), phase: JobPhase::Downloading { done: 48 << 20, total: 131 << 20 } });+ ev.push(Event::Paused { app: "deckcraft".into(), download: Some(PausedDownload { done: 30 << 20, total: 120 << 20 }) });+ ev.push(Event::Progress { app: "gridcraft".into(), phase: JobPhase::Reconnecting { done: 10 << 20, total: 90 << 20 } });+ ev.push(Event::CheckFinished { at: NOW - 180, source: Source::GitHubApi, rate_limited_until: None });+ ev+}++fn harness_language(+ view: ViewMode,+ theme: egui::Theme,+ manage: bool,+ language: &str,+) -> (Harness<'static, Option<LauncherUi>>, Rc<RefCell<Vec<Command>>>) {+ artcraft_launcher_ui_egui::i18n::set_test_lang(artcraft_launcher_ui_egui::i18n::Lang::EN);+ let sent = Rc::new(RefCell::new(Vec::new()));+ let sent2 = sent.clone();+ let mut backend = Some(Fake { events: events(), sent: sent2, manage, clock: Rc::new(Cell::new(NOW)) });+ let settings = Settings { view, notify: false, language: language.into(), ..Settings::default() };+ let harness = Harness::builder().with_size(egui::vec2(1180.0, 900.0)).with_theme(theme).build_ui_state(+ move |ui, state: &mut Option<LauncherUi>| {+ if state.is_none()+ && let Some(b) = backend.take()+ {+ let mut s = settings.clone();+ s.theme = if theme == egui::Theme::Dark {+ artcraft_launcher_ui_egui::theme::ThemeChoice::Dark+ } else {+ artcraft_launcher_ui_egui::theme::ThemeChoice::Light+ };+ *state = Some(LauncherUi::new(ui.ctx(), Box::new(b), s));+ }+ if let Some(l) = state.as_mut() {+ l.logic(&ui.ctx().clone(), &mut NoHost);+ l.ui(ui, &mut NoHost);+ }+ },+ None,+ );+ (harness, sent)+}++fn snapshot(h: &mut Harness<'_, Option<LauncherUi>>, name: &str) {+ let Some(dir) = std::env::var_os("ARTCRAFT_LAUNCHER_SNAPSHOTS") else { return };+ let img = h.render().unwrap();+ img.save(std::path::Path::new(&dir).join(format!("{name}.png"))).unwrap();+}++// One test: the UI language is process-wide.+#[test]+fn korean_switches_persists_and_falls_back_without_a_font() {+ use artcraft_launcher_ui_egui::i18n::{self, Lang};+ let (mut h, _) = harness_language(ViewMode::Grid, egui::Theme::Dark, true, "en");+ h.run_steps(6);+ h.get_by_label("Settings").click();+ h.run_steps(3);+ h.get_by_value("English").click();+ h.run_steps(2);+ assert!(!artcraft_launcher_ui_egui::theme::cjk_font_loaded(Lang::KO));+ h.get_by_label("Korean").click_accesskit();+ h.run_steps(3);+ let settings = h.state().as_ref().unwrap().settings.clone();+ assert_eq!(settings.language, "ko");+ let saved = serde_json::to_string(&settings).unwrap();+ let restored: Settings = serde_json::from_str(&saved).unwrap();+ assert_eq!(restored.language, "ko");+ if !artcraft_launcher_ui_egui::theme::cjk_font_available(Lang::KO) {+ h.get_by_label_contains("No font for this language was found");+ h.get_by_label("Notify me when an installed app has an update");+ snapshot(&mut h, "settings-ko-no-font");+ return;+ }+ h.get_by_value("Korean (한국어)");+ h.get_by_label("설치된 앱의 업데이트 알림 받기");+ snapshot(&mut h, "settings-ko-dark");+ h.get_by_label("완료").click();+ h.run_steps(3);+ h.get_by_label(i18n::tr_ctx(Lang::KO, "photocraft", "Image editing"));+ snapshot(&mut h, "grid-ko-dark");+ let (mut table, _) = harness_language(ViewMode::Table, egui::Theme::Light, true, &restored.language);+ table.run_steps(6);+ table.get_by_label("버전");+ snapshot(&mut table, "table-ko-light");+}