i18n: Indonesian (id) interface language

Adds crates/ui-egui/locales/id.json and id-formats.json: every plain
(2,258) and formats (297) message any catalog has, plus 69 command
labels no catalog translated yet. What's New stays in English.

Indonesian keeps a noun unchanged after a count, so English plural
suffixes are consumed with {:.0}; dates read "Minggu, 20/09/2026".
Menu triangles use → and ⌄ like the other catalogs. 356 labels reuse
the sibling apps' Indonesian wording (PhotoCraft, VectorCraft,
FilmCraft, FigCraft) so the suite reads the same; credited in
ATTRIBUTION.md.

Registers Locale::Id ("Bahasa Indonesia", Latn), the
app.language.indonesian command, the keyword-list catalog check and
an Indonesian switch/persist/format/date test. Docs: localization.md,
localization-id.md, localization-parity.md, ROADMAP.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Reva Doni Aprilio
2026-10-19 12:17:59 +07:00
co-authored by Claude Opus 5.5
parent d999e382ba
commit 816a1112e3
10 changed files with 2746 additions and 8 deletions
+2 -1
View File
@@ -96,7 +96,7 @@ while lower-layer errors stay English. Detail: [localization-parity.md](docs/loc
| Korean | ko | 0% | none (no Hangul face) | | Korean | ko | 0% | none (no Hangul face) |
| Vietnamese | vi | 0% | none | | Vietnamese | vi | 0% | none |
Also shipped: Traditional Chinese (87%), Russian (74%), Ukrainian (99%), Italian (92%), Georgian (100%). Also shipped: Traditional Chinese (87%), Russian (74%), Ukrainian (99%), Italian (92%), Georgian (100%), Indonesian (every message but What's New).
## Upcoming ## Upcoming
@@ -183,6 +183,7 @@ Ranked toward beta; detail in [docs/roadmap.md](docs/roadmap.md), gaps in [docs/
| Date | Change | Summary | | Date | Change | Summary |
|---|---|---| |---|---|---|
| 2026-10-19 | minor | Indonesian interface (`id`): every plain and formats message, plus 69 command labels, locale, menu command and tests; What's New left in English; headline numbers unchanged |
| 2026-10-12 | minor | Italian interface (`it`, #1192): 2,201 of 2,398 messages, locale, menu command and tests; headline numbers unchanged | | 2026-10-12 | minor | Italian interface (`it`, #1192): 2,201 of 2,398 messages, locale, menu command and tests; headline numbers unchanged |
| 2026-10-13 | minor | Sony ILCA-99M2 camera colour profile in the log; numbers unchanged | | 2026-10-13 | minor | Sony ILCA-99M2 camera colour profile in the log; numbers unchanged |
| 2026-10-14 | minor | Localization ~52% → ~55% (mean of the 12 key languages): de, es, fr, ja and uk catalogs filled | | 2026-10-14 | minor | Localization ~52% → ~55% (mean of the 12 key languages): de, es, fr, ja and uk catalogs filled |
+1
View File
@@ -33,6 +33,7 @@ covers a whole directory), or if a licence file referenced in the Licence column
| `crates/ui-egui/locales/uk.json`, `crates/ui-egui/locales/uk-formats.json` | Ukrainian interface catalogs | @dmatviichuk with Codex; PhotoCraft, FilmCraft and VectorCraft translation contributors for reused labels | original translations from English source; 337 initial English-key matches reviewed from [PhotoCraft 69c41f3](https://github.com/storytold/photocraft/blob/69c41f3523e147eb215da8c7b4c29b18b35d1c63/crates/ui-egui/src/i18n/uk.tsv), [FilmCraft 4854c27](https://github.com/storytold/filmcraft/blob/4854c277536fe8118257362304b6a7d82d8e704b/crates/ui-egui/src/i18n/uk.tsv), [VectorCraft 4b44265](https://github.com/storytold/vectorcraft/blob/4b44265965fdd6ffedfd6182a892c1a6c360d344/crates/ui-egui/src/i18n/uk.tsv) | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`, also the source repositories' licences) | 2026-10-09 | adapted to LightCraft photo-development context, completed catalogs, reviewed spelling and placeholders | | `crates/ui-egui/locales/uk.json`, `crates/ui-egui/locales/uk-formats.json` | Ukrainian interface catalogs | @dmatviichuk with Codex; PhotoCraft, FilmCraft and VectorCraft translation contributors for reused labels | original translations from English source; 337 initial English-key matches reviewed from [PhotoCraft 69c41f3](https://github.com/storytold/photocraft/blob/69c41f3523e147eb215da8c7b4c29b18b35d1c63/crates/ui-egui/src/i18n/uk.tsv), [FilmCraft 4854c27](https://github.com/storytold/filmcraft/blob/4854c277536fe8118257362304b6a7d82d8e704b/crates/ui-egui/src/i18n/uk.tsv), [VectorCraft 4b44265](https://github.com/storytold/vectorcraft/blob/4b44265965fdd6ffedfd6182a892c1a6c360d344/crates/ui-egui/src/i18n/uk.tsv) | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`, also the source repositories' licences) | 2026-10-09 | adapted to LightCraft photo-development context, completed catalogs, reviewed spelling and placeholders |
| `crates/ui-egui/locales/cs.json`, `crates/ui-egui/locales/cs-formats.json` | Czech interface catalogs | @Losminek with AI assistance (Codex) | original work: translations of English source phrases, reviewed against the app UI conventions; no external translation material | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | 2026-10-11 | catalog coverage, Czech dates and count-neutral wording; independent scope/terminology review; native-speaker proofreading pending | | `crates/ui-egui/locales/cs.json`, `crates/ui-egui/locales/cs-formats.json` | Czech interface catalogs | @Losminek with AI assistance (Codex) | original work: translations of English source phrases, reviewed against the app UI conventions; no external translation material | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | 2026-10-11 | catalog coverage, Czech dates and count-neutral wording; independent scope/terminology review; native-speaker proofreading pending |
| `crates/ui-egui/locales/it.json`, `crates/ui-egui/locales/it-formats.json` | Italian interface catalogs | @GabrielePecchioli with Claude Code; PhotoCraft, VectorCraft, PdfCraft and WordCraft translation contributors for reused labels | original translations from English source; 410 initial English-key matches reviewed from [PhotoCraft da3da09](https://github.com/storytold/photocraft/blob/da3da0942c14c1159f40c652afcf33584332c733/crates/ui-egui/src/i18n/it.tsv), [VectorCraft 5432821](https://github.com/storytold/vectorcraft/blob/543282148007bcb5633c493a5374cd26b0339c8d/crates/ui-egui/src/i18n/it.tsv), [PdfCraft e8e275c](https://github.com/storytold/pdfcraft/blob/e8e275cbd40ccd0dc6b56f82b16a575571043702/crates/ui-egui/src/i18n/it.tsv) and the Italian WordCraft catalog ([storytold/wordcraft#598](https://github.com/storytold/wordcraft/pull/598)); 46 of them retranslated where the sibling's meaning didn't fit a photo app | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`, also the source repositories' licences) | 2026-10-12 | adapted to LightCraft photo-development context, count-neutral labels for Italian plural agreement, reviewed placeholders | | `crates/ui-egui/locales/it.json`, `crates/ui-egui/locales/it-formats.json` | Italian interface catalogs | @GabrielePecchioli with Claude Code; PhotoCraft, VectorCraft, PdfCraft and WordCraft translation contributors for reused labels | original translations from English source; 410 initial English-key matches reviewed from [PhotoCraft da3da09](https://github.com/storytold/photocraft/blob/da3da0942c14c1159f40c652afcf33584332c733/crates/ui-egui/src/i18n/it.tsv), [VectorCraft 5432821](https://github.com/storytold/vectorcraft/blob/543282148007bcb5633c493a5374cd26b0339c8d/crates/ui-egui/src/i18n/it.tsv), [PdfCraft e8e275c](https://github.com/storytold/pdfcraft/blob/e8e275cbd40ccd0dc6b56f82b16a575571043702/crates/ui-egui/src/i18n/it.tsv) and the Italian WordCraft catalog ([storytold/wordcraft#598](https://github.com/storytold/wordcraft/pull/598)); 46 of them retranslated where the sibling's meaning didn't fit a photo app | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`, also the source repositories' licences) | 2026-10-12 | adapted to LightCraft photo-development context, count-neutral labels for Italian plural agreement, reviewed placeholders |
| `crates/ui-egui/locales/id.json`, `crates/ui-egui/locales/id-formats.json` | Indonesian interface catalogs | @RevDonz with Claude Code; PhotoCraft, VectorCraft, FilmCraft and FigCraft translation contributors for reused labels | original translations from English source; 356 labels match the sibling apps' Indonesian wording, reviewed from [PhotoCraft c39d752](https://github.com/storytold/photocraft/blob/c39d752758155492bbaba71d802cfaa03206fb4a/crates/ui-egui/src/i18n/id.tsv), [VectorCraft 9fe0f03](https://github.com/storytold/vectorcraft/blob/9fe0f03de2bd29e8c86a1179f977c3e651069424/crates/ui-egui/src/i18n/id.tsv), [FilmCraft fae7f8a](https://github.com/storytold/filmcraft/blob/fae7f8a9be09bd74f95fbe08c3d98adc17fe1030/crates/ui-egui/src/i18n/id.tsv) and [FigCraft 8482c3e](https://github.com/storytold/figcraft/blob/8482c3e85bc5f2a430e2f3be2826674872f6d084/crates/ui-egui/locales/id.toml) | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`, also the source repositories' licences) | 2026-10-19 | adapted to LightCraft's photo context, count-neutral nouns, reviewed placeholders |
| `crates/ui-egui/locales/ka.json`, `crates/ui-egui/locales/ka-formats.json` | Georgian interface catalogs | Davit Matchakhelidze with Claude; WordCraft, GridCraft, PhotoCraft, FilmCraft and VectorCraft Georgian catalogs for reused labels | original translations from the English source (meaning-based, no vendor localization used); 450 English-key matches reused from the sibling Craft apps' Georgian catalogs | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | 2026-10-12 | adapted to LightCraft photo-library and raw-development context; count-neutral numerals; placeholders checked | | `crates/ui-egui/locales/ka.json`, `crates/ui-egui/locales/ka-formats.json` | Georgian interface catalogs | Davit Matchakhelidze with Claude; WordCraft, GridCraft, PhotoCraft, FilmCraft and VectorCraft Georgian catalogs for reused labels | original translations from the English source (meaning-based, no vendor localization used); 450 English-key matches reused from the sibling Craft apps' Georgian catalogs | MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`) | 2026-10-12 | adapted to LightCraft photo-library and raw-development context; count-neutral numerals; placeholders checked |
Notes Notes
+299
View File
@@ -0,0 +1,299 @@
{
" · {skipped} skipped (file exists)": " · {skipped} dilewati (file sudah ada)",
" · {} duplicate{} skipped": " · {} duplikat dilewati{:.0}",
" · {} failed: {}": " · {} gagal: {}",
" · {} moved": " · {} dipindahkan",
" · {} not readable": " · {} tidak dapat dibaca",
" · {} restored from Recently Deleted": " · {} dipulihkan dari Baru dihapus",
", {failed} file{} not readable": ", {failed} file tidak dapat dibaca{:.0}",
"Add {local_n} to My Photos": "Tambahkan {local_n} ke Foto saya",
"Add {} Photo{}": "Tambahkan {} foto{:.0}",
"Add {} Photo{}, restore {}": "Tambahkan {} foto{:.0}, pulihkan {}",
"Add “{}” to Favorites": "Tambahkan “{}” ke Favorit",
"Added {n} photo{} to My Photos": "{n} foto{:.0} ditambahkan ke Foto saya",
"Added {n} photo{} to “{}”": "{n} foto ditambahkan ke “{1}”{0:.0}",
"All {n} selected photos move by the same amount.": "Semua {n} foto yang dipilih digeser dengan selisih waktu yang sama.",
"Already in “{}”": "Sudah ada di “{}”",
"Auto Sync: edits apply to {n} photos": "Sinkronisasi otomatis: edit diterapkan ke {n} foto",
"Automatic ({} MB)": "Otomatis ({} MB)",
"Click {}:1": "Klik {}:1",
"Color space: Rec. 2020 {curve} (HDR AVIF)": "Ruang warna: Rec. 2020 {curve} (AVIF HDR)",
"Create {n} Virtual Copies": "Buat {n} salinan virtual",
"Decoder: {reason}": "Dekoder: {reason}",
"Delete \"{label}\"": "Hapus \"{label}\"",
"Delete \"{label}\" (and the mask)": "Hapus \"{label}\" (beserta maskernya)",
"Delete {n} Photos": "Hapus {n} foto",
"Delete “{current}”": "Hapus “{current}”",
"Duplicate \"{label}\"": "Duplikat \"{label}\"",
"Example: {example}": "Contoh: {example}",
"Exported {n} photo{}": "{n} foto{:.0} diekspor",
"Exported {ok} of {total} photo{}": "{ok} dari {total} foto diekspor{:.0}",
"Exported {} point curve preset{}": "{} preset kurva titik{:.0} diekspor",
"Exported {} preset{}": "{} preset{:.0} diekspor",
"Exporting {} of {total}": "Mengekspor {} dari {total}",
"Filter bar — {active} active filter{}": "Bilah filter — {active} filter aktif{:.0}",
"Healed {n} dust spot{}": "{n} bintik debu diperbaiki{:.0}",
"Imported {n} point curve preset{}": "{n} preset kurva titik diimpor{:.0}",
"Imported {n} preset{}": "{n} preset{:.0} diimpor",
"Imported {n} preset{}, {f} file{} not readable": "{n} preset diimpor{0:.0}, {f} file tidak dapat dibaca{1:.0}",
"Imported {} photo{}": "{} foto{:.0} diimpor",
"LightCraft could not open its window: {e}": "LightCraft tidak dapat membuka jendela: {e}",
"LightCraft could not open its window: {e}\n\nThe log file has the details: {path}": "LightCraft tidak dapat membuka jendela: {e}\n\nDetailnya ada di file log: {path}",
"Matches: {}": "Cocok: {}",
"Merging… {stage} {:.0}%": "Menggabungkan… {stage} {:.0}%",
"Move {n} photo{} to “{}”": "Pindahkan {n} foto{0:.0} ke “{1}”",
"Move {n} photo{} to “{}”?": "Pindahkan {n} foto{0:.0} ke “{1}”?",
"Move {what} to Recently Deleted?": "Pindahkan {what} ke Baru dihapus?",
"Moved {n} photo{} to “{}”. Edit → Undo moves them back.": "{n} foto dipindahkan ke “{1}”{0:.0}. Pilih Edit → Urungkan untuk mengembalikannya.",
"Named ({named_n})": "Bernama ({named_n})",
"Paste into {n} photo{} — only settings that were copied are pasted": "Tempel ke {n} foto{:.0} — hanya menempelkan pengaturan yang disalin",
"Preset: {name}": "Preset: {name}",
"Preview only — editing the camera's embedded JPEG ({})": "Hanya pratinjau — yang diedit adalah JPEG bawaan kamera ({})",
"Preview only — {}": "Hanya pratinjau — {}",
"Profile: {}": "Profil: {}",
"Proof Copy ({})": "Salinan soft proof ({})",
"Proof Preview · {}": "Pratinjau soft proof · {}",
"Quick Develop · {n} photos": "Olah cepat · {n} foto",
"Rated {}": "Diberi rating {}",
"Reading Lightroom catalog… {done} of {total}": "Membaca katalog Lightroom… {done} dari {total}",
"Reading photos… {done} of {total}": "Membaca foto… {done} dari {total}",
"Redo {}": "Ulangi {}",
"Remove “{}” from Favorites": "Keluarkan “{}” dari Favorit",
"Removed from {}": "Dikeluarkan dari {}",
"Rename {n} Photos…": "Ganti nama {n} foto…",
"Rename “{name}”": "Ganti nama “{name}”",
"Rendering on the CPU: {why}": "Merender dengan CPU: {why}",
"Reset {} Channel": "Atur ulang kanal {}",
"Restore {}": "Pulihkan {}",
"Restore {} Photo{}": "Pulihkan {} foto{:.0}",
"Restored {} photo{} from Recently Deleted": "{} foto{:.0} dipulihkan dari Baru dihapus",
"Saved export preset “{}”": "Preset ekspor “{}” disimpan",
"Show {hidden} hidden location{}": "Tampilkan {hidden} lokasi tersembunyi{:.0}",
"Showing {n} of {}": "Menampilkan {n} dari {}",
"Smart album: {}": "Album pintar: {}",
"The XMP sidecars of {n} photos couldn't be written (is their folder offline?). LightCraft keeps trying.": "File sidecar XMP untuk {n} foto tidak dapat ditulis (apakah foldernya sedang offline?). LightCraft akan terus mencoba.",
"Undo {}": "Urungkan {}",
"Version {} — a clean-room, pure-Rust photo library and raw developer.": "Versi {} — pustaka foto dan pengolah raw yang dibuat secara clean-room dengan Rust murni.",
"Version: {}": "Versi: {}",
"none (CPU rendering): {why}": "tidak ada (rendering CPU): {why}",
"the {n} selected photos": "{n} foto yang dipilih",
"the {} photos in view": "{} foto yang ditampilkan",
"these {count} photos": "{count} foto ini",
"{count} smart preview{} · {:.1} MB": "{count} pratinjau pintar{0:.0} · {1:.1} MB",
"{f} folders (and their subfolders)": "{f} folder (beserta subfoldernya)",
"{label} Grading": "Grading {label}",
"{label} {d:+} on every selected photo": "{label} {d:+} pada setiap foto yang dipilih",
"{label}, {n} photos": "{label}, {n} foto",
"{matching} of {t} photos": "{matching} dari {t} foto",
"{matching} photos": "{matching} foto",
"{mode} spot {} of {}": "{mode}: bintik {} dari {}",
"{msg} ({failed} failed)": "{msg} ({failed} gagal)",
"{name} Label": "Label {name}",
"{name} Off": "{name} nonaktif",
"{name} On": "{name} aktif",
"{name} — double-click to reset it": "{name} — klik dua kali untuk mengatur ulang",
"{n} change{} saved in memory but not written to disk: {e}\nLightCraft retries automatically; quitting now would lose {}.": "{n} perubahan{0:.0} tersimpan di memori, tetapi belum ditulis ke disk: {e}\nLightCraft akan otomatis mencoba lagi; jika Anda keluar sekarang, perubahan tersebut akan hilang.{1:.0}",
"{n} found · {sel} selected": "{n} ditemukan · {sel} dipilih",
"{n} photos": "{n} foto",
"{n} photos in {} are hidden by the filters above": "{n} foto di {} disembunyikan oleh filter di atas",
"{n} photo{}": "{n} foto{:.0}",
"{n} photo{} match · updates automatically as photos change": "{n} foto cocok{:.0} · diperbarui otomatis saat foto berubah",
"{n} photo{} now · updates automatically as photos change": "Saat ini {n} foto{:.0} · diperbarui otomatis saat foto berubah",
"{n} selected photos · {pages} pages": "{n} foto dipilih · {pages} halaman",
"{sel_n} selected · {counted}": "{sel_n} dipilih · {counted}",
"{stage}… {:.0}%": "{stage}… {:.0}%",
"{what} merge added": "Hasil penggabungan {what} ditambahkan",
"{what} merge failed: {e}": "Penggabungan {what} gagal: {e}",
"{} and higher": "{} ke atas",
"{} files": "{} file",
"{} files and {f} folder{}": "{} file dan {f} folder{:.0}",
"{} of 4 guides — drag along lines that should be vertical or horizontal.": "{} dari 4 garis panduan — seret di sepanjang garis yang seharusnya vertikal atau horizontal.",
"{} photos": "{} foto",
"{} photo{} not readable": "{} foto tidak dapat dibaca{:.0}",
"{} spot(s) on this photo": "{} bintik di foto ini",
"· {dups} already in the library": "· {dups} sudah ada di pustaka",
"· {n} photo{}": "· {n} foto{:.0}",
"· {trashed} in Recently Deleted": "· {trashed} di “Baru dihapus”",
"… and {more} more": "… dan {more} lainnya",
"Import cancelled · {} photo{} added": "Impor dibatalkan · {} foto{:.0} ditambahkan",
"Reading photos… {} of {}": "Membaca foto… {} dari {}",
"Adding photos… {} of {}": "Menambahkan foto… {} dari {}",
" ({} in Recently Deleted)": " ({} di Baru dihapus)",
"Already in the library: {} photo{} selected in All Photos": "Sudah ada di pustaka: {} foto{:.0} dipilih di Semua foto",
"Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "Sudah ada di pustaka, di Baru dihapus: {} foto{:.0} dipilih · klik kanan → Pulihkan, atau Hapus permanen untuk mengimpor ulang",
"Auto ({})": "Otomatis ({})",
"Build 1:1 Previews ({scope})": "Buat pratinjau 1:1 ({scope})",
"Build Smart Previews ({scope})": "Buat pratinjau pintar ({scope})",
"Build Standard-Sized Previews ({scope})": "Buat pratinjau ukuran standar ({scope})",
"Discard Smart Previews ({scope})": "Buang pratinjau pintar ({scope})",
"Lightroom catalog inspected: {photos} photos, {collections} collections, {missing} missing": "Katalog Lightroom diperiksa: {photos} foto, {collections} koleksi, {missing} hilang",
"Lightroom import complete: {imported} photos added": "Impor Lightroom selesai: {imported} foto ditambahkan",
"Lightroom import complete: {imported} photos added, {failed} failed": "Impor Lightroom selesai: {imported} foto ditambahkan, {failed} gagal",
"Lightroom import complete: {imported} photos added; index warning: {warning}": "Impor Lightroom selesai: {imported} foto ditambahkan; peringatan indeks: {warning}",
"Lightroom import failed: {e}{warning}": "Impor Lightroom gagal: {e}{warning}",
"Lightroom inspection failed: {e}{warning}": "Pemeriksaan Lightroom gagal: {e}{warning}",
"Label Set: {name}": "Set label: {name}",
"Quick Develop: {name}": "Olah cepat: {name}",
"Reset {name}": "Atur ulang {name}",
"Updated “{name}”": "“{name}” diperbarui",
"{month}": "{month:02}",
"{month} {year}": "{month:02}/{year}",
"{verb} {n} Photo{}": "{verb} {n} foto{:.0}",
"{weekday}, {day}": "{weekday}, {day}",
"{weekday}, {day} {month} {year}": "{weekday}, {day:02}/{month:02}/{year}",
"{year}": "{year}",
"Remove “{name}” and its {count} photo{} from the library?": "Keluarkan “{name}” beserta {count} foto{:.0} di dalamnya dari pustaka?",
" ({unsure} with several look-alike files: use Locate)": " ({unsure} memiliki beberapa file serupa: gunakan “Temukan file hilang”)",
" · {:.0}% clipped": " · {:.0}% terpotong",
" · {failed} couldn't be rendered": " · {failed} gagal dirender",
" · {} original{} left at the source": " · {} file asli{:.0} tetap di sumber",
" — not carried over: {}{}": " — tidak ikut terbawa: {}{}",
"+{} more": "+{} lainnya",
"; {kept} already had a location": "; {kept} sudah memiliki lokasi",
"; {outside} outside its time range": "; {outside} di luar rentang waktunya",
"@{}: {} PRs, {} commits, +{} / −{} lines (Δ {}), +{} / −{} binary assets, {} – {}": "@{}: {} PR, {} commit, +{} / −{} baris (Δ {}), +{} / −{} aset biner, {} – {}",
"Added {what} to {name}": "{what} ditambahkan ke {name}",
"Auto Import: added {} photo{}": "Impor otomatis: {} foto{:.0} ditambahkan",
"Building previews for {} photos…": "Membuat pratinjau untuk {} foto…",
"Couldn't open the editor: {e}": "Tidak dapat membuka editor: {e}",
"Creates {} stacks from {} of {scope}": "Membuat {} tumpukan dari {} di antara {scope}",
"Done ({what}): {done} photo{plural}": "Selesai ({what}): {done} foto{plural:.0}",
"Focus {:.0}": "Fokus {:.0}",
"Folder “{}” (and its subfolders)": "Folder “{}” (beserta subfoldernya)",
"Found {n} missing photo{}; {left} still missing{unsure}": "Ditemukan {n} foto yang hilang{:.0}; {left} masih hilang{unsure}",
"Import failed: {e}": "Impor gagal: {e}",
"Import warnings ({n}):\n{warnings}": "Peringatan impor ({n}):\n{warnings}",
"XMP profile \"{profile}\" was not applied for {path}; colors may differ from Lightroom.": "Profil XMP \"{profile}\" tidak diterapkan untuk {path}; warna mungkin berbeda dari Lightroom.",
"Lightroom geometry was not preserved for {path}; {mode} will be recalculated.": "Geometri Lightroom untuk {path} tidak dipertahankan; {mode} akan dihitung ulang.",
"Lightroom geometry for {path} was not preserved; saved transform is ignored because Upright is Off.": "Geometri Lightroom untuk {path} tidak dipertahankan; transformasi tersimpan diabaikan karena Tegakkan nonaktif.",
"Lightroom geometry for {path} was not preserved; Guided Upright saved transform is unsupported.": "Geometri Lightroom untuk {path} tidak dipertahankan; transformasi Tegakkan terpandu yang tersimpan tidak didukung.",
"Lightroom geometry for {path} was not preserved; saved Upright transform is unsupported.": "Geometri Lightroom untuk {path} tidak dipertahankan; transformasi Tegakkan yang tersimpan tidak didukung.",
"XMP adjustments were not imported for {path}: {fields}.": "Penyesuaian XMP untuk {path} tidak diimpor: {fields}.",
"Imported {profiles} profile{} (Profile browser ▸ their groups)": "{profiles} profil diimpor{:.0} (Penjelajah profil → grup masing-masing)",
"Insert at the cursor (also written {})": "Sisipkan di posisi kursor (dapat juga ditulis {})",
"MIT OR Apache-2.0. Fonts: {} (OFL). Icons: original.": "MIT OR Apache-2.0. Font: {} (OFL). Ikon: orisinal.",
"Painting “{k}”: click photos to add or remove it · Esc stops": "Menerapkan “{k}”: klik foto untuk menambahkan atau menghapusnya · Esc untuk berhenti",
"Previews ready for {done} photo{plural}": "Pratinjau untuk {done} foto{plural:.0} sudah siap",
"Removed {what} from {name}": "{what} dihapus dari {name}",
"Renames “{from}” on {n} photo{} (keywords below it too). Use | for levels, e.g. Travel|Italy. An existing name merges the two.": "Mengganti nama “{from}” di {n} foto{:.0} (termasuk kata kunci di bawahnya). Gunakan | untuk tingkatan, mis. Liburan|Italia. Jika nama sudah ada, keduanya akan digabungkan.",
"Replace {} with:": "Ganti {} dengan:",
"Scores {scope} for focus and exposure and finds similar shots taken within seconds of each other (bursts).": "Menilai fokus dan eksposur {scope}, lalu mencari bidikan serupa yang diambil berselang beberapa detik (bidikan beruntun).",
"Tagged {n} photo{} from the tracklog": "{n} foto diberi lokasi dari log jejak GPS{:.0}",
"The SAM 3 download failed: {e}": "Gagal mengunduh SAM 3: {e}",
"Unknown tag{s} {} stay{} as typed (see Tags)": "Tag tak dikenal{s:.0} {0} tetap seperti yang diketik{1:.0} (lihat Tag)",
"Virtual copy “{name}” of {of}": "Salinan virtual “{name}” dari {of}",
"Working on {what} for {} photos…": "Memproses {what} untuk {} foto…",
"{label} {index} of {n}": "{label} {index} dari {n}",
"{name} opened for editing; it is stacked with the original": "{name} dibuka untuk diedit dan ditumpuk dengan file aslinya",
"{n} change{} couldn't be written to disk: {e}\nIf you quit now, {} lost.": "{n} perubahan{0:.0} tidak dapat ditulis ke disk: {e}\nJika Anda keluar sekarang, perubahan tersebut akan hilang{1:.0}.",
"{n} change{} saved in memory but not written to disk: {e} — LightCraft will retry": "{n} perubahan{:.0} tersimpan di memori, tetapi belum ditulis ke disk: {e} — LightCraft akan mencoba lagi",
"{pid} (missing)": "{pid} (hilang)",
"{{date:…}} directives: {}": "Direktif {{date:…}}: {}",
"{} contributors · {} commits": "{} kontributor · {} commit",
"{} failed": "{} gagal",
"{} item{}": "{} item{:.0}",
"{} photos · exposures {} EV": "{} foto · eksposur {} EV",
"{} photos · {} · {:.0}° × {:.0}°": "{} foto · {} · {:.0}° × {:.0}°",
"{} sample{}": "{} sampel{:.0}",
"{} sec": "{} dtk",
"{} — show their photos": "{} — tampilkan foto orang ini",
"LightCraft can't decode this raw variant yet ({}). You're editing the camera's embedded JPEG preview, which already includes the camera's picture style (e.g. Monochrome) and white balance.": "LightCraft belum dapat mendekode varian raw ini ({}). Anda sedang mengedit pratinjau JPEG bawaan kamera, yang sudah menyertakan gaya gambar kamera (mis. Monokrom) dan keseimbangan putihnya.",
"Filtered: {label}": "Difilter: {label}",
"{n} similar photo{} · View ▸ Clear Filters to see all": "{n} foto serupa{:.0} · Tampilan → Hapus filter untuk melihat semua",
"Moved; {} photo(s) relinked": "Dipindahkan; {} foto ditautkan ulang",
"{} label (click more labels to show any of them)": "Label {} (klik label lain untuk ikut menampilkan foto berlabel tersebut)",
", {} star{}": ", {} bintang{:.0}",
", {} label": ", label {}",
"Downloads from {host}. Once it has arrived and checked out, LightCraft installs it, {after}.": "Diunduh dari {host}. Setelah selesai diunduh dan lolos pemeriksaan, LightCraft akan menginstalnya, {after}.",
"{:.1} MB": "{:.1} MB",
"Running on the processor: {}.": "Berjalan di prosesor: {}.",
"{} · {}. Kept in the library's “denoise” folder; the oldest ones that no photo uses go first when the limit is reached.": "{} · {}. Disimpan di folder “denoise” pustaka; saat batas tercapai, file terlama yang tidak dipakai foto mana pun dihapus lebih dulu.",
"Denoising {name}: {done} of {total} tiles{}": "Mengurangi noise {name}: {done} dari {total} petak{}",
"{size} GB": "{size} GB",
"{} of {} from {host}": "{} / {} dari {host}",
"{} KB": "{} KB",
"{} MB": "{} MB",
"Making the denoised picture… {}%": "Membuat gambar hasil pengurangan noise… {}%",
"{name} · {terms}": "{name} · {terms}",
"{n} {noun}": "{n} {noun}",
"Trained on: {provenance}": "Dilatih dengan: {provenance}",
" · {queued} waiting": " · {queued} menunggu",
"Shows the model's terms, then downloads {} from {host}, installs it and starts using it. Nothing else is sent.": "Menampilkan ketentuan model, lalu mengunduh {} dari {host}, menginstalnya, dan mulai menggunakannya. Tidak ada data lain yang dikirim.",
"In line for AI Denoise ({ahead} ahead)…": "Dalam antrean pengurangan noise AI ({ahead} di depan)…",
"Running on {}.": "Berjalan di {}.",
"{file} · {size}": "{file} · {size}",
"• {assumption}": "• {assumption}",
"Put inside “{parent}”": "Masukkan ke “{parent}”",
"Delete the keyword “{name}”?": "Hapus kata kunci “{name}”?",
"It is taken off {count} photo{}, with the keywords below it. Undo brings it back.": "Kata kunci ini akan dilepas dari {count} foto{:.0}, beserta kata kunci di bawahnya. Urungkan untuk mengembalikannya.",
"Create Keyword Tag Inside “{name}”…": "Buat tag kata kunci di dalam “{name}”…",
"“{parent}” has a “{name}” already. Merge the two?": "“{parent}” sudah berisi “{name}”. Gabungkan keduanya?",
"There is a “{name}” at the top level already. Merge the two?": "Sudah ada “{name}” di tingkat teratas. Gabungkan keduanya?",
"Added “{keyword}” to {n} photo{}": "“{keyword}” ditambahkan ke {n} foto{:.0}",
"On {have} of {of} selected photos": "Pada {have} dari {of} foto yang dipilih",
"Remove “{name}”": "Hapus “{name}”",
"There is a keyword set “{name}” already": "Set kata kunci “{name}” sudah ada",
"Added {n} keyword{}": "{n} kata kunci ditambahkan{:.0}",
"; {n} gained synonyms": "; {n} mendapat sinonim baru",
"Exported {n} keyword{}": "{n} kata kunci diekspor{:.0}",
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One tidak akan mengimpor daftar yang kata kuncinya mengandung ; , < atau >: {names}",
" — left out, as a keyword list can't hold them: {names}": " — tidak disertakan karena tidak dapat dimasukkan ke daftar kata kunci: {names}",
"{done} of {total}": "{done} dari {total}",
"{done} of {total} MB": "{done} dari {total} MB",
"{pct} %": "{pct} %",
"+{n} more": "+{n} lainnya",
"Create the keyword “{name}”": "Buat kata kunci “{name}”",
"new, inside {parent}": "baru, di dalam {parent}",
"Keywords of {n} selected photos": "Kata kunci {n} foto yang dipilih",
"Removed “{name}” from {n} photos. Edit ▸ Undo puts it back.": "“{name}” dihapus dari {n} foto. Edit → Urungkan akan mengembalikannya.",
"1 task is still running: {names}. Quitting stops it.": "1 tugas masih berjalan: {names}. Jika keluar sekarang, tugas ini akan dihentikan.",
"{n} tasks are still running: {names}. Quitting stops them.": "{n} tugas masih berjalan: {names}. Jika keluar sekarang, semua tugas ini akan dihentikan.",
"{what} merge cancelled": "Penggabungan {what} dibatalkan",
"{n} Local photo{} skipped: albums hold library photos (Add to My Photos first)": "{n} foto Lokal{:.0} dilewati: album hanya dapat berisi foto pustaka (tambahkan ke Foto saya terlebih dahulu)",
"New Folder in “{name}”": "Folder baru di “{name}”",
"Moved {} photo(s)": "{} foto dipindahkan",
"not added to Apple Photos: {e}": "tidak ditambahkan ke Apple Photos: {e}",
"{n} added to Apple Photos album “{album}”": "{n} ditambahkan ke album Apple Photos “{album}”",
"{n} added to Apple Photos": "{n} ditambahkan ke Apple Photos",
"{n} photos opened for editing; each is stacked with its original": "{n} foto dibuka untuk diedit; masing-masing ditumpuk dengan file aslinya",
" in {g} group{}": " dalam {g} grup{:.0}",
" · kept, not applied: {}": " · dipertahankan, tidak diterapkan: {}",
" — kept but not applied: {}": " — disimpan, tetapi tidak diterapkan: {}",
"Apply to “{mask}” on Selected Photos": "Terapkan ke “{mask}” pada foto yang dipilih",
"Delete the effect preset “{name}”?": "Hapus preset efek “{name}”?",
"Delete these {n} effect presets?": "Hapus {n} preset efek ini?",
"Delete {n} effect preset{}? This can't be undone.": "Hapus {n} preset efek{:.0}? Tindakan ini tidak dapat diurungkan.",
"Effect: {name}": "Efek: {name}",
"Exported {} effect preset{}": "{} preset efek diekspor{:.0}",
"Imported {e} mask effect{} (Masking ▸ Effect)": "{e} efek masker{:.0} diimpor (Masker → Efek)",
"Update “{name}” with Current Settings": "Perbarui “{name}” dengan pengaturan saat ini",
"{n} preset{}": "{n} preset{:.0}",
"{name} opened in the other editor": "{name} dibuka di editor lain",
"{n} originals opened in the other editor": "{n} file asli dibuka di editor lain",
"· {in_source} identical copies": "· {in_source} salinan identik",
"{count} more warnings.": "{count} peringatan lainnya.",
"{photos} catalog records processed; {collections} collections.": "{photos} entri katalog diproses; {collections} koleksi.",
"{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "{missing} file asli hilang. Data katalognya masih tersedia; gunakan “Cari foto hilang” untuk menautkannya ulang.",
"Applied {name}": "{name} diterapkan",
"Delete “{name}”?": "Hapus “{name}”?",
"Updated {name}": "{name} diperbarui",
"Exported to {path}": "Diekspor ke {path}",
"Saved template {name}": "Templat {name} disimpan",
"Imported {name}": "{name} diimpor",
"Skipped {problem}": "Dilewati: {problem}",
"…and {more} more": "…dan {more} lainnya",
"Template preview: {name}": "Pratinjau templat: {name}",
"{n} photos in view · {slides} slides · {time}": "{n} foto dalam tampilan · {slides} slide · {time}",
"{n} pages": "{n} halaman",
"{n} JPEG files": "{n} file JPEG",
"{s} s of AV1 video": "Video AV1 {s} detik",
"Slideshow exported: {what}": "Tayangan slide diekspor: {what}",
" · {n} skipped (could not be rendered)": " · {n} dilewati (tidak dapat dirender)",
"{n} raws can now be developed from their raw data": "{n} file raw kini dapat diolah dari data raw-nya",
"LightCraft stopped unexpectedly: {e}\n\nThe log file has the details: {path}": "LightCraft berhenti tiba-tiba: {e}\n\nDetailnya ada di file log: {path}",
"LightCraft stopped unexpectedly: {e}": "LightCraft berhenti tiba-tiba: {e}",
"Renames {n} face{} across the library. The files are not changed.": "Mengganti nama {n} wajah{:.0} di seluruh pustaka. File tidak diubah.",
"“{to}” is already a person. Combining them makes one person; Undo separates them again.": "“{to}” sudah terdaftar sebagai orang. Jika digabungkan, keduanya menjadi satu orang; Urungkan akan memisahkannya lagi.",
"{what} merge added at {mp}: its full size, {full}, needs more memory than this computer has": "Hasil penggabungan {what} ditambahkan dengan resolusi {mp}: resolusi penuhnya ({full}) memerlukan memori melebihi kapasitas komputer ini",
"{what} merge added at {mp}, the largest a panorama can be (its full size is {full})": "Hasil penggabungan {what} ditambahkan dengan resolusi {mp}, resolusi maksimum untuk panorama (resolusi penuhnya {full})"
}
File diff suppressed because it is too large Load Diff
+41
View File
@@ -130,6 +130,7 @@ language_table! {
Cs, "cs", "Čeština", "Latn", include_str!("../locales/cs.json"); Cs, "cs", "Čeština", "Latn", include_str!("../locales/cs.json");
It, "it", "Italiano", "Latn", include_str!("../locales/it.json"); It, "it", "Italiano", "Latn", include_str!("../locales/it.json");
Ka, "ka", "ქართული", "Geor", include_str!("../locales/ka.json"); Ka, "ka", "ქართული", "Geor", include_str!("../locales/ka.json");
Id, "id", "Bahasa Indonesia", "Latn", include_str!("../locales/id.json");
} }
// The settings file stores the BCP-47 code (`"zh-hans"`), never the Rust variant name, so a // The settings file stores the BCP-47 code (`"zh-hans"`), never the Rust variant name, so a
@@ -534,6 +535,45 @@ mod tests {
set_language(Locale::En); set_language(Locale::En);
} }
#[test]
fn indonesian_switches_persists_formats_dates_and_preserves_values() {
use crate::control::{ControlRequest, Outcome};
let mut app = crate::LightcraftApp::new(lightcraft_engine::Session::with_demo(), Default::default());
app.run("app.language.indonesian", serde_json::json!({})).unwrap();
assert_eq!(app.ui.language, Locale::Id);
assert_eq!(Locale::Id.name(), "Bahasa Indonesia");
assert_eq!(Locale::Id.script(), "Latn");
assert_eq!(crate::menubar::checked(&app, "app.language.indonesian"), Some(true));
assert_eq!(tr("Exposure"), "Eksposur");
assert_eq!(tr("My Photos"), "Foto saya");
assert_eq!(date_group_label("2026-09-20", false), "Minggu, 20/09/2026");
assert_eq!(date_group_label("2026-09-20", true), "Minggu, 20");
assert_eq!(date_group_label("2026-09", false), "09/2026");
assert_eq!(date_group_label("2026", false), "2026");
assert_eq!(display_time("2026-09-20T16:04:05"), "Minggu, 20/09/2026 16:04:05");
// Indonesian keeps a noun unchanged after a count ("1 foto", "22 foto"), so the English
// plural suffix is consumed and never shows.
for n in [0, 1, 2, 5, 11, 22] {
assert_eq!(tr_format!("Imported {} photo{}", n, if n == 1 { "" } else { "s" }), format!("{n} foto diimpor"));
}
let name = String::from("Color 1234.56 {title} 日本語");
assert_eq!(tr_format!("Added {n} photo{} to “{}”", "s", name, n = 22), format!("22 foto ditambahkan ke “{name}”"));
assert_eq!(builtin_label("Warm Glow", false), "Warm Glow");
assert_eq!(tr("photo-{title}.jpg"), "photo-{title}.jpg");
let saved = serde_json::to_string(&app.ui).unwrap();
assert!(saved.contains(r#""language":"id""#));
assert_eq!(serde_json::from_str::<crate::state::UiState>(&saved).unwrap().language, Locale::Id);
let ctx = egui::Context::default();
for tag in ["id", "id-ID", "id_ID.UTF-8", "id-Latn-ID"] {
assert_eq!(Locale::parse_tag(tag), Some(Locale::Id));
let (request, _) = ControlRequest::new("ui.set", serde_json::json!({"language": tag}));
let Outcome::Done(reply) = crate::control::handle(&mut app, &ctx, &request) else { panic!("expected reply") };
assert_eq!(reply["ok"], true);
assert_eq!(language(), Locale::Id);
}
set_language(Locale::En);
}
#[test] #[test]
fn sentence_case_keeps_georgian_lowercase() { fn sentence_case_keeps_georgian_lowercase() {
assert_eq!(upper_first("its input is not an image"), "Its input is not an image"); assert_eq!(upper_first("its input is not an image"), "Its input is not an image");
@@ -790,6 +830,7 @@ mod tests {
("app.language.czech", Locale::Cs), ("app.language.czech", Locale::Cs),
("app.language.italian", Locale::It), ("app.language.italian", Locale::It),
("app.language.georgian", Locale::Ka), ("app.language.georgian", Locale::Ka),
("app.language.indonesian", Locale::Id),
]; ];
// One command per language, and every command reachable from the menu table. // One command per language, and every command reachable from the menu table.
assert_eq!(commands.len(), Locale::ALL.len()); assert_eq!(commands.len(), Locale::ALL.len());
+2
View File
@@ -28,6 +28,7 @@ pub const LANGUAGE_COMMANDS: &[UiCommand] = &[
("app.language.czech", crate::i18n::Locale::Cs.name(), None, "Edit>Language"), ("app.language.czech", crate::i18n::Locale::Cs.name(), None, "Edit>Language"),
("app.language.italian", crate::i18n::Locale::It.name(), None, "Edit>Language"), ("app.language.italian", crate::i18n::Locale::It.name(), None, "Edit>Language"),
("app.language.georgian", crate::i18n::Locale::Ka.name(), None, "Edit>Language"), ("app.language.georgian", crate::i18n::Locale::Ka.name(), None, "Edit>Language"),
("app.language.indonesian", crate::i18n::Locale::Id.name(), None, "Edit>Language"),
]; ];
/// Every UI command: the languages, then everything else. `xtask parity` reads both tables from /// Every UI command: the languages, then everything else. `xtask parity` reads both tables from
@@ -54,6 +55,7 @@ pub fn language_from_command(id: &str) -> Option<crate::i18n::Locale> {
"app.language.czech" => Some(crate::i18n::Locale::Cs), "app.language.czech" => Some(crate::i18n::Locale::Cs),
"app.language.italian" => Some(crate::i18n::Locale::It), "app.language.italian" => Some(crate::i18n::Locale::It),
"app.language.georgian" => Some(crate::i18n::Locale::Ka), "app.language.georgian" => Some(crate::i18n::Locale::Ka),
"app.language.indonesian" => Some(crate::i18n::Locale::Id),
_ => None, _ => None,
} }
} }
+2 -2
View File
@@ -621,13 +621,13 @@ mod tests {
"Export Keywords…", "Export Keywords…",
]; ];
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("locales"); let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("locales");
for code in ["de", "es", "fr", "pt-br", "ru", "uk", "it", "ka", "ja", "zh-hans", "zh-hant", "pl", "cs"] { for code in ["de", "es", "fr", "pt-br", "ru", "uk", "it", "ka", "ja", "zh-hans", "zh-hant", "pl", "cs", "id"] {
let text = std::fs::read_to_string(dir.join(format!("{code}.json"))).unwrap(); let text = std::fs::read_to_string(dir.join(format!("{code}.json"))).unwrap();
let catalog: std::collections::HashMap<String, String> = serde_json::from_str(&text).unwrap(); let catalog: std::collections::HashMap<String, String> = serde_json::from_str(&text).unwrap();
for label in labels { for label in labels {
assert!(catalog.contains_key(label), "{code}: “{label}”"); assert!(catalog.contains_key(label), "{code}: “{label}”");
} }
} }
assert_eq!(Locale::ALL.len(), 14, "a language added: list its file above"); assert_eq!(Locale::ALL.len(), 15, "a language added: list its file above");
} }
} }
+63
View File
@@ -0,0 +1,63 @@
# LightCraft in Indonesian
Choose **Edit → Language → Bahasa Indonesia** or **Settings → General → Language**. The choice applies
at once and is saved for the next launch (`language: "id"` in `ui.json`); the control command is
`app.language.indonesian`. `LIGHTCRAFT_LANGUAGE` selects it for headless runs and accepts `id`, `id-ID`
and `id_ID.UTF-8`; the saved value is always `id`. LightCraft does not follow the system language, for
Indonesian as for every other language.
The Indonesian catalogs cover every plain and formats message the other catalogs have (2,258 + 297),
plus 69 command labels no catalog translated yet: menus, shortcuts, library, editing, crop, masks,
people, import/export, print, slideshow, settings, built-in presets and profiles, history, date
headings, status messages and the smart-album rule editor. The embedded release notes (What's New)
stay in English. Technical errors from lower layers stay in English, as in the other languages.
The catalogs were written with an agent from the meaning of the English labels and reviewed for
natural, standard app Indonesian; 356 labels reuse the Indonesian wording of the sibling apps
(PhotoCraft, VectorCraft, FilmCraft, FigCraft) so the suite reads the same. A native speaker's pass
is welcome.
File names, command ids, template placeholders and albums, presets or metadata you named yourself are
not translated.
## Style
- Sentence case everywhere, as in the English; names, brands and abbreviations keep their capitals.
- Buttons and menu commands use the bare verb (Simpan, Hapus, Ekspor, Pangkas); progress lines use
me- with an ellipsis (Mengimpor…); finished-state toasts put the subject first (`{n} foto diekspor`).
- The user is addressed as *Anda*, only where a sentence needs a subject; never *kamu*.
- Indonesian keeps a noun unchanged after a count (`3 foto`), so English plural endings are dropped
with `{:.0}`.
- Dates read `Minggu, 20/09/2026` (`{weekday}, {day:02}/{month:02}/{year}`), months `09/2026`.
- Delete is **Hapus**; remove from a container (album, stack, favorites) is **Keluarkan dari …**.
Select is **Pilih**; the Pick flag is **Pilihan**.
- `▸` and `▾` are written `→` and `⌄`, as in the other catalogs.
## Terminology
| Source | Indonesian |
|---|---|
| Develop | Olah |
| File / Edit / View | File / Edit / Tampilan |
| Settings / General / Language | Pengaturan / Umum / Bahasa |
| Import / Export | Impor / Ekspor |
| Photos / Albums / Folders | Foto / Album / Folder |
| Smart Album | Album pintar |
| Crop | Pangkas |
| Masks / Masking | Masker |
| Exposure / Contrast | Eksposur / Kontras |
| Highlights / Shadows / Whites / Blacks | Sorotan / Bayangan / Putih / Hitam |
| Temperature / Tint | Suhu / Rona |
| Vibrance / Saturation | Vibrance / Saturasi |
| Clarity / Texture / Dehaze | Kejernihan / Tekstur / Hilangkan kabut |
| Sharpening / Noise Reduction | Penajaman / Pengurangan noise |
| Vignette / Grain | Vinyet / Butiran |
| White Balance / Tone Curve | Keseimbangan putih / Kurva tone |
| Lens Corrections | Koreksi lensa |
| Presets / Profile | Preset / Profil |
| Keywords / Rating | Kata kunci / Rating |
| Flag / Pick / Reject | Tanda / Pilihan / Tolak |
| Stack / Virtual Copy | Tumpukan / Salinan virtual |
| History / Recently Deleted | Riwayat / Baru dihapus |
| Compare / Before / After | Bandingkan / Sebelum / Sesudah |
| Slideshow / Print / Template | Tayangan slide / Cetak / Templat |
| People | Orang |
+5 -3
View File
@@ -1,12 +1,12 @@
# Localization parity # Localization parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (de, es, fr, ja, uk catalogs filled; re-measured; Italian and Georgian interface catalogs added) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 > **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-19 · **Change:** minor (Indonesian interface catalog added; de, es, fr, ja, uk catalogs filled; re-measured; Italian and Georgian interface catalogs added) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
How far each interface language is from `full`. How to add or maintain a language is in How far each interface language is from `full`. How to add or maintain a language is in
[localization.md](localization.md); per-language notes are in `localization-<code>.md` [localization.md](localization.md); per-language notes are in `localization-<code>.md`
([zh-hans](localization-zh-hans.md), [zh-hant](localization-zh-hant.md), [ja](localization-ja.md), ([zh-hans](localization-zh-hans.md), [zh-hant](localization-zh-hant.md), [ja](localization-ja.md),
[pt-br](localization-pt-br.md), [es](localization-es.md), [de](localization-de.md), [pt-br](localization-pt-br.md), [es](localization-es.md), [de](localization-de.md),
[ru](localization-ru.md), [ka](localization-ka.md)). [ru](localization-ru.md), [ka](localization-ka.md), [id](localization-id.md)).
## The target ## The target
@@ -58,7 +58,7 @@ that is promising but is not recorded as review, so the column says `no (communi
| 11 | Korean | `ko` | 0 (0%) | none | **no Korean (Hangul) face**: needs a non-Adobe OFL face in craft-fonts (Noto CJK is allowed only for Chinese) | no | none | 14–22 h | | 11 | Korean | `ko` | 0 (0%) | none | **no Korean (Hangul) face**: needs a non-Adobe OFL face in craft-fonts (Noto CJK is allowed only for Chinese) | no | none | 14–22 h |
| 12 | Vietnamese | `vi` | 0 (0%) | none | Latin with stacked diacritics (Inter covers it; check combining marks) | no | none (target: not shipped) | 10–16 h | | 12 | Vietnamese | `vi` | 0 (0%) | none | Latin with stacked diacritics (Inter covers it; check combining marks) | no | none (target: not shipped) | 10–16 h |
## Other languages LightCraft ships (4) ## Other languages LightCraft ships (5)
| Language | Code | UI strings translated | Script | Native review | Status | To `full` | | Language | Code | UI strings translated | Script | Native review | Status | To `full` |
|---|---|---:|---|---|---|---:| |---|---|---:|---|---|---|---:|
@@ -67,6 +67,7 @@ that is promising but is not recorded as review, so the column says `no (communi
| Ukrainian | `uk` | 2,383 / 2,398 (99%; 2026-10-12) | Cyrillic (Inter) | no (community edits) | partial (lower-layer errors English) | 2–4 h | | Ukrainian | `uk` | 2,383 / 2,398 (99%; 2026-10-12) | Cyrillic (Inter) | no (community edits) | partial (lower-layer errors English) | 2–4 h |
| Italian | `it` | 2,201 / 2,398 (92%, measured 2026-10-12; the rest is What's New) | Latin (Inter) | no (written with an agent; native-speaker pass pending) | partial (lower-layer errors English) | 3–5 h | | Italian | `it` | 2,201 / 2,398 (92%, measured 2026-10-12; the rest is What's New) | Latin (Inter) | no (written with an agent; native-speaker pass pending) | partial (lower-layer errors English) | 3–5 h |
| Georgian | `ka` | 2,398 / 2,398 (100%: every plain and formats message any catalog has on 2026-10-12, plus the 27 display labels and 397 What's New lines no other catalog translates) | Geor: no craft-fonts face yet (boxes until one is available) | yes (native speaker) | partial (lower-layer errors English) | 2–4 h (+ a Georgian face) | | Georgian | `ka` | 2,398 / 2,398 (100%: every plain and formats message any catalog has on 2026-10-12, plus the 27 display labels and 397 What's New lines no other catalog translates) | Geor: no craft-fonts face yet (boxes until one is available) | yes (native speaker) | partial (lower-layer errors English) | 2–4 h (+ a Georgian face) |
| Indonesian | `id` | every plain (2,258) and formats (297) message any catalog has on 2026-10-19, plus 69 command labels no other catalog translates; What's New left in English | Latin (Inter) | no (written with an agent; native-speaker pass welcome) | partial (lower-layer errors English) | 2–4 h |
Target languages LightCraft lacks: Korean, Dutch, Polish, Swedish, Norwegian Bokmål, Thai Target languages LightCraft lacks: Korean, Dutch, Polish, Swedish, Norwegian Bokmål, Thai
(Thai needs shaping and line breaking without spaces). Each Latin-script one is ~10–16 h; Thai (Thai needs shaping and line breaking without spaces). Each Latin-script one is ~10–16 h; Thai
@@ -93,6 +94,7 @@ Target languages LightCraft lacks: Korean, Dutch, Polish, Swedish, Norwegian Bok
| Date | Change | Summary | | Date | Change | Summary |
|---|---|---| |---|---|---|
| 2026-10-19 | minor | Indonesian interface catalog (`id`): every plain and formats message any catalog has, plus 69 command labels; What's New left in English |
| 2026-10-12 | minor | Italian interface catalog (`it`): 2,201 of 2,398 messages, What's New left in English | | 2026-10-12 | minor | Italian interface catalog (`it`): 2,201 of 2,398 messages, What's New left in English |
| 2026-10-11 | minor | Shared work: the interface decides on error kinds, not English error text (#1046) | | 2026-10-11 | minor | Shared work: the interface decides on error kinds, not English error text (#1046) |
| 2026-10-12 | minor | Georgian (`ka`) row: every catalog message | | 2026-10-12 | minor | Georgian (`ka`) row: every catalog message |
+2 -2
View File
@@ -2,11 +2,11 @@
The interface ships in the language it is written in (English) plus every language in the table in The interface ships in the language it is written in (English) plus every language in the table in
`crates/ui-egui/src/i18n.rs`. Today that is English, Simplified Chinese (`zh-hans`), Traditional `crates/ui-egui/src/i18n.rs`. Today that is English, Simplified Chinese (`zh-hans`), Traditional
Chinese (`zh-hant`, Taiwan), Japanese (`ja`), Brazilian Portuguese (`pt-br`), Spanish (`es`), German (`de`), Russian (`ru`), French (`fr`), Ukrainian (`uk`), Czech (`cs`) and Georgian (`ka`). This file is the reference for **adding or maintaining a language**; the per-language notes Chinese (`zh-hant`, Taiwan), Japanese (`ja`), Brazilian Portuguese (`pt-br`), Spanish (`es`), German (`de`), Russian (`ru`), French (`fr`), Ukrainian (`uk`), Czech (`cs`), Georgian (`ka`) and Indonesian (`id`). This file is the reference for **adding or maintaining a language**; the per-language notes
are in [`localization-zh-hans.md`](localization-zh-hans.md), [`localization-zh-hant.md`](localization-zh-hant.md), are in [`localization-zh-hans.md`](localization-zh-hans.md), [`localization-zh-hant.md`](localization-zh-hant.md),
[`localization-ja.md`](localization-ja.md), [`localization-pt-br.md`](localization-pt-br.md), [`localization-es.md`](localization-es.md), [`localization-ja.md`](localization-ja.md), [`localization-pt-br.md`](localization-pt-br.md), [`localization-es.md`](localization-es.md),
[`localization-de.md`](localization-de.md), [`localization-ru.md`](localization-ru.md), [`localization-de.md`](localization-de.md), [`localization-ru.md`](localization-ru.md),
[`localization-cs.md`](localization-cs.md) and [`localization-ka.md`](localization-ka.md). [`localization-cs.md`](localization-cs.md), [`localization-ka.md`](localization-ka.md) and [`localization-id.md`](localization-id.md).
## Adding a language ## Adding a language