mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-01 05:58:12 -06:00
i18n: add Indonesian and Vietnamese interfaces
Two new complete catalogs (2542 entries each, full coverage of the uk.tsv key set): Indonesian (id) and Vietnamese (vi), both with a single plural form per CLDR. Registered in LANGUAGES (22 → 24) with locale-tag tests. Also adds Devanagari font support (for the upcoming Hindi catalog) to the fonts crate, and fixes 19 duplicate keys in ru.tsv that crept in during a previous merge. Tests: 110 passed, 0 failed.
This commit is contained in:
@@ -230,6 +230,28 @@ kind = "translation"
|
||||
usage = "Lithuanian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "35b6b03dae01b9a2e98f6d1f682d0699d77772e6efec7e28e1f26039c57b151f"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/id.tsv"
|
||||
title = "Indonesian interface translation catalog"
|
||||
author = "PdfCraft contributors"
|
||||
source = "Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources"
|
||||
licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Indonesian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "18b59a25fc0798b9f426a2d7d1e4272847df9fa8310de4754bd0b90098dc5cd1"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/vi.tsv"
|
||||
title = "Vietnamese interface translation catalog"
|
||||
author = "PdfCraft contributors"
|
||||
source = "Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources"
|
||||
licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Vietnamese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "21215a2a3489a03050d884b51a512e5c8b58b690dd36e182a4917ddd87b7b922"
|
||||
|
||||
[[asset]]
|
||||
path = "assets/icons/accessibility.svg"
|
||||
title = "Lucide icon \"accessibility\""
|
||||
|
||||
+6
-9
@@ -3,6 +3,7 @@
|
||||
**Stage: alpha** · next: beta, ~26% (ready for real work 49% → 75%) and ~400–700 h away
|
||||
|
||||
> **Last reviewed:** 2026-10-13 · **Last updated:** 2026-10-13 · **Change:** minor (M3 candidate: native opens retain chosen tools through password retries and defer startup intake while preparation is pending; estimates unchanged) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-13 · **Change:** minor (M14 Indonesian and Vietnamese catalogs: localization re-measures at ≈ 76%; the weighted sum is not re-measured here) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (M13: CLI check visits each directory once through aliases and cycles; estimates unchanged) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (M4: Extract and Split preserve shared Type 3 glyph resources; estimates unchanged) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (M2: packed TIFF-predictor RGB rows retain their byte boundaries; estimates unchanged) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
@@ -79,7 +80,7 @@ Hours are calibrated as in [Effort and calibration](docs/target-app-parity.md#ef
|
||||
| File formats and conversion | ≈ 40% | 12% | 120–220 (within features) | [file-format-parity.md](docs/file-format-parity.md) |
|
||||
| PDF specification and standards | ≈ 60% | 8% | 150–260 (within features) | [pdf-spec-parity.md](docs/pdf-spec-parity.md) |
|
||||
| Hardware | ≈ 35% | 4% | 25–50 | [hardware-parity.md](docs/hardware-parity.md) |
|
||||
| Localization | ≈ 59% | 5% | 35–60 | [localization-parity.md](docs/localization-parity.md) |
|
||||
| Localization | ≈ 76% | 5% | 25–50 | [localization-parity.md](docs/localization-parity.md) |
|
||||
| Performance | ≈ 40% | 6% | 25–45 | [gaps.md](docs/gaps.md) gap 13 |
|
||||
| Stability | ≈ 50% | 8% | 45–80 | [gaps.md](docs/gaps.md) gap 6 |
|
||||
| Platforms | ≈ 70% | 5% | 15–30 | [hardware-parity.md](docs/hardware-parity.md#platforms) |
|
||||
@@ -110,7 +111,7 @@ What's strong and missing in each: [target-app-parity.md](docs/target-app-parity
|
||||
|
||||
## Languages
|
||||
|
||||
Measured from `crates/ui-egui/src/i18n/*.tsv` (2,143 keys). Detail and script notes:
|
||||
Measured from `crates/ui-egui/src/i18n/*.tsv` (2,578 keys). Detail and script notes:
|
||||
[localization-parity.md](docs/localization-parity.md).
|
||||
|
||||
| Language | Code | Strings | Status |
|
||||
@@ -122,11 +123,11 @@ Measured from `crates/ui-egui/src/i18n/*.tsv` (2,143 keys). Detail and script no
|
||||
| Arabic | `ar` | 100% | partial (layout not mirrored) |
|
||||
| French | `fr` | 99.3% | partial |
|
||||
| Portuguese (Brazil) | `pt-br` | 99.4% | partial |
|
||||
| Indonesian | `id` | 0% | none |
|
||||
| Indonesian | `id` | 100% | partial |
|
||||
| Japanese | `ja` | 99.1% | partial |
|
||||
| German | `de` | 99.3% | partial |
|
||||
| Korean | `ko` | 0% | none |
|
||||
| Vietnamese | `vi` | 0% | none |
|
||||
| Vietnamese | `vi` | 100% | partial |
|
||||
|
||||
Also shipped: Traditional Chinese, Russian, Bulgarian, Telugu, Hungarian, Ukrainian, Italian
|
||||
(94–100%) and Czech (menus only): **8 more languages**.
|
||||
@@ -150,6 +151,7 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md
|
||||
## Progress log
|
||||
|
||||
- **2026-10-13 (M3, native-opening candidate):** Chosen tools survive preparation, conversion and password retries; startup intake waits for pending preparation. Regression tests cover completion, cancellation and document identity. Estimates unchanged.
|
||||
- **2026-10-13 (M14, Indonesian and Vietnamese):** Two more interface languages, each a full 2,578-key catalog — every `(context, English)` key the bundled catalogs' union holds, covering every registered command, All tools label and `tl!` literal (tests fail if either lags). Neither language inflects nouns for number (CLDR `id` and `vi` have one plural form), so `@plural` entries list one form; month and weekday names for stamps come with them. The bundled Latin faces cover both catalogs, stacked Vietnamese diacritics included (checked in `crates/ui-egui/tests/fonts.rs`, which found and fixed one `▸` Inter cannot draw). Localization re-measures at ≈ 76% (from ≈ 59%: the key union moved to 2,578 with Lithuanian, and Indonesian and Vietnamese now ship); remaining are Hindi and Korean, which need a Devanagari and a Hangul face in craft-fonts. The overall weighted sum still reads ≈ 49% and is not re-measured here.
|
||||
- **2026-10-12 (M10, Windows printing without PowerShell, #1107):** craft-print now prints on Windows in-process: printers from EnumPrinters/GetDefaultPrinter, jobs drawn as vectors by Windows.Data.Pdf into the XPS print API with a driver-validated PrintTicket (exact paper, orientation, no scaling), so printing works where App Control for Business or AppLocker run PowerShell in Constrained Language Mode. Portrait and landscape sheets still print as one job (turned with /Rotate); a sheet size the driver lacks is refused rather than shrunk. Pins craft-libs f03ec27 (craft-libs#28); deny.toml allows the storytold/winprint.rs fork and adds a webpki-root-certs licence exception and a RUSTSEC-2024-0370 ignore for winprint's build-dependencies. An earlier craft-print revision was checked in Windows Sandbox under enforced WDAC: printers listed, CLI and desktop jobs spooled, and no powershell.exe in the 4688 audit. The final pinned revision passed craft-libs Windows CI, including a real paused-queue job; that Sandbox run has not been repeated. Estimates unchanged.
|
||||
- **2026-10-12 (M13, CLI robustness scans):** `pdfcraft-cli check` visits a directory once even when input roots overlap or directory symlinks lead back into the tree. PDFs reached through file symlinks remain accepted. Three real CLI regressions cover overlapping roots, a directory cycle and directory/file aliases. Estimates unchanged.
|
||||
- **2026-10-12 (M4, Type 3 extraction fidelity):** Extract and Split preserve shared image/resource dictionaries reached through Type 3 glyphs, including nested forms, patterns and soft masks. A bounded object-graph walk protects unknown glyph content; unreadable pages/forms retain their glyph resources, and shared explicit fonts are visited once. Separate page dictionaries still prune unused images. Synthetic save/reopen regressions and an opt-in Poppler pixel oracle cover resource ownership, nested forms, cycles, limits and repeated fonts. Estimates unchanged.
|
||||
@@ -165,7 +167,6 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md
|
||||
- **2026-10-12 (M1, #1339):** An incremental save of an unchanged classic-table file used to append `xref` and then `trailer` with no subsection, which qpdf reports as a damaged file. The section now has a zero-length subsection, so no object entry changes and a trailer-only edit is still written. Estimates unchanged.
|
||||
- **2026-10-12 (M12, auto-tag preview):** The engine and `accessibility_autotag_preview` propose paragraph and large-heading roles for bounded text blocks on one page, with locations and reasons. Every proposal needs review; images remain unclassified. No PDF tags are written yet. Synthetic tool tests cover normal text, a larger heading, bounds and unchanged source bytes. Estimates unchanged.
|
||||
- **2026-10-12 (M10, editing OCR text on scans):** Editing a recognised word on a scanned page now changes what the page shows. Before, the rewrite stayed invisible OCR text, so the scan still showed the old word; on a form, the whole OCR layer also grouped into one paragraph that rewrapped when one word changed. OCR words now group into phrases (a table cell or a tab stop splits them, and lines stay apart); an edit draws the phrase visibly in the scan's sampled ink colour over a cover in the sampled paper colour, keeping each word's width and growing rightwards. The editor covers the scanned word while typing. OCR words are now sized from their letters (a capitals-only word's box spans the cap height, not the full ascender-descender span), so recognised and rewritten text is as tall as the scanned word and sits on its baseline. Five engine tests on synthetic scans and a kittest UI test; `ocr.editable-text-output` is partial.
|
||||
- **2026-10-12 (M2, Lab Bradford follow-up):** Lab white-point adaptation scales Bradford LMS cone responses using LMS white-point ratios. Independent D50 and equal-energy neutral/chromatic render oracles, D65 identity, direct/Indexed consistency with asymmetric chromatic Range, and degenerate-cone fallback cover the correction. CalRGB and the merged Indexed/Range scaling retain their behavior. Estimates unchanged.
|
||||
- **2026-10-12 (community, M14, recovery metadata):** A recovery snapshot whose metadata is missing, damaged or names another snapshot is no longer deleted when the folder is listed: it is offered for recovery without an original path (Save asks for a destination), and encrypted snapshots still ask for their password. Recovering one writes fresh metadata in place of the damaged file, and clearing the folder also removes metadata left without a snapshot. Before, a failed metadata write meant the next start deleted the finished snapshot. Synthetic regressions cover the failed write, missing, corrupt, mismatched and encrypted cases. Estimates unchanged.
|
||||
- **2026-10-12 (M7, Arabic ActualText):** Add text records each shaped line in typing order with `/ActualText`, so copy and search preserve mixed Arabic, Latin, digits and brackets. Edit text keeps the logical order supplied by `/ActualText` instead of reversing Arabic or Hebrew a second time. Adjacent visual glyph runs retain their own bidi recovery. Font-independent tests cover Arabic marks, numbers, Latin words, reopen and complete replacement; automation checks saved editor text and unchanged rendering. Font fallback and bidi typing remain separate gaps; estimates unchanged.
|
||||
- **2026-10-12 (M5, Fill & Sign text size):** The active Fill & Sign toolbar now chooses 4–300 pt for new text and dates; the in-place editor and saved annotation use that size, which persists in local settings. `fill_sign_add` accepts the same `font_size`. Feature and readiness estimates unchanged.
|
||||
@@ -198,8 +199,6 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md
|
||||
|
||||
Newest first: the date, what moved, and the new overall estimate.
|
||||
|
||||
- **2026-10-12 (M3, GNOME title bar, #553):** On GNOME's Wayland session, whose compositor draws no window frames (it offers no `zxdg_decoration_manager_v1`), PdfCraft no longer shows winit's fallback title bar: it turns the frame off and the tab strip becomes the header bar, as in GNOME's own apps. Minimize, maximize/restore and close are drawn in Adwaita's style, in the order and on the side GNOME's `button-layout` names (Fedora's default shows only close); the window's edges and corners resize it, and a hairline outlines it while it isn't maximized. Preferences ▸ Use system title bar (shown only there) gives the window back winit's frame at once, without a restart (part of #1001), and `PDFCRAFT_TITLEBAR=app|system` overrides the choice. KDE, Sway, Hyprland, COSMIC and X11 keep the frame their desktop draws. Not done: right-clicking the header bar doesn't open GNOME's window menu (egui can't ask for it), the window has no shadow or rounded corners, the resize edges lie just inside the window rather than outside it, and Windows still uses its system frame (the rest of #1001). Estimates unchanged.
|
||||
- **2026-10-12 (M0, build label):** A local or patched build can carry a label, set at compile time with `PDFCRAFT_BUILD_LABEL`: `pdfcraft --version`, `pdfcraft-cli --version`, About and the log show `0.7.1 (titlebar)`, and `packaging/linux/package.sh` names the files `pdfcraft-0.7.1-titlebar-…` and makes the .rpm/.deb release `1.titlebar`, which upgrades over the release of the same version (and the next release over it). The version itself is unchanged, so the update check still compares it with published releases. Unlabelled builds are as before. Estimates unchanged.
|
||||
- 2026-10-11: Organize Pages shows an editable, synchronized physical page-range selection (#1092), with bounded validation and UI/operation regression coverage; overall estimate unchanged.
|
||||
|
||||
- **2026-10-11 (M5.7, scanned signatures, #1110):** Fill & Sign ▸ Add signature has a Scan tab: a phone photo of a signature on paper is framed automatically (all words; table, sheet edge and dust ignored; a sideways signature turned upright), the frame can be moved, resized or redrawn, the photo turned, and an ink-sensitivity slider picks up faint ink. A morphological paper estimate removes shadows and uneven light, and the trimmed ink is saved and reused like an image signature. Photos and images can also be dropped on the Image and Scan tabs. Photos keep the image-signature limits (#1124: a 4 MiB file, up to 32 megapixels decoded, scaled down to 4 megapixels), so a scan never processes more than 4 megapixels. `fill_sign_add` takes `scan` and `sensitivity`. Synthetic engine, UI and automation tests; checked by hand on real phone photos. Milestone and effort estimates unchanged.
|
||||
@@ -427,8 +426,6 @@ Newest first: the date, what moved, and the new overall estimate.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-12 | minor | M3/#1364 candidate: absolute local GoToR links require desktop consent or headless allow_absolute; existing relative navigation and validation retained. Estimates unchanged. |
|
||||
| 2026-10-12 | minor | M8: redaction retires inherited `/Resources` so a parent `/Pages` can't keep a removed XObject reachable; only redacted pages that inherit are pinned (one shared reference), unredacted pages are untouched; estimates unchanged |
|
||||
| 2026-10-12 | minor | M7/#1200 candidate: bounded local GoToR opening, root-confined automation, and action-preserving appearance edits; named destinations and window behavior remain partial; estimates unchanged. |
|
||||
| 2026-10-12 | minor | Exclusive image crops resize with their pixels across nested states, intervening operators and content streams; shared clips remain fixed. Synthetic engine/tool regressions and private image save/reopen checks; estimates unchanged. |
|
||||
| 2026-10-11 | minor | M10: borderless inferred tables and positioned invoice watermarks (#773); estimates unchanged. |
|
||||
|
||||
@@ -56,7 +56,7 @@ const AR_DAYS: [&str; 7] = ["الأحد", "الاثنين", "الثلاثاء",
|
||||
|
||||
/// The languages month and weekday names can be written in (Preferences ▸ Date format ▸ Language):
|
||||
/// the interface languages, in the same order and with the same names.
|
||||
pub static DATE_LANGUAGES: [DateLanguage; 22] = [
|
||||
pub static DATE_LANGUAGES: [DateLanguage; 24] = [
|
||||
DateLanguage {
|
||||
code: "en",
|
||||
name: "English",
|
||||
@@ -360,6 +360,54 @@ pub static DATE_LANGUAGES: [DateLanguage; 22] = [
|
||||
days: ["sekmadienis", "pirmadienis", "antradienis", "trečiadienis", "ketvirtadienis", "penktadienis", "šeštadienis"],
|
||||
days_short: ["sk", "pr", "an", "tr", "kt", "pn", "št"],
|
||||
},
|
||||
// Indonesian. Month and weekday names are the same with and after a day number, so
|
||||
// `months_with_day` is `None`.
|
||||
DateLanguage {
|
||||
code: "id",
|
||||
name: "Bahasa Indonesia",
|
||||
months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
|
||||
months_with_day: None,
|
||||
months_short: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agu", "Sep", "Okt", "Nov", "Des"],
|
||||
days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"],
|
||||
days_short: ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
|
||||
},
|
||||
// Vietnamese: the month name is `tháng Một` with a day number and `Tháng Một` standing
|
||||
// alone, as in English.
|
||||
DateLanguage {
|
||||
code: "vi",
|
||||
name: "Tiếng Việt",
|
||||
months: [
|
||||
"Tháng Một",
|
||||
"Tháng Hai",
|
||||
"Tháng Ba",
|
||||
"Tháng Tư",
|
||||
"Tháng Năm",
|
||||
"Tháng Sáu",
|
||||
"Tháng Bảy",
|
||||
"Tháng Tám",
|
||||
"Tháng Chín",
|
||||
"Tháng Mười",
|
||||
"Tháng Mười Một",
|
||||
"Tháng Mười Hai",
|
||||
],
|
||||
months_with_day: Some([
|
||||
"tháng Một",
|
||||
"tháng Hai",
|
||||
"tháng Ba",
|
||||
"tháng Tư",
|
||||
"tháng Năm",
|
||||
"tháng Sáu",
|
||||
"tháng Bảy",
|
||||
"tháng Tám",
|
||||
"tháng Chín",
|
||||
"tháng Mười",
|
||||
"tháng Mười Một",
|
||||
"tháng Mười Hai",
|
||||
]),
|
||||
months_short: ["Thg 1", "Thg 2", "Thg 3", "Thg 4", "Thg 5", "Thg 6", "Thg 7", "Thg 8", "Thg 9", "Thg 10", "Thg 11", "Thg 12"],
|
||||
days: ["Chủ Nhật", "Thứ Hai", "Thứ Ba", "Thứ Tư", "Thứ Năm", "Thứ Sáu", "Thứ Bảy"],
|
||||
days_short: ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
|
||||
},
|
||||
];
|
||||
|
||||
/// The date language for `code` (any case), or `None` when there's none.
|
||||
@@ -660,6 +708,25 @@ mod tests {
|
||||
assert_eq!(in_lang("dddd, mmmm", "cs"), "sobota, březen");
|
||||
}
|
||||
|
||||
/// Indonesian month names are the same with and after a day number; Vietnamese lowers `tháng`
|
||||
/// after a day number and keeps it capitalised standing alone.
|
||||
#[test]
|
||||
fn indonesian_and_vietnamese_month_and_weekday_names() {
|
||||
assert_eq!(in_lang("d mmmm yyyy", "id"), "7 Maret 2026");
|
||||
assert_eq!(in_lang("mmmm yyyy", "id"), "Maret 2026");
|
||||
assert_eq!(in_lang("dddd, d mmmm yyyy", "id"), "Sabtu, 7 Maret 2026");
|
||||
assert_eq!(in_lang("d mmmm yyyy", "vi"), "7 tháng Ba 2026");
|
||||
assert_eq!(in_lang("mmmm yyyy", "vi"), "Tháng Ba 2026");
|
||||
assert_eq!(in_lang("ddd, d mmm", "vi"), "T7, 7 Thg 3");
|
||||
// Indonesian month names are Latin, so they reach a PDF; Vietnamese ones are not Western
|
||||
// European, and the date-text path refuses them with what to use instead.
|
||||
let mut s = Session::new().with_clock(|| 1_700_000_000);
|
||||
s.set_date_language(Some("vi")).unwrap();
|
||||
assert!(s.today_text_for_pdf(Some("d mmmm yyyy"), None).unwrap_err().contains("dd.mm.yyyy"));
|
||||
s.set_date_language(Some("id")).unwrap();
|
||||
assert_eq!(s.today_text_for_pdf(Some("d mmmm yyyy"), None).as_deref(), Ok("14 November 2023"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_session_writes_names_in_its_date_language() {
|
||||
let mut s = Session::new().with_clock(|| 1_700_000_000);
|
||||
|
||||
+10
-4
@@ -1,8 +1,8 @@
|
||||
//! Embeds the fonts of the optional craft-fonts build input (`CRAFT_FONTS_DIR`) as `CRAFT_FONTS`.
|
||||
//! The recipe is craft-fonts' `docs/integration.md`; unset, `CRAFT_FONTS` is empty. It only reads
|
||||
//! the local checkout (no network). On wasm32 it embeds only the UI faces (BIZ UDPGothic Regular
|
||||
//! and any `Arab`, `Telu`, `Hebr` or `Thai` face), to keep the web build within hosting limits (Cloudflare Pages:
|
||||
//! 25 MiB per file).
|
||||
//! and any `Arab`, `Telu`, `Hebr`, `Thai` or `Deva` face), to keep the web build within hosting
|
||||
//! limits (Cloudflare Pages: 25 MiB per file).
|
||||
|
||||
use std::fmt::Write as _;
|
||||
use std::path::PathBuf;
|
||||
@@ -55,8 +55,14 @@ fn craft_fonts(dir: &std::path::Path) -> Result<String, String> {
|
||||
let telugu = scripts.split(',').any(|s| s.trim() == "Telu");
|
||||
// And the Hebrew ones (small), for Hebrew text and Edit text on the web.
|
||||
let hebrew = scripts.split(',').any(|s| s.trim() == "Hebr");
|
||||
let web_face =
|
||||
(*family == "BIZ UDPGothic" && *style == "Regular") || arabic || telugu || hebrew || scripts.split(',').any(|s| s.trim() == "Thai");
|
||||
// Devanagari too, for the Hindi interface (one small face).
|
||||
let devanagari = scripts.split(',').any(|s| s.trim() == "Deva");
|
||||
let web_face = (*family == "BIZ UDPGothic" && *style == "Regular")
|
||||
|| arabic
|
||||
|| telugu
|
||||
|| hebrew
|
||||
|| scripts.split(',').any(|s| s.trim() == "Thai")
|
||||
|| devanagari;
|
||||
if wasm && !web_face {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,20 @@ pub fn document_thai_font() -> Option<&'static CraftFont> {
|
||||
ui_thai_fonts().into_iter().next()
|
||||
}
|
||||
|
||||
/// The `Deva` craft-fonts faces for Devanagari interface text (the Hindi catalog, file names,
|
||||
/// document titles), in manifest order. Empty when built without craft-fonts or when its revision
|
||||
/// has no Devanagari face; a desktop build then falls back to one font already installed on the
|
||||
/// machine (`SYSTEM_DEVANAGARI_FALLBACK`), and the web build shows replacement boxes.
|
||||
pub fn ui_devanagari_fonts() -> Vec<&'static CraftFont> {
|
||||
CRAFT_FONTS.iter().filter(|f| f.covers("Deva")).collect()
|
||||
}
|
||||
|
||||
/// The face for Devanagari text written into PDFs: the first `Deva` face. `None` when built
|
||||
/// without craft-fonts or when its revision has no Devanagari face.
|
||||
pub fn document_devanagari_font() -> Option<&'static CraftFont> {
|
||||
ui_devanagari_fonts().into_iter().next()
|
||||
}
|
||||
|
||||
/// The `Hebr` craft-fonts faces for Hebrew interface text (file names, document titles, text
|
||||
/// being edited), in manifest order. Empty when built without craft-fonts or when it has no
|
||||
/// Hebrew face.
|
||||
|
||||
@@ -16,9 +16,9 @@ mod shaping;
|
||||
pub mod source_shaping;
|
||||
mod std14;
|
||||
pub use craft::{
|
||||
CRAFT_FONTS, CraftFont, SHIPPORI_MINCHO, document_arabic_font, document_hebrew_font, document_japanese_font, document_japanese_font_for_style,
|
||||
document_japanese_fonts_for_style, document_thai_font, ui_arabic_fonts, ui_chinese_fonts, ui_cjk_fonts, ui_hebrew_fonts, ui_japanese_fonts,
|
||||
ui_telugu_fonts, ui_thai_fonts,
|
||||
CRAFT_FONTS, CraftFont, SHIPPORI_MINCHO, document_arabic_font, document_devanagari_font, document_hebrew_font, document_japanese_font,
|
||||
document_japanese_font_for_style, document_japanese_fonts_for_style, document_thai_font, ui_arabic_fonts, ui_chinese_fonts, ui_cjk_fonts,
|
||||
ui_devanagari_fonts, ui_hebrew_fonts, ui_japanese_fonts, ui_telugu_fonts, ui_thai_fonts,
|
||||
};
|
||||
pub use script::{
|
||||
GlyphError, GlyphOutline, INTER_REGULAR, MAX_SIGNATURE_CHARS, ScriptOutline, japanese_glyph, japanese_glyph_from, script_missing, script_outline,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -147,7 +147,7 @@ fn plural_sl(n: u64) -> usize {
|
||||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 22] = [
|
||||
pub static LANGUAGES: [LangInfo; 24] = [
|
||||
LangInfo { code: "en", name: "English", source: "", plural: plural_one_other, rtl: false, catalog: OnceLock::new() },
|
||||
LangInfo { code: "ja", name: "日本語", source: include_str!("ja.tsv"), plural: plural_none, rtl: false, catalog: OnceLock::new() },
|
||||
// Simplified Chinese; `zh`, `zh-CN`, `zh-SG` and `zh-Hans-*` locales resolve here (see `candidates`).
|
||||
@@ -207,6 +207,12 @@ pub static LANGUAGES: [LangInfo; 22] = [
|
||||
LangInfo { code: "tr", name: "Türkçe", source: include_str!("tr.tsv"), plural: plural_none, rtl: false, catalog: OnceLock::new() },
|
||||
// Lithuanian; every `lt-*` locale (`lt-LT`, `lt_LT.UTF-8`) resolves here. Three plural forms (one, few, other).
|
||||
LangInfo { code: "lt", name: "Lietuvių", source: include_str!("lt.tsv"), plural: plural_lithuanian, rtl: false, catalog: OnceLock::new() },
|
||||
// Indonesian; every `id-*` locale (`id-ID`, `id_ID.UTF-8`) resolves here. CLDR `id` counts have
|
||||
// one form ("other"), so `@plural` entries list a single form.
|
||||
LangInfo { code: "id", name: "Bahasa Indonesia", source: include_str!("id.tsv"), plural: plural_none, rtl: false, catalog: OnceLock::new() },
|
||||
// Vietnamese; every `vi-*` locale (`vi-VN`, `vi_VN.UTF-8`) resolves here. CLDR `vi` counts have
|
||||
// one form ("other"), so `@plural` entries list a single form.
|
||||
LangInfo { code: "vi", name: "Tiếng Việt", source: include_str!("vi.tsv"), plural: plural_none, rtl: false, catalog: OnceLock::new() },
|
||||
];
|
||||
|
||||
impl LangInfo {
|
||||
@@ -2378,6 +2384,125 @@ mod tests {
|
||||
assert!(missing.is_empty(), "untranslated Slovene UI literals: {missing:#?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indonesian_and_vietnamese_are_registered_and_persist() {
|
||||
let id = Lang::from_code("id").expect("id registered");
|
||||
let vi = Lang::from_code("vi").expect("vi registered");
|
||||
assert_eq!(id.name(), "Bahasa Indonesia");
|
||||
assert_eq!(vi.name(), "Tiếng Việt");
|
||||
assert_eq!(normalize_pref("ID"), Some("id"));
|
||||
assert_eq!(normalize_pref("Vi"), Some("vi"));
|
||||
for tag in ["id", "id-ID", "id_ID.UTF-8", "ID-id"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(id), "{tag}");
|
||||
}
|
||||
for tag in ["vi", "vi-VN", "vi_VN.UTF-8", "VI-vn"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(vi), "{tag}");
|
||||
}
|
||||
assert_eq!(first_supported("id-ID\r\nen-US"), Some(id));
|
||||
assert_eq!(first_supported("vi-VN\r\nen-US"), Some(vi));
|
||||
assert_eq!(tr(id, "Save"), "Simpan");
|
||||
assert_eq!(tr(id, "Save as…"), "Simpan sebagai…");
|
||||
assert_eq!(tr(id, "Bookmarks"), "Bookmark");
|
||||
assert_eq!(tr(id, "Fill & Sign"), "Isi & Tanda Tangani");
|
||||
assert_eq!(tr(vi, "Save"), "Lưu");
|
||||
assert_eq!(tr(vi, "Save as…"), "Lưu thành…");
|
||||
assert_eq!(tr(vi, "Bookmarks"), "Dấu trang");
|
||||
assert_eq!(tr(vi, "Fill & Sign"), "Điền & Ký");
|
||||
assert_eq!(tr(vi, "Interface language"), "Ngôn ngữ giao diện");
|
||||
assert_eq!(tr(id, "Interface language"), "Bahasa antarmuka");
|
||||
// One CLDR plural form (CLDR `id`, `vi`): the message reads the same for every count.
|
||||
assert_eq!((0..=3).map(|n| (id.0.plural)(n)).collect::<Vec<_>>(), [0, 0, 0, 0]);
|
||||
assert_eq!((0..=3).map(|n| (vi.0.plural)(n)).collect::<Vec<_>>(), [0, 0, 0, 0]);
|
||||
for n in [0, 1, 2, 5] {
|
||||
assert_eq!(trn(id, n, "{n} page", "{n} pages"), format!("{n} halaman"));
|
||||
assert_eq!(trn(vi, n, "{n} page", "{n} pages"), format!("{n} trang"));
|
||||
assert_eq!(trn(id, n, "{n} field", "{n} fields"), format!("{n} kolom"));
|
||||
assert_eq!(trn(vi, n, "{n} field", "{n} fields"), format!("{n} ô"));
|
||||
}
|
||||
let mut app = crate::PdfCraftApp::default();
|
||||
app.set_option("language", "ID").unwrap();
|
||||
assert_eq!(app.language, "id");
|
||||
let mut restored = crate::PdfCraftApp::default();
|
||||
restored.restore(&app.persist());
|
||||
assert_eq!(restored.language, "id");
|
||||
let mut app = crate::PdfCraftApp::default();
|
||||
app.set_option("language", "vi").unwrap();
|
||||
assert_eq!(app.language, "vi");
|
||||
let mut restored = crate::PdfCraftApp::default();
|
||||
restored.restore(&app.persist());
|
||||
assert_eq!(restored.language, "vi");
|
||||
assert!(restored.set_option("language", "xx").is_err());
|
||||
assert_eq!(restored.language, "vi");
|
||||
}
|
||||
|
||||
/// Indonesian and Vietnamese translate every registered command and every All tools group,
|
||||
/// section and item.
|
||||
#[test]
|
||||
fn indonesian_and_vietnamese_cover_commands_and_catalogue() {
|
||||
for lang in [Lang::from_code("id").expect("id registered"), Lang::from_code("vi").expect("vi registered")] {
|
||||
for command in pdfcraft_engine::commands::COMMANDS {
|
||||
assert!(has(lang, command.label), "{} missing command: {}", lang.code(), command.label);
|
||||
if let Some(menu) = command.menu {
|
||||
assert!(has(lang, menu), "{} missing menu: {menu}", lang.code());
|
||||
}
|
||||
}
|
||||
for group in pdfcraft_engine::catalog::TOOL_GROUPS {
|
||||
assert!(has(lang, group.label), "{} missing group: {}", lang.code(), group.label);
|
||||
for section in group.sections {
|
||||
assert!(has(lang, section.title), "{} missing section: {}", lang.code(), section.title);
|
||||
for item in section.items {
|
||||
assert!(has(lang, item.label), "{} missing item: {}", lang.code(), item.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Every `tl!("…")` literal in the UI source is translated in Indonesian and Vietnamese, so a
|
||||
/// new label cannot silently fall back to English in either.
|
||||
#[test]
|
||||
fn indonesian_and_vietnamese_cover_ui_literals() {
|
||||
let mut stack = vec![std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src")];
|
||||
let mut literals = std::collections::BTreeSet::new();
|
||||
while let Some(dir) = stack.pop() {
|
||||
for entry in std::fs::read_dir(dir).expect("UI source directory") {
|
||||
let path = entry.expect("UI source entry").path();
|
||||
if path.is_dir() {
|
||||
if path.file_name().is_some_and(|name| name != "i18n") {
|
||||
stack.push(path);
|
||||
}
|
||||
} else if path.extension().is_some_and(|ext| ext == "rs") {
|
||||
let source = std::fs::read_to_string(path).expect("UI source file").replace("\r\n", "\n");
|
||||
let mut rest = source.split("#[cfg(test)]\nmod ").next().unwrap_or_default();
|
||||
while let Some((_, after)) = rest.split_once("tl!(\"") {
|
||||
let mut escaped = false;
|
||||
let end = after
|
||||
.char_indices()
|
||||
.find_map(|(i, c)| {
|
||||
if c == '"' && !escaped {
|
||||
return Some(i);
|
||||
}
|
||||
escaped = c == '\\' && !escaped;
|
||||
None
|
||||
})
|
||||
.expect("closed tl! literal");
|
||||
let (raw, tail) = after.split_at(end);
|
||||
if tail.starts_with("\")") {
|
||||
let label: String = serde_json::from_str(&format!("\"{raw}\"")).expect("UI literal escapes");
|
||||
literals.insert(label);
|
||||
}
|
||||
rest = tail.strip_prefix('"').expect("closing quote");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(literals.len() > 900, "source scan found only {} literals", literals.len());
|
||||
for lang in [Lang::from_code("id").expect("id registered"), Lang::from_code("vi").expect("vi registered")] {
|
||||
let missing: Vec<_> = literals.iter().filter(|label| !has(lang, label)).collect();
|
||||
assert!(missing.is_empty(), "untranslated {} UI literals: {missing:#?}", lang.code());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bulgarian_is_registered() {
|
||||
let bg = Lang::from_code("bg").expect("bg registered");
|
||||
|
||||
+118
-46
@@ -7,10 +7,20 @@
|
||||
# (Acrobat's included) was consulted or copied; add entries the same way.
|
||||
# Product and technology names (PdfCraft, PDF, Acrobat JavaScript ...) are not translated;
|
||||
# the trailing "…" of dialog commands is kept.
|
||||
|
||||
# Document font selection
|
||||
# Shared field properties (Prepare a form).
|
||||
# Multi-object selection and movement (#844)
|
||||
# Keyboard key names, distinct from command labels.
|
||||
# Shared panel docking
|
||||
# Architectural print controls (follow-up to #599).
|
||||
# Accessibility structure tags.
|
||||
# PDF tab context menu (#1233)
|
||||
Delete Удалить
|
||||
Home Главная
|
||||
Menu Меню
|
||||
File Файл
|
||||
Edit Редактирование
|
||||
Off Выключено
|
||||
Pages Страницы
|
||||
View Просмотр
|
||||
Help Справка
|
||||
@@ -24,9 +34,6 @@
|
||||
Name on new comments Имя в новых комментариях
|
||||
Open… Открыть…
|
||||
Open Recent Открыть недавние
|
||||
Clear Recent Files Очистить недавние файлы
|
||||
Remove from Recent Убрать из недавних
|
||||
Remove {name} from Recent Убрать {name} из недавних
|
||||
Pin folder to Home… Закрепить папку на главной…
|
||||
Folders Папки
|
||||
Pin a folder… Закрепить папку…
|
||||
@@ -83,8 +90,6 @@
|
||||
Display theme Тема оформления
|
||||
Side panels Боковые панели
|
||||
Enable Acrobat JavaScript Включить Acrobat JavaScript
|
||||
Flatten Fill & Sign when saving Расплющить «Заполнить и подписать» при сохранении
|
||||
Text, marks and signatures become part of the page. Other comments stay editable. Текст, пометки и подписи становятся частью страницы. Остальные комментарии можно по-прежнему править.
|
||||
OK OK
|
||||
Join our Discord Присоединяйтесь к нашему Discord
|
||||
Close Закрыть
|
||||
@@ -99,7 +104,7 @@
|
||||
Previous / next page Предыдущая / следующая страница
|
||||
Delete selected pages (Organize) Удалить выделенные страницы (Организация)
|
||||
Select all pages (Organize) Выделить все страницы (Организация)
|
||||
Home Главная
|
||||
key Home Home
|
||||
Open Открыть
|
||||
Open a PDF ({key}) Открыть PDF ({key})
|
||||
Dark gray theme Тёмно-серая тема
|
||||
@@ -354,7 +359,7 @@
|
||||
Page options Параметры страниц
|
||||
Page grid Сетка страниц
|
||||
Rotate Повернуть
|
||||
Delete Удалить
|
||||
key Delete Delete
|
||||
Extract Извлечь
|
||||
Insert Вставить
|
||||
Replace Заменить
|
||||
@@ -615,7 +620,6 @@
|
||||
Bottom: Снизу:
|
||||
Width: Ширина:
|
||||
Height: Высота:
|
||||
Rotation: Поворот:
|
||||
Points from the page's bottom-left corner. Точки от левого нижнего угла страницы.
|
||||
Alignment: Выравнивание:
|
||||
Default Value: Значение по умолчанию:
|
||||
@@ -697,14 +701,11 @@
|
||||
of the following fields: из следующих полей:
|
||||
Simplified field notation: Упрощённая запись полей:
|
||||
Field names with + - * / and parentheses; put \\ before spaces in names. Имена полей с + - * / и скобками; ставьте \\ перед пробелами в именах.
|
||||
|
||||
# Document font selection
|
||||
Finding document fonts… Поиск шрифтов для документа…
|
||||
Installed fonts… Установленные шрифты…
|
||||
Keep source fonts Сохранить исходные шрифты
|
||||
Load font… Загрузить шрифт…
|
||||
No openly licensed document fonts found. Load a font file. Шрифты с открытой лицензией не найдены. Загрузите файл шрифта.
|
||||
|
||||
Format text Форматировать текст
|
||||
Bold Полужирный
|
||||
Italic Курсив
|
||||
@@ -789,7 +790,7 @@
|
||||
{verb} Header and Footer {verb} колонтитулы
|
||||
{verb} Watermark {verb} водяной знак
|
||||
{verb} Background {verb} фон
|
||||
Margins Поля
|
||||
Margins (in) Поля (дюймы)
|
||||
Left Header Text Текст левого колонтитула
|
||||
Center Header Text Текст центрального колонтитула
|
||||
Right Header Text Текст правого колонтитула
|
||||
@@ -955,13 +956,6 @@
|
||||
This comment's appearance can't be changed yet. Внешний вид этого комментария пока нельзя изменить.
|
||||
Icon Значок
|
||||
Thickness Толщина
|
||||
Line ending Окончание линии
|
||||
Open arrow Открытая стрелка
|
||||
Closed arrow Закрытая стрелка
|
||||
Butt Торец
|
||||
Reverse open arrow Обратная открытая стрелка
|
||||
Reverse closed arrow Обратная закрытая стрелка
|
||||
Slash Косая черта
|
||||
Modified Изменено
|
||||
No status has been set. Статус не установлен.
|
||||
Compare Files Сравнить файлы
|
||||
@@ -1174,7 +1168,7 @@
|
||||
Collate Собирать в тетради
|
||||
Print in grayscale Печать в оттенках серого
|
||||
Two-sided: Двусторонняя:
|
||||
Off Выкл
|
||||
animation effects Off Выкл
|
||||
Flip on long edge Переворот по длинной стороне
|
||||
Flip on short edge Переворот по короткой стороне
|
||||
Paper: Бумага:
|
||||
@@ -2048,14 +2042,8 @@
|
||||
Default page display Отображение страниц по умолчанию
|
||||
Default zoom Масштаб по умолчанию
|
||||
Used when a PDF doesn't ask for a layout or zoom. Continuous scrolling never snaps between pages. Используется, когда PDF не запрашивает макет или масштаб. Непрерывная прокрутка никогда не привязывается между страницами.
|
||||
Reopen the files that were open when PdfCraft last closed Повторно открывать файлы, которые были открыты при последнем закрытии PdfCraft
|
||||
Reload documents when their files change on disk Перезагружать документы при изменении их файлов на диске
|
||||
This file changed on disk. Reload it to see the new version; your unsaved changes will be lost. Этот файл был изменён на диске. Перезагрузите его, чтобы увидеть новую версию; несохранённые изменения будут потеряны.
|
||||
Reload Перезагрузить
|
||||
Couldn't reload {name}: {e} Не удалось перезагрузить {name}: {e}
|
||||
Choose a PNG or JPEG image (up to 4 MiB). Transparency is preserved. Выберите изображение PNG или JPEG (до 4 МиБ). Прозрачность сохраняется.
|
||||
Couldn't import the signature image: {e} Не удалось импортировать изображение подписи: {e}
|
||||
|
||||
@plural {n} page|{n} pages {n} страница|{n} страницы|{n} страниц
|
||||
@plural {n} field|{n} fields {n} поле|{n} поля|{n} полей
|
||||
@plural {n} field name collides with earlier files and will be renamed: {list}|{n} field names collide with earlier files and will be renamed: {list} Имя {n} поля конфликтует с предыдущими файлами и будет переименовано: {list}|Названия {n} полей конфликтуют с предыдущими файлами и будут переименованы: {list}|Названия {n} полей конфликтуют с предыдущими файлами и будут переименованы: {list}
|
||||
@@ -2077,7 +2065,101 @@
|
||||
Created a PDF from {n} files; left out: {names} PDF создан из файлов: {n}; пропущены: {names}
|
||||
Created a PDF from the first {n} files PDF создан из первых файлов: {n}
|
||||
Go to page… Перейти на страницу…
|
||||
For page sizes and margins. Для размеров страниц и полей.
|
||||
Optimization is already running Оптимизация уже выполняется
|
||||
Optimization cancelled Оптимизация отменена
|
||||
Couldn't optimize the file Не удалось оптимизировать файл
|
||||
Files are combined in this order. Leave Pages empty to take every page. Файлы объединяются в этом порядке. Оставьте «Страницы» пустым, чтобы взять все страницы.
|
||||
Portable mode is off: {folder} can't be written ({error}). Settings are kept in your user folder instead. Портативный режим выключен: невозможно записать в {folder} ({error}). Настройки хранятся в вашей пользовательской папке.
|
||||
PdfCraft is drawing without a graphics processor, so it may be slow. Updating the graphics driver may help. PdfCraft работает без графического процессора, поэтому может быть медленным. Обновление графического драйвера может помочь.
|
||||
Show Показывать
|
||||
Sort Сортировать
|
||||
Reverse the order Обратный порядок
|
||||
Grab bag Разное
|
||||
Table Таблица
|
||||
Username Имя пользователя
|
||||
Display name Отображаемое имя
|
||||
Real name Настоящее имя
|
||||
Name (A–Z) Имя (А–Я)
|
||||
Merged PRs Принятые PR
|
||||
PRs PR
|
||||
Commits Коммиты
|
||||
Lines added Добавлено строк
|
||||
+LOC +LOC
|
||||
Lines deleted Удалено строк
|
||||
−LOC −LOC
|
||||
Line delta Изменение количества строк
|
||||
ΔLOC ΔLOC
|
||||
Binary assets added Добавлено бинарных ресурсов
|
||||
+Bin +Bin
|
||||
Binary assets removed Удалено бинарных ресурсов
|
||||
−Bin −Bin
|
||||
First commit Первый коммит
|
||||
First Первый
|
||||
Last commit Последний коммит
|
||||
Last Последний
|
||||
No contributor data was built into this copy. В эту копию не встроены данные об участниках.
|
||||
No model credits were built into this copy. В эту копию не встроены сведения о моделях.
|
||||
{n} contributors · {c} commits Участников: {n} · Коммитов: {c}
|
||||
Company Организация
|
||||
Model Модель
|
||||
% of all commits % всех коммитов
|
||||
Lines +/− Строк +/−
|
||||
@{login}: {prs} PRs, {commits} commits, +{added} / −{deleted} lines (Δ {delta}), +{bin_added} / −{bin_deleted} binary assets, {first} – {last} @{login}: PR: {prs}, коммитов: {commits}, +{added} / −{deleted} строк (Δ {delta}), +{bin_added} / −{bin_deleted} бинарных ресурсов, {first} – {last}
|
||||
None of those files can be made into a PDF; use PDFs, images or .txt files Ни один из этих файлов нельзя преобразовать в PDF; используйте PDF, изображения или файлы .txt
|
||||
About О программе
|
||||
Contributors Участники
|
||||
Models Модели
|
||||
Only continue if you trust this document. An address can carry information from the document, such as what you typed into its form. Продолжайте только если доверяете этому документу. Адрес может содержать информацию из документа, например данные, введённые в форму.
|
||||
Print single-sided. Keep the sheets in order, cut at the marks, then stack the piles left to right, top to bottom. Печатайте с одной стороны. Сохраняйте порядок листов, разрежьте по меткам, затем сложите стопки слева направо, сверху вниз.
|
||||
Undo {action} Отменить {action}
|
||||
Redo {action} Повторить {action}
|
||||
Reopen the files that were open when PdfCraft last closed Повторно открывать файлы, которые были открыты при последнем закрытии PdfCraft
|
||||
Clear Recent Files Очистить недавние файлы
|
||||
Remove from Recent Убрать из недавних
|
||||
Remove {name} from Recent Убрать {name} из недавних
|
||||
Inserted text Вставленный текст
|
||||
Add {what} Добавить: {what}
|
||||
Use {what} Использовать: {what}
|
||||
Place saved {what} Разместить сохранённое: {what}
|
||||
Remove saved {what} Удалить сохранённое: {what}
|
||||
A link Ссылка
|
||||
A button Кнопка
|
||||
A script Сценарий
|
||||
{who} in this document tried to open an address PdfCraft won't open: {e}. {who} в этом документе пытается открыть адрес, который PdfCraft не открывает: {e}.
|
||||
{who} in this document tried to open an address PdfCraft won't open: {e}. Only web (http, https) and email (mailto) links open from documents. {who} в этом документе пытается открыть адрес, который PdfCraft не открывает: {e}. Из документов открываются только веб-ссылки (http, https) и ссылки на электронную почту (mailto).
|
||||
Another file dialog is still open. Finish with it first. Ещё открыто другое окно выбора файла. Сначала завершите работу с ним.
|
||||
Couldn't show the file picker. Please try again. Не удалось показать окно выбора файла. Попробуйте ещё раз.
|
||||
The document changed while you were choosing a file, so nothing was changed. Документ изменился, пока вы выбирали файл, поэтому ничего не изменено.
|
||||
The document was closed before it could be saved. Документ был закрыт до того, как его удалось сохранить.
|
||||
The pages were extracted but not deleted, because the document changed while you were choosing a folder. Страницы извлечены, но не удалены, потому что документ изменился, пока вы выбирали папку.
|
||||
That didn't work: an internal error stopped it ({m}). Не удалось: действие остановила внутренняя ошибка ({m}).
|
||||
The Windows store's digital IDs couldn't be listed: {e} Не удалось получить список цифровых удостоверений из хранилища Windows: {e}
|
||||
Align fields Выровнять поля
|
||||
Distribute fields Распределить поля
|
||||
Match field sizes Задать одинаковые размеры полей
|
||||
Mark for redaction Пометить для удаления содержимого
|
||||
Mark pages for redaction Пометить страницы для удаления содержимого
|
||||
Change document properties Изменить свойства документа
|
||||
Mark differences Пометить различия
|
||||
Run form script Выполнить сценарий формы
|
||||
Scripted content must be usable with a keyboard and assistive technology. Check the document's scripts. Содержимое со сценариями должно быть доступно для управления с помощью клавиатуры и вспомогательных технологий. Проверьте сценарии документа.
|
||||
Table rows belong directly in a table or its head, body or foot. Fix the table's tags. Строки таблицы должны принадлежать непосредственно таблице или её заголовочной, основной или нижней части. Исправьте теги таблицы.
|
||||
Basic constraints Основные ограничения
|
||||
Certificate authority Центр сертификации
|
||||
End entity Конечный субъект
|
||||
SHA-1 digest Отпечаток SHA-1
|
||||
SHA-256 digest Отпечаток SHA-256
|
||||
Squiggly Волнистое
|
||||
Home / End Home / End
|
||||
H / Space (hold) H / Пробел (удерживать)
|
||||
Rotation: Поворот:
|
||||
Line ending Окончание линии
|
||||
Open arrow Открытая стрелка
|
||||
Closed arrow Закрытая стрелка
|
||||
Butt Торец
|
||||
Reverse open arrow Обратная открытая стрелка
|
||||
Reverse closed arrow Обратная закрытая стрелка
|
||||
Slash Косая черта
|
||||
The PDF text fonts can't draw “{c}” ({code}). Choose supported text. Шрифты текста PDF не могут отобразить «{c}» ({code}). Выберите поддерживаемый текст.
|
||||
The standard fonts can't draw “{c}” ({code}). Only Western European characters can be added as text for now. Стандартные шрифты не могут отобразить «{c}» ({code}). Пока в виде текста можно добавлять только западноевропейские символы.
|
||||
This field's font can't draw “{c}” ({code}), so PdfCraft shows ? in its place. The value is saved, and other PDF viewers redraw the field from it. Шрифт этого поля не может отобразить «{c}» ({code}), поэтому PdfCraft показывает на его месте ?. Значение сохранено, и другие программы просмотра PDF перерисуют поле по нему.
|
||||
@@ -2085,6 +2167,8 @@
|
||||
Larger pages Крупнее страницы
|
||||
Smaller pages Мельче страницы
|
||||
Reset page size Сбросить размер страниц
|
||||
Flatten Fill & Sign when saving Расплющить «Заполнить и подписать» при сохранении
|
||||
Text, marks and signatures become part of the page. Other comments stay editable. Текст, пометки и подписи становятся частью страницы. Остальные комментарии можно по-прежнему править.
|
||||
Date format Формат даты
|
||||
Fill & Sign dates Даты для «Заполнить и подписать»
|
||||
Pattern Шаблон
|
||||
@@ -2098,10 +2182,10 @@
|
||||
Printer properties Свойства принтера
|
||||
Reset to printer defaults Вернуть параметры принтера по умолчанию
|
||||
This printer offers no settings of its own. У этого принтера нет собственных параметров.
|
||||
Margins Поля
|
||||
For page sizes and margins. Для размеров страниц и полей.
|
||||
Scroll tabs left Прокрутить вкладки влево
|
||||
Scroll tabs right Прокрутить вкладки вправо
|
||||
|
||||
# Shared field properties (Prepare a form).
|
||||
Shared Field Properties Общие свойства полей
|
||||
{n} fields selected Выбрано полей: {n}
|
||||
Mixed Разные значения
|
||||
@@ -2121,8 +2205,6 @@
|
||||
The field {name} no longer exists. Поле {name} больше не существует.
|
||||
A selected field has no widget on any page, so the fields can't be edited together. У одного из выбранных полей нет виджета ни на одной странице, поэтому поля нельзя изменить вместе.
|
||||
({n} at {dpi} dpi, the largest size they can be drawn at) ({n} — с разрешением {dpi} dpi, максимальным, с каким их можно отрисовать)
|
||||
|
||||
# Multi-object selection and movement (#844)
|
||||
@plural {n} object selected|{n} objects selected Выделен {n} объект|Выделено {n} объекта|Выделено {n} объектов
|
||||
Drag the selection to move it. Esc clears the selection. Перетащите выделение, чтобы переместить его. Esc снимает выделение.
|
||||
Shift-click to select several objects. Drag empty space to select an area. Щёлкните с Shift, чтобы выбрать несколько объектов. Протяните рамку на пустом месте, чтобы выделить область.
|
||||
@@ -2137,13 +2219,9 @@
|
||||
Fill colour Цвет заливки
|
||||
No fill Без заливки
|
||||
Fill colour: none Цвет заливки: нет
|
||||
|
||||
# Keyboard key names, distinct from command labels.
|
||||
key Ctrl Ctrl
|
||||
key Shift Shift
|
||||
key Delete Delete
|
||||
key Esc Esc
|
||||
key Home Home
|
||||
key End End
|
||||
key Space Пробел
|
||||
Grid view Вид сеткой
|
||||
@@ -2157,23 +2235,21 @@ key Space Пробел
|
||||
Collapse all Свернуть все
|
||||
Remove page Удалить страницу
|
||||
Remove {n} pages Удалить страницы: {n}
|
||||
|
||||
# Shared panel docking
|
||||
Reset panel layout Сбросить расположение панелей
|
||||
Float panel Сделать панель плавающей
|
||||
Move to group Переместить в группу
|
||||
Panels Панели
|
||||
Resize panels Изменить размеры панелей
|
||||
Resize panel window Изменить размер окна панели
|
||||
Tools Инструменты
|
||||
Inspector Инспектор
|
||||
|
||||
# Architectural print controls (follow-up to #599).
|
||||
PDF page: {size} Страница PDF: {size}
|
||||
Match page at 100% Подобрать бумагу, масштаб 100%
|
||||
PDF scale: {scale}% Масштаб PDF: {scale}%
|
||||
Printer paper and scaling may differ. Check driver settings. Бумага и масштаб принтера могут отличаться. Проверьте настройки драйвера.
|
||||
Drawing scale changes. Use Actual size for full-scale plans. Масштаб чертежа изменяется. Для печати в исходном масштабе выберите Фактический размер.
|
||||
Reload documents when their files change on disk Перезагружать документы при изменении их файлов на диске
|
||||
This file changed on disk. Reload it to see the new version; your unsaved changes will be lost. Этот файл был изменён на диске. Перезагрузите его, чтобы увидеть новую версию; несохранённые изменения будут потеряны.
|
||||
Reload Перезагрузить
|
||||
Couldn't reload {name}: {e} Не удалось перезагрузить {name}: {e}
|
||||
Select page {label} Выбрать страницу {label}
|
||||
Rotate page {label} counterclockwise Повернуть страницу {label} против часовой стрелки
|
||||
Rotate page {label} clockwise Повернуть страницу {label} по часовой стрелке
|
||||
@@ -2184,7 +2260,6 @@ scroll speed Normal Обычная
|
||||
scroll speed Fast Высокая
|
||||
Animation effects Эффекты анимации
|
||||
animation effects On Вкл
|
||||
animation effects Off Выкл
|
||||
Enhance camera image Улучшить снимок с камеры
|
||||
Drag the corners onto the document's edges, then choose Enhance page. Перетащите углы к краям документа, затем выберите «Улучшить страницу».
|
||||
Enhance page Улучшить страницу
|
||||
@@ -2214,7 +2289,6 @@ animation effects Off Выкл
|
||||
Tools panel side Сторона панели инструментов
|
||||
Add comment Добавить комментарий
|
||||
Change tag properties Изменить свойства тега
|
||||
# Accessibility structure tags.
|
||||
Tags panel Панель тегов
|
||||
Tags Теги
|
||||
Tag type Тип тега
|
||||
@@ -2240,8 +2314,6 @@ animation effects Off Выкл
|
||||
Ink sensitivity Чувствительность к чернилам
|
||||
No signature was found in the frame. Move the frame over the signature or raise the ink sensitivity. В рамке не найдена подпись. Наведите рамку на подпись или увеличьте чувствительность к чернилам.
|
||||
Couldn't use the photo: {e} Не удалось использовать фото: {e}
|
||||
|
||||
# PDF tab context menu (#1233)
|
||||
Close Tab Закрыть вкладку
|
||||
Reopen Closed Tab Открыть закрытую вкладку
|
||||
Reveal in Finder Показать в Finder
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
File diff suppressed because it is too large
Load Diff
@@ -338,13 +338,14 @@ fn font_definitions_for_platform(prefer_hans: bool, platform: FontPolicyPlatform
|
||||
}
|
||||
}
|
||||
// Arabic-script faces (file names, document titles), Telugu faces (the Telugu interface,
|
||||
// file names), Hebrew faces (file names, text being edited) and Thai faces after the CJK ones; the
|
||||
// ranges don't overlap.
|
||||
// file names), Hebrew faces (file names, text being edited), Thai faces and Devanagari faces
|
||||
// (the Hindi interface) after the CJK ones; the ranges don't overlap.
|
||||
for face in pdfcraft_fonts::ui_arabic_fonts()
|
||||
.into_iter()
|
||||
.chain(pdfcraft_fonts::ui_telugu_fonts())
|
||||
.chain(pdfcraft_fonts::ui_hebrew_fonts())
|
||||
.chain(pdfcraft_fonts::ui_thai_fonts())
|
||||
.chain(pdfcraft_fonts::ui_devanagari_fonts())
|
||||
{
|
||||
let name = face.name();
|
||||
if !fonts.font_data.contains_key(&name) {
|
||||
|
||||
@@ -8,6 +8,7 @@ const JAPANESE: &str = "日本語の文字";
|
||||
const CHINESE: &str = "简体中文欢迎";
|
||||
const ARABIC: &str = "واحد اثنين";
|
||||
const TELUGU: &str = "తెలుగు ఫైల్";
|
||||
const DEVANAGARI: &str = "हिंदी पाठ सम्पादन";
|
||||
|
||||
fn families() -> Vec<FontId> {
|
||||
vec![FontId::proportional(13.0), FontId::monospace(13.0), theme::medium(13.0), theme::semibold(17.0)]
|
||||
@@ -163,7 +164,30 @@ fn system_fallback_covers_devanagari() {
|
||||
assert_eq!(stack.iter().filter(|n| *n == theme::SYSTEM_DEVANAGARI_FALLBACK).count(), 1, "{family:?}");
|
||||
}
|
||||
let mut fonts = Fonts::new(TextOptions::default(), defs);
|
||||
const DEVANAGARI: &str = "हिंदी पाठ सम्पादन";
|
||||
for id in families() {
|
||||
assert!(fonts.has_glyphs(&id, DEVANAGARI), "{id:?} lacks {DEVANAGARI}");
|
||||
}
|
||||
assert!(layout_widths(&mut fonts, DEVANAGARI).iter().all(|w| w.is_finite() && *w > 0.0));
|
||||
}
|
||||
|
||||
/// Built with craft-fonts, Hindi text renders with real glyphs (no tofu) in every family: a
|
||||
/// `Deva` face is a fallback after the app's own fonts. Without craft-fonts it is the installed
|
||||
/// Devanagari fallback's job (see `system_fallback_covers_devanagari`), and the web build has
|
||||
/// neither.
|
||||
#[test]
|
||||
fn devanagari_ui_text_uses_craft_fonts() {
|
||||
if pdfcraft_fonts::ui_devanagari_fonts().is_empty() {
|
||||
eprintln!("skipping devanagari_ui_text_uses_craft_fonts: built without craft-fonts (set CRAFT_FONTS_DIR to run it)");
|
||||
return;
|
||||
}
|
||||
let defs = theme::font_definitions();
|
||||
for family in [FontFamily::Proportional, FontFamily::Monospace] {
|
||||
let stack = &defs.families[&family];
|
||||
let first = stack.iter().position(|n| pdfcraft_fonts::ui_devanagari_fonts().iter().any(|f| f.name() == *n)).expect("a Deva face is a fallback");
|
||||
let own = stack.iter().position(|n| n == "Inter" || n == "JetBrainsMono").expect("the app's own font");
|
||||
assert!(own < first, "{family:?}: {stack:?}");
|
||||
}
|
||||
let mut fonts = Fonts::new(TextOptions::default(), defs);
|
||||
for id in families() {
|
||||
assert!(fonts.has_glyphs(&id, DEVANAGARI), "{id:?} lacks {DEVANAGARI}");
|
||||
}
|
||||
@@ -264,7 +288,7 @@ fn ui_fonts_work_without_craft_fonts() {
|
||||
theme::install_fonts(&ctx);
|
||||
}
|
||||
|
||||
/// Latin- and Cyrillic-script catalogs (Czech, Brazilian Portuguese, German, Spanish, French, Russian, Bulgarian, Hungarian, Ukrainian, Italian, Slovene) are drawn entirely by the app's own faces
|
||||
/// Latin- and Cyrillic-script catalogs (Czech, Brazilian Portuguese, German, Spanish, French, Russian, Bulgarian, Kazakh, Hungarian, Ukrainian, Italian, Slovene, Lithuanian, Greek, Turkish, Indonesian, Vietnamese) are drawn entirely by the app's own faces
|
||||
/// (Inter, JetBrains Mono): no letter falls through to egui's defaults or a CJK fallback. (egui's
|
||||
/// `has_glyphs` can't answer this: with only the primary face it is also the replacement face.)
|
||||
#[test]
|
||||
@@ -287,8 +311,14 @@ fn primary_ui_fonts_cover_latin_and_cyrillic_catalogs() {
|
||||
("tr", include_str!("../src/i18n/tr.tsv")),
|
||||
("sl", include_str!("../src/i18n/sl.tsv")),
|
||||
("lt", include_str!("../src/i18n/lt.tsv")),
|
||||
("id", include_str!("../src/i18n/id.tsv")),
|
||||
("vi", include_str!("../src/i18n/vi.tsv")),
|
||||
] {
|
||||
let mut text = String::from("áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽãõçâêôàÃÕÇÂÊÔÀñÑüÜäÄöÖőűß„“¿¡«»…ґҐєЄіІїЇәғқңөұүһӘҒҚҢӨҰҮҺ");
|
||||
// Vietnamese stacks its diacritics: the precomposed letters and the base letters they
|
||||
// combine with, including `đ` and the tone marks `ạ ả ấ ầ ẩ ẫ ậ ắ ằ ẳ ẵ ặ ế ề ể ễ ệ`.
|
||||
let mut text = String::from(
|
||||
"áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽãõçâêôàÃÕÇÂÊÔÀñÑüÜäÄöÖőűß„“¿¡«»…ґҐєЄіІїЇәғқңөұүһӘҒҚҢӨҰҮҺđĐạảấầẩẫậắằẳẵặếềểễệọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ",
|
||||
);
|
||||
for line in catalog.lines().filter(|l| !l.starts_with('#')) {
|
||||
if let Some(translation) = line.split('\t').nth(2) {
|
||||
text.extend(translation.chars().filter(|c| !c.is_whitespace()));
|
||||
|
||||
+6
-6
@@ -38,7 +38,7 @@ update the numbers.
|
||||
| 11 | Accessibility: checker only, no tagging tools | feature | 40–70 | [target-app-parity.md](target-app-parity.md) |
|
||||
| 12 | No measured fidelity harness against Acrobat | quality | (in 2) | [target-app-parity.md](target-app-parity.md) |
|
||||
| 13 | Performance budgets and lazy loading of very large files | performance | 25–45 | this file |
|
||||
| 14 | Arabic not mirrored; no Hindi, Indonesian, Korean, Vietnamese | localization | 30–50 | [localization-parity.md](localization-parity.md) |
|
||||
| 14 | Arabic not mirrored; no Hindi, Korean | localization | 30–50 | [localization-parity.md](localization-parity.md) |
|
||||
| 15 | Forms: JavaScript object model, actions, submit, XFA depth | feature | 40–75 | [pdf-spec-parity.md](pdf-spec-parity.md) |
|
||||
| 16 | UI precision gaps (multi-select move, rulers, accelerators, windows) | UI/UX | 30–60 | [ui-parity.md](ui-parity.md) |
|
||||
| 17 | Certificate security and other protect gaps | feature, spec | 15–30 | [pdf-spec-parity.md](pdf-spec-parity.md) |
|
||||
@@ -236,14 +236,14 @@ update the numbers.
|
||||
to edit.
|
||||
- **Estimate:** 25–45 h. **Doc:** this file.
|
||||
|
||||
### 14. Arabic not mirrored; no Hindi, Indonesian, Korean, Vietnamese
|
||||
### 14. Arabic not mirrored; no Hindi, Korean
|
||||
|
||||
- **Missing:** a right-to-left layout (panels, menus, alignment), bidirectional text in the text
|
||||
layout itself (wrapping, typed text); catalogs for Hindi, Indonesian, Korean and Vietnamese
|
||||
(Acrobat ships Korean; it ships Arabic in its Middle East edition; it ships none of the other
|
||||
three).
|
||||
layout itself (wrapping, typed text); catalogs for Hindi and Korean, each with the script face it
|
||||
needs (Acrobat ships Korean; it ships Arabic in its Middle East edition; it ships neither Hindi
|
||||
nor Indonesian nor Vietnamese). Indonesian and Vietnamese ship as of 2026-10-13.
|
||||
- **Evidence:** [localization.md](localization.md) §Arabic; `misc.rtl-ui` planned.
|
||||
- **Estimate:** 30–50 h (Arabic RTL 20–35, four catalogs 10–15 plus review). **Doc:**
|
||||
- **Estimate:** 30–50 h (Arabic RTL 20–35, two catalogs 10–15 plus review). **Doc:**
|
||||
[localization-parity.md](localization-parity.md).
|
||||
|
||||
### 15. Forms: JavaScript object model, actions, submit, XFA depth
|
||||
|
||||
+15
-10
@@ -1,6 +1,6 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (list all registered interface languages; preserve the original coverage snapshot) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (Indonesian and Vietnamese ship as full 2,578-key catalogs; the key union is now 2,578; the localization dimension re-measures at ≈ 76%) · **Target:** Adobe Acrobat Pro (Acrobat DC, continuous track 26.002.21931, macOS)
|
||||
|
||||
Per-language status of PdfCraft's interface against Acrobat Pro's. How translations work and how to
|
||||
add a language: [localization.md](localization.md). Summary in [ROADMAP.md](../ROADMAP.md).
|
||||
@@ -9,8 +9,11 @@ add a language: [localization.md](localization.md). Summary in [ROADMAP.md](../R
|
||||
|
||||
- **Strings translated** is measured: the catalogs in `crates/ui-egui/src/i18n/<code>.tsv`, counted
|
||||
as distinct `(context, English)` keys with a non-empty translation. The denominator is the union
|
||||
of keys across all catalogs, **2,143**. Cross-check: of the 1,151 distinct `tl!("…")` literals in
|
||||
`crates/ui-egui/src`, every full catalog covers 96.5–99.4% (7 literals are in no catalog yet).
|
||||
of keys across all catalogs, **2,578** (it was 2,143 until the Lithuanian catalog arrived; the
|
||||
percentages in the table below for the languages measured on 2026-10-10 are that older
|
||||
snapshot's). Cross-check: of the `tl!("…")` literals in `crates/ui-egui/src`, Indonesian and
|
||||
Vietnamese translate every one of them (required by `indonesian_and_vietnamese_cover_ui_literals`),
|
||||
and two literals are in no catalog yet.
|
||||
English is built in.
|
||||
- **Not counted:** error details from the engine and the operating system, which are shown in
|
||||
English inside a translated frame; there is no translated user documentation or help yet (Acrobat
|
||||
@@ -34,16 +37,17 @@ add a language: [localization.md](localization.md). Summary in [ROADMAP.md](../R
|
||||
| Arabic | `ar` | 2,143 (100%) | dialogs and tooltips; no help | Letters joined and lines reordered at load, but **layout not mirrored**, wrapped lines break at the wrong end, typed text runs left to right (`localization.md` §Arabic) | no | partial | yes (Middle East edition) | 25–45 |
|
||||
| French | `fr` | 2,128 (99.3%) | dialogs and tooltips; no help | Latin | no | partial | yes | 1–2 |
|
||||
| Portuguese (Brazil) | `pt-br` | 2,130 (99.4%) | dialogs and tooltips; no help | Latin | community (#288) | partial | yes | 1–2 |
|
||||
| Indonesian | `id` | 0 | none | Latin | no | none | no | 3–5 |
|
||||
| Indonesian | `id` | 2,578 (100%) | dialogs and tooltips through `tl!`; no help | Latin, drawn by the bundled Inter and JetBrains Mono (tested in `crates/ui-egui/tests/fonts.rs`) | no | partial | no | 1–2 |
|
||||
| Japanese | `ja` | 2,123 (99.1%) | dialogs and tooltips; no help | BIZ UDPGothic from craft-fonts; IME patched on macOS, Windows, X11, Wayland; vertical text renders but can't be edited | no | partial | yes | 4–8 |
|
||||
| German | `de` | 2,128 (99.3%) | dialogs and tooltips; no help | Latin | community, in progress (#828: terminology, clipping) | partial | yes | 2–4 |
|
||||
| Korean | `ko` | 0 | none | Hangul IME patched (Microsoft and Apple Korean, `vendor/README.md` winit 3, 6, 9, 10); no Hangul UI face in craft-fonts | no | none | yes | 6–10 |
|
||||
| Vietnamese | `vi` | 0 | none | Latin with stacked diacritics; bundled Inter covers them (not tested) | no | none | no | 3–5 |
|
||||
| Vietnamese | `vi` | 2,578 (100%) | dialogs and tooltips through `tl!`; no help | Latin with stacked diacritics; the bundled Inter covers them, tested in `crates/ui-egui/tests/fonts.rs` | no | partial | no | 1–2 |
|
||||
|
||||
**Localization dimension ≈ 59%**: the mean over the twelve, counting a catalog's percentage but only
|
||||
60% for Chinese and Arabic until their script problems are fixed, and 0 for the four missing
|
||||
languages. **≈ 60–115 h** to bring all twelve to `full`, of which ≈ 35–60 h is engineering (Hans
|
||||
face, RTL layout, bidi text) and the rest catalogs and fixes; native-speaker review needs humans.
|
||||
**Localization dimension ≈ 76%**: the mean over the twelve, counting a catalog's percentage but only
|
||||
60% for Chinese and Arabic until their script problems are fixed, and 0 for the two languages still
|
||||
missing (Hindi, Korean). **≈ 55–90 h** to bring all twelve to `full`, of which ≈ 35–60 h is
|
||||
engineering (Hans face, RTL layout, bidi text) and the rest the two remaining catalogs and fixes;
|
||||
native-speaker review needs humans.
|
||||
|
||||
No language is `full` except English: every catalog leaves engine error details in English, and
|
||||
none has translated help.
|
||||
@@ -67,7 +71,7 @@ none has translated help.
|
||||
| Turkish | `tr` | 2,313 entries | partial | yes |
|
||||
| Lithuanian | `lt` | 2,578 entries | partial; community native-speaker review ([#1401](https://github.com/storytold/pdfcraft/pull/1401)) | no |
|
||||
|
||||
**14 other languages** ship (21 catalogs plus English, 22 interface languages in Preferences).
|
||||
**14 other languages** ship (23 catalogs plus English, 24 interface languages in Preferences).
|
||||
The entry counts without percentages were measured on 2026-10-12; they are not percentages of the older 2,143-key snapshot above. Their presence does not imply a detailed linguistic or runtime review.
|
||||
Acrobat languages PdfCraft lacks: Danish, Dutch, Finnish, Norwegian, Swedish, Hebrew.
|
||||
|
||||
@@ -75,5 +79,6 @@ Acrobat languages PdfCraft lacks: Danish, Dutch, Finnish, Norwegian, Swedish, He
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | minor | Indonesian and Vietnamese ship as full 2,578-key catalogs with month and weekday names; the key union is 2,578 and the dimension re-measures at ≈ 76% (Hindi and Korean are the two languages still missing) |
|
||||
| 2026-10-12 | minor | Added current counts for Kazakh, Polish, Slovenian, Greek, Turkish and Lithuanian; retained existing language assessments and the original percentage snapshot |
|
||||
| 2026-10-10 | major | Created. Catalog counts measured from `crates/ui-egui/src/i18n/*.tsv` against the 2,143-key union; Acrobat's localizations from its bundle's `.lproj` and Sequences folder names |
|
||||
|
||||
+11
-8
@@ -1,10 +1,11 @@
|
||||
# Interface language
|
||||
|
||||
Choose **Menu > Edit > Preferences…** (Command-comma on macOS, Ctrl-comma elsewhere) **> Interface language** and select **Auto**, **English**, **日本語**, **简体中文**, **繁體中文**, **Čeština**, **Português (Brasil)**, **Deutsch**, **Español**, **Français**, **Русский**, **Қазақша**, **Български**, **తెలుగు**, **Magyar**, **Українська**, **Italiano**, **العربية**, **Polski**, **Slovenščina**, **Ελληνικά**, **Türkçe** or **Lietuvių**. The change applies immediately and persists between launches. Command ids, document contents and file names are unchanged.
|
||||
Choose **Menu > Edit > Preferences…** (Command-comma on macOS, Ctrl-comma elsewhere) **> Interface language** and select **Auto**, **English**, **日本語**, **简体中文**, **繁體中文**, **Čeština**, **Português (Brasil)**, **Deutsch**, **Español**, **Français**, **Русский**, **Қазақша**, **Български**, **తెలుగు**, **Magyar**, **Українська**, **Italiano**, **العربية**, **Polski**, **Slovenščina**, **Ελληνικά**, **Türkçe**, **Lietuvių**, **Bahasa Indonesia** or **Tiếng Việt**. The change applies immediately and persists between launches. Command ids, document contents and file names are unchanged.
|
||||
|
||||
Displayed keyboard shortcuts use the same active interface language. For example, German shows `Strg+K` and `Strg+Umschalt+S` in the toolbar, menus, command palette, tooltips and Keyboard shortcuts dialog; macOS keeps its modifier symbols (`⌘`, `⇧`, `⌃`). Key names have their own `key` catalog context, so the Delete key can read `Entf` while the command remains `Löschen`. A missing key-name entry falls back to the English key name. Actual bindings and machine-facing command ids remain stable.
|
||||
|
||||
**Auto** (the default) follows the system language: `LC_ALL`, `LC_MESSAGES` or `LANG`, then the preferred-languages list on macOS. Any Portuguese locale (`pt_BR`, `pt_PT`) uses the Brazilian catalog, any French locale (`fr`, `fr_FR`, `fr_CA`, `fr_BE`) the French one, any German locale (`de`, `de_DE`, `de_AT`, `de_CH`) the German one, and Chinese locales for Taiwan, Hong Kong and Macau (`zh_TW`, `zh_HK`, `zh_MO`, `zh-Hant`) the Traditional Chinese one; other Chinese locales (`zh`, `zh_CN`, `zh_SG`, `zh-Hans`) the Simplified Chinese one. Telugu locales (`te`, `te_IN`) use the Telugu catalog, Bulgarian locales (`bg`, `bg_BG`) the Bulgarian one, and Hungarian locales (`hu`, `hu_HU`) the Hungarian one, and Ukrainian locales (`uk`, `uk_UA`) the Ukrainian one, and Italian locales (`it`, `it_IT`, `it_CH`) the Italian one, and Arabic locales (`ar`, `ar_MA`, `ar_EG`, `ar_SA`, …) the Arabic one, and Turkish locales (`tr`, `tr_TR`) the Turkish one, and Slovenian locales (`sl`, `sl_SI`) the Slovenian one. A system language without a catalog shows English. On Windows, **Auto** uses the Windows display languages, in preference order, when no supported locale is selected by those environment variables. Neutral `C`/`POSIX` locale values (including `C.UTF-8`) inherited from a launcher do not override the Windows display language; explicitly named supported locales such as `fr_FR.UTF-8` still do. The query asks Windows directly (`GetUserPreferredUILanguages`, through the `sys-locale` crate) without starting a process, and is cached for the process. If detection fails or the display language has no catalog, PdfCraft shows English.
|
||||
**Auto** (the default) follows the system language: `LC_ALL`, `LC_MESSAGES` or `LANG`, then the preferred-languages list on macOS. Any Portuguese locale (`pt_BR`, `pt_PT`) uses the Brazilian catalog, any French locale (`fr`, `fr_FR`, `fr_CA`, `fr_BE`) the French one, any German locale (`de`, `de_DE`, `de_AT`, `de_CH`) the German one, and Chinese locales for Taiwan, Hong Kong and Macau (`zh_TW`, `zh_HK`, `zh_MO`, `zh-Hant`) the Traditional Chinese one; other Chinese locales (`zh`, `zh_CN`, `zh_SG`, `zh-Hans`) the Simplified Chinese one. Telugu locales (`te`, `te_IN`) use the Telugu catalog, Bulgarian locales (`bg`, `bg_BG`) the Bulgarian one, and Hungarian locales (`hu`, `hu_HU`) the Hungarian one, and Ukrainian locales (`uk`, `uk_UA`) the Ukrainian one, and Italian locales (`it`, `it_IT`, `it_CH`) the Italian one, and Arabic locales (`ar`, `ar_MA`, `ar_EG`, `ar_SA`, …) the Arabic one, and Turkish locales (`tr`, `tr_TR`) the Turkish one, and Indonesian locales (`id`, `id_ID`) the Indonesian one, and Vietnamese locales (`vi`, `vi_VN`) the Vietnamese one. A system language without a catalog shows English. On Windows, **Auto** uses the Windows display languages, in preference order, when no supported locale is selected by those environment variables. Neutral `C`/`POSIX` locale values (including `C.UTF-8`) inherited from a launcher do not override the Windows display language; explicitly named supported locales such as `fr_FR.UTF-8` still do. The query asks Windows directly (`GetUserPreferredUILanguages`, through the `sys-locale` crate) without starting a process, and is cached for the process. If detection fails or the display language has no catalog, PdfCraft shows English.
|
||||
**Auto** (the default) follows the system language: `LC_ALL`, `LC_MESSAGES` or `LANG`, then the preferred-languages list on macOS. Any Portuguese locale (`pt_BR`, `pt_PT`) uses the Brazilian catalog, any French locale (`fr`, `fr_FR`, `fr_CA`, `fr_BE`) the French one, any German locale (`de`, `de_DE`, `de_AT`, `de_CH`) the German one, and Chinese locales for Taiwan, Hong Kong and Macau (`zh_TW`, `zh_HK`, `zh_MO`, `zh-Hant`) the Traditional Chinese one; other Chinese locales (`zh`, `zh_CN`, `zh_SG`, `zh-Hans`) the Simplified Chinese one. Telugu locales (`te`, `te_IN`) use the Telugu catalog, Bulgarian locales (`bg`, `bg_BG`) the Bulgarian one, and Hungarian locales (`hu`, `hu_HU`) the Hungarian one, and Ukrainian locales (`uk`, `uk_UA`) the Ukrainian one, and Italian locales (`it`, `it_IT`, `it_CH`) the Italian one, and Arabic locales (`ar`, `ar_MA`, `ar_EG`, `ar_SA`, …) the Arabic one, and Slovenian locales (`sl`, `sl_SI`) the Slovenian one, and Indonesian locales (`id`, `id_ID`) the Indonesian one, and Vietnamese locales (`vi`, `vi_VN`) the Vietnamese one. A system language without a catalog shows English. On Windows, **Auto** uses the Windows display languages, in preference order, when no supported locale is selected by those environment variables. Neutral `C`/`POSIX` locale values (including `C.UTF-8`) inherited from a launcher do not override the Windows display language; explicitly named supported locales such as `fr_FR.UTF-8` still do. The query asks Windows directly (`GetUserPreferredUILanguages`, through the `sys-locale` crate) without starting a process, and is cached for the process. If detection fails or the display language has no catalog, PdfCraft shows English.
|
||||
|
||||
The control channel exposes the setting through `ui.set`:
|
||||
|
||||
@@ -12,10 +13,10 @@ The control channel exposes the setting through `ui.set`:
|
||||
{"method":"ui.set","params":{"key":"language","value":"ja"}}
|
||||
```
|
||||
|
||||
The value is `auto` or a language code (`en`, `ja`, `zh-hans`, `zh-hant`, `cs`, `pt-br`, `de`, `es`, `fr`, `ru`, `kk`, `bg`, `te`, `hu`, `uk`, `it`, `ar`, `pl`, `sl`, `el`, `tr`, `lt`), in any case. `ui.state` reports `language` as written in the preferences (`auto`, `en`, `ja`, `zh-hans`, `zh-hant`, `cs`, `pt-br`, `de`, `es`, `fr`, `ru`, `kk`, `bg`, `te`, `hu`, `uk`, `it`, `ar`, `pl`, `sl`, `el`, `tr` or `lt`). Unknown values return an error without changing the current setting. Preferences saved before this setting existed follow the system language.
|
||||
The value is `auto` or a language code (`en`, `ja`, `zh-hans`, `zh-hant`, `cs`, `pt-br`, `de`, `es`, `fr`, `ru`, `kk`, `bg`, `te`, `hu`, `uk`, `it`, `ar`, `pl`, `sl`, `el`, `tr`, `lt`, `id`, `vi`), in any case. `ui.state` reports `language` as written in the preferences (`auto`, `en`, `ja`, `zh-hans`, `zh-hant`, `cs`, `pt-br`, `de`, `es`, `fr`, `ru`, `kk`, `bg`, `te`, `hu`, `uk`, `it`, `ar`, `pl`, `sl`, `el`, `tr`, `lt`, `id` or `vi`). Unknown values return an error without changing the current setting. Preferences saved before this setting existed follow the system language.
|
||||
|
||||
Dialogs, panels, menus, notices and history labels go through the catalog (`tl!`). Japanese, Traditional Chinese, Simplified Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese and Arabic translate them (about 2,000–2,140 entries each); Czech and Turkish cover the menus so far. Untranslated labels use English. Error details that come from the engine or the operating system are shown as they are, inside a translated frame ("操作「…」に失敗しました: …"). The command palette matches the translated label, the English label and the command id. Vertical Japanese PDF rendering is an existing viewer feature; this change does not add vertical text editing. Per-language coverage, script support and what each language still needs are in [localization-parity.md](localization-parity.md).
|
||||
Dialogs, panels, menus, notices and history labels go through the catalog (`tl!`). Japanese, Traditional Chinese, Simplified Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese, Arabic and Slovenian translate them (about 2,000–2,190 entries each); Czech covers the menus so far. Untranslated labels use English. Error details that come from the engine or the operating system are shown as they are, inside a translated frame ("操作「…」に失敗しました: …"). The command palette matches the translated label, the English label and the command id. Vertical Japanese PDF rendering is an existing viewer feature; this change does not add vertical text editing. Per-language coverage, script support and what each language still needs are in [localization-parity.md](localization-parity.md).
|
||||
Dialogs, panels, menus, notices and history labels go through the catalog (`tl!`). Japanese, Traditional Chinese, Simplified Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese and Arabic translate them (about 2,000–2,140 entries each); Czech and Turkish cover the menus so far. Indonesian and Vietnamese translate every key in the union (2,578 entries each, including every registered command, All tools label and `tl!` literal). Untranslated labels use English. Error details that come from the engine or the operating system are shown as they are, inside a translated frame ("操作「…」に失敗しました: …"). The command palette matches the translated label, the English label and the command id. Vertical Japanese PDF rendering is an existing viewer feature; this change does not add vertical text editing. Per-language coverage, script support and what each language still needs are in [localization-parity.md](localization-parity.md).
|
||||
Dialogs, panels, menus, notices and history labels go through the catalog (`tl!`). Japanese, Traditional Chinese, Simplified Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese, Arabic and Slovenian translate them (about 2,000–2,190 entries each); Czech covers the menus so far. Indonesian and Vietnamese translate every key in the union (2,578 entries each). Untranslated labels use English. Error details that come from the engine or the operating system are shown as they are, inside a translated frame ("操作「…」に失敗しました: …"). The command palette matches the translated label, the English label and the command id. Vertical Japanese PDF rendering is an existing viewer feature; this change does not add vertical text editing. Per-language coverage, script support and what each language still needs are in [localization-parity.md](localization-parity.md).
|
||||
|
||||
Whole sentences that the layers below word — what the accessibility checker found, what the PDF/A verifier reports, what a signature's details say, what is wrong with the pages to print, what an edit is called in the history — are translated too, because those layers hand the sentence over apart from the document's own words. See [Sentences the engine words](#sentences-the-engine-words).
|
||||
|
||||
@@ -23,11 +24,13 @@ The Ukrainian catalog has 2,047 entries, written from the meaning of the English
|
||||
|
||||
The Lithuanian catalog has 2,578 entries and three integer plural forms. Calendar headings use standalone month names; dates use genitive forms. Coverage tests check commands/tools, scanned literal and direct lookups, and source/context keys in the other bundled catalogs. Calendar, inspector and password-dialog UI regressions check dynamic labels and preserve document values. A native Lithuanian-speaking contributor has reviewed the translation.
|
||||
|
||||
The Indonesian and Vietnamese catalogs have 2,578 entries each: every `(context, English)` key the union of the bundled catalogs holds, written from the meaning of the English labels with Indonesian computing vocabulary and standard Vietnamese terms. Neither language inflects nouns for number ([CLDR](https://www.unicode.org/cldr/charts/48/supplemental/language_plural_rules.html) `id` and `vi` have one form, `other`), so their `@plural` entries list a single form that reads the same for every count. Catalog regressions require every registered command, every All tools group, section and item, and every `tl!` literal in both, and the font test requires the bundled Latin faces to cover both catalogs, stacked Vietnamese diacritics included. Month and weekday names for stamps are in the same two languages: Indonesian writes them as they stand, Vietnamese lower-cases `tháng` after a day number (`7 tháng Ba`) and keeps it capitalised standing alone (`Tháng Ba`).
|
||||
|
||||
Edit text reads Hebrew lines in typing order and writes them back in drawing order (`crates/edit/src/bidi.rs`); letters the page's own font lacks are drawn with the craft-fonts `Hebr` face (Noto Sans Hebrew) as Type 3 glyphs, and a build without one says so. Hebrew points (niqqud) can't be written yet. The craft-fonts revision pinned for CI and releases has no `Hebr` face yet, so until one is added releases report that error instead of drawing the missing letters.
|
||||
|
||||
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Russian, Bulgarian, Czech, Brazilian Portuguese, German, Spanish, French, Hungarian, Italian, Turkish and Lithuanian need only the bundled Latin faces (tested in `tests/fonts.rs`). Hungarian uses the same one/other plural rule as English: 1 is singular, and every other count — including 0 — is the other form. After a numeral the noun stays singular (`1 oldal`, `2 oldal`). Telugu is drawn with a craft-fonts `Telu` face (Noto Sans Telugu, kept in the web build too), whose conjuncts egui shapes; a build without one shows Telugu labels as replacement boxes, and the release-pinned craft-fonts input has no Telugu face yet.
|
||||
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Russian, Bulgarian, Czech, Brazilian Portuguese, German, Spanish, French, Hungarian, Italian and Slovenian need only the bundled Latin faces (tested in `tests/fonts.rs`). Slovenian has four plural forms: one (1, 101, 201…), two (2, 102…), few (3, 4, 103…) and other (everything else, including 0, 5–20 and 21+), so `@plural` entries list four forms (`1 stran`, `2 strani`, `3 strani`, `5 strani`). Hungarian uses the same one/other plural rule as English: 1 is singular, and every other count — including 0 — is the other form. After a numeral the noun stays singular (`1 oldal`, `2 oldal`). Telugu is drawn with a craft-fonts `Telu` face (Noto Sans Telugu, kept in the web build too), whose conjuncts egui shapes; a build without one shows Telugu labels as replacement boxes, and the release-pinned craft-fonts input has no Telugu face yet.
|
||||
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Until craft-fonts has an allowed `Hans` face, the desktop app draws a Chinese interface with a Chinese face already installed on the machine (Microsoft YaHei or JhengHei on Windows, PingFang, Hiragino Sans GB or STHeiti on macOS, WenQuanYi or Droid Sans Fallback on Linux; never Noto CJK or Source Han), placed before the Japanese faces so a line keeps one face (#826). The web build has no installed fonts to fall back on. Russian, Bulgarian, Czech, Brazilian Portuguese, German, Spanish, French, Hungarian and Italian need only the bundled Latin faces (tested in `tests/fonts.rs`). Hungarian uses the same one/other plural rule as English: 1 is singular, and every other count — including 0 — is the other form. After a numeral the noun stays singular (`1 oldal`, `2 oldal`). Telugu is drawn with a craft-fonts `Telu` face (Noto Sans Telugu, kept in the web build too), whose conjuncts egui shapes; a build without one shows Telugu labels as replacement boxes, and the release-pinned craft-fonts input has no Telugu face yet.
|
||||
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Russian, Bulgarian, Czech, Brazilian Portuguese, German, Spanish, French, Hungarian, Italian, Turkish, Lithuanian, Indonesian and Vietnamese need only the bundled Latin faces (tested in `tests/fonts.rs`). Hungarian uses the same one/other plural rule as English: 1 is singular, and every other count — including 0 — is the other form. After a numeral the noun stays singular (`1 oldal`, `2 oldal`). Telugu is drawn with a craft-fonts `Telu` face (Noto Sans Telugu, kept in the web build too), whose conjuncts egui shapes; a build without one shows Telugu labels as replacement boxes, and the release-pinned craft-fonts input has no Telugu face yet.
|
||||
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Russian, Bulgarian, Czech, Brazilian Portuguese, German, Spanish, French, Hungarian, Italian, Slovenian, Indonesian and Vietnamese need only the bundled Latin faces (tested in `tests/fonts.rs`). Slovenian has four plural forms: one (1, 101, 201…), two (2, 102…), few (3, 4, 103…) and other (everything else, including 0, 5–20 and 21+), so `@plural` entries list four forms (`1 stran`, `2 strani`, `3 strani`, `5 strani`). Hungarian uses the same one/other plural rule as English: 1 is singular, and every other count — including 0 — is the other form. After a numeral the noun stays singular (`1 oldal`, `2 oldal`). Telugu is drawn with a craft-fonts `Telu` face (Noto Sans Telugu, kept in the web build too), whose conjuncts egui shapes; a build without one shows Telugu labels as replacement boxes, and the release-pinned craft-fonts input has no Telugu face yet.
|
||||
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Until craft-fonts has an allowed `Hans` face, the desktop app draws a Chinese interface with a Chinese face already installed on the machine (Microsoft YaHei or JhengHei on Windows, PingFang, Hiragino Sans GB or STHeiti on macOS, WenQuanYi or Droid Sans Fallback on Linux; never Noto CJK or Source Han), placed before the Japanese faces so a line keeps one face (#826). The web build has no installed fonts to fall back on. Russian, Bulgarian, Czech, Brazilian Portuguese, German, Spanish, French, Hungarian, Italian, Indonesian and Vietnamese need only the bundled Latin faces (tested in `tests/fonts.rs`). Hungarian uses the same one/other plural rule as English: 1 is singular, and every other count — including 0 — is the other form. After a numeral the noun stays singular (`1 oldal`, `2 oldal`). Telugu is drawn with a craft-fonts `Telu` face (Noto Sans Telugu, kept in the web build too), whose conjuncts egui shapes; a build without one shows Telugu labels as replacement boxes, and the release-pinned craft-fonts input has no Telugu face yet.
|
||||
|
||||
The Brazilian Portuguese catalog has 2,000 entries: the ~1,826-entry gist André Cardoso posted in [#288](https://github.com/storytold/pdfcraft/issues/288), the labels added since, and a review of the existing wording. Portuguese, like French, treats 0 and 1 as singular (`1 página`, `2 páginas`).
|
||||
|
||||
|
||||
+2
-2
@@ -116,9 +116,9 @@ total matches the parity estimate.
|
||||
| M13 | Automation (MCP, Action Wizard, CLI) + AI providers | 60–120 | 45% | 25–45 | Done: MCP resources (document info, text, page images); headless tool table (123 tools incl. signing, optimizing, initial view, links, stamps, data exchange, comment review, forms authoring and scripts, redaction, sanitize, print, add content), opt-in MCP server over stdio, CLI `run`/`tools` (closed stdout pipes exit cleanly), UI control channel with drag. Missing: Action Wizard, AI providers<br>Done since: CLI run and MCP reject missing, empty, repeated and option-valued --root arguments before any tool/script execution or MCP initialization. A single-hyphen directory name remains valid; synthetic CLI regressions preserve a would-be overwritten file. `text_find` also caps the per-page match ranges it materializes at the remaining result limit plus one continuation match. CLI robustness scans visit each directory once through overlapping roots and symlink aliases. |
|
||||
| M12 | Accessibility, compare, measure, search, XFA | 200–380 | 30% | 90–160 | Done: new `a11y` crate with the Accessibility Checker (all 32 rules, report, Fix/Skip/Explain, options dialog and results panel, agent tools). Locatable figure, annotation and form findings highlight their page bounds.<br>Done since: 2D distance, perimeter and area measurements with persistent viewport calibration, snapping, live information and CSV export; a bounded text-only auto-tag proposal tool; text-only Apply writes marked content, paragraph tags and a parent tree, then opens the Tags panel for review. Missing: mixed-content and field autotag, heading/list/table/figure classification, Order/Content panels, Reading Order tool, compare, geospatial/3D measurement, search index, XFA |
|
||||
| M13 | Automation (MCP, Action Wizard, CLI) + AI providers | 60–120 | 45% | 25–45 | Done: MCP resources (document info, text, page images); headless tool table (123 tools incl. signing, optimizing, initial view, links, stamps, data exchange, comment review, forms authoring and scripts, redaction, sanitize, print, add content), opt-in MCP server over stdio, CLI `run`/`tools` (closed stdout pipes exit cleanly), UI control channel with drag. Missing: Action Wizard, AI providers<br>Done since: CLI run and MCP reject missing, empty, repeated and option-valued --root arguments before any tool/script execution or MCP initialization. A single-hyphen directory name remains valid; synthetic CLI regressions preserve a would-be overwritten file. `text_find` also caps the per-page match ranges it materializes at the remaining result limit plus one continuation match. |
|
||||
| M14 | 1.0 polish: performance, localization, installers | 120–250 | 15% | 80–150 | Done: PhotoCraft's translation system (`i18n/`: TSV catalogs, `tl!`, command-id and plural entries, system-language detection, strict catalog tests); every dialog, panel and notice goes through `tl!`. Catalogs of ≈ 2,000–2,140 entries for Japanese, Simplified and Traditional Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese and Arabic (logical-order catalog put into display order; layout not yet mirrored); Czech covers the menus. Signed and notarized macOS builds, signed Windows installers (x64, x86, ARM64), AppImage, deb, rpm, Flatpak and FreeBSD packages. #307 (bounded large-PDF memory): shared resource indexes and parser metadata, lazy inspection, bounded caches; a 9,156-page file inspects in ≈ 1.8 s at ≈ 500 MiB instead of exceeding 10 GiB. Edits let go of the previous working file before writing the next (a 1 GiB file peaks at two copies while editing, not three; gap 13). Shortcut display uses localized modifier/key names across menus, toolbar, palette, Help and tooltips (#496). Lithuanian adds a 2,578-entry catalog and date names, reviewed by a native-speaking contributor. Validated candidate (macOS raster probe; Windows runtime unverified): the Windows policy changes only embedded JetBrains Mono; horizontal alpha-edge energy per ink improves 2.78%, 3.87% and 1.59% at 100%, 125% and 150%, and is down 0.79% at 200%. Inter is pixel-identical; layout hashes, glyph coverage and the 8 MiB atlas are unchanged. Timing and RSS ranges overlap, with no improvement claimed. Estimates unchanged. Missing: a right-to-left layout, a Chinese UI face in releases, Hindi/Indonesian/Korean/Vietnamese, file-backed input and global memory budgets, performance budgets, keyboard-only operation |
|
||||
| M14 | 1.0 polish: performance, localization, installers | 120–250 | 15% | 80–150 | Done: PhotoCraft's translation system (`i18n/`: TSV catalogs, `tl!`, command-id and plural entries, system-language detection, strict catalog tests); every dialog, panel and notice goes through `tl!`. Catalogs of ≈ 2,000–2,140 entries for Japanese, Simplified and Traditional Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese and Arabic (logical-order catalog put into display order; layout not yet mirrored); Czech covers the menus. Indonesian and Vietnamese add full 2,578-key catalogs (2026-10-13). Signed and notarized macOS builds, signed Windows installers (x64, x86, ARM64), AppImage, deb, rpm, Flatpak and FreeBSD packages. #307 (bounded large-PDF memory): shared resource indexes and parser metadata, lazy inspection, bounded caches; a 9,156-page file inspects in ≈ 1.8 s at ≈ 500 MiB instead of exceeding 10 GiB. Edits let go of the previous working file before writing the next (a 1 GiB file peaks at two copies while editing, not three; gap 13). Shortcut display uses localized modifier/key names across menus, toolbar, palette, Help and tooltips (#496). Lithuanian adds a 2,578-entry catalog and date names, reviewed by a native-speaking contributor. Validated candidate (macOS raster probe; Windows runtime unverified): the Windows policy changes only embedded JetBrains Mono; horizontal alpha-edge energy per ink improves 2.78%, 3.87% and 1.59% at 100%, 125% and 150%, and is down 0.79% at 200%. Inter is pixel-identical; layout hashes, glyph coverage and the 8 MiB atlas are unchanged. Timing and RSS ranges overlap, with no improvement claimed. Estimates unchanged. Missing: a right-to-left layout, a Chinese UI face in releases, Hindi/Korean catalogs and their Devanagari and Hangul faces, file-backed input and global memory budgets, performance budgets, keyboard-only operation |
|
||||
| M13 | Automation (MCP, Action Wizard, CLI) + AI providers | 60–120 | 45% | 25–45 | Done: MCP resources (document info, text, page images); headless tool table (123 tools incl. signing, optimizing, initial view, links, stamps, data exchange, comment review, forms authoring and scripts, redaction, sanitize, print, add content), opt-in MCP server over stdio, CLI `run`/`tools` (closed stdout pipes exit cleanly), UI control channel with drag. Missing: Action Wizard, AI providers |
|
||||
| M14 | 1.0 polish: performance, localization, installers | 120–250 | 15% | 80–150 | Done: PhotoCraft's translation system (`i18n/`: TSV catalogs, `tl!`, command-id and plural entries, system-language detection, strict catalog tests); every dialog, panel and notice goes through `tl!`. Catalogs of ≈ 2,000–2,140 entries for Japanese, Simplified and Traditional Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese and Arabic (logical-order catalog put into display order; layout not yet mirrored); Czech covers the menus. Signed and notarized macOS builds, signed Windows installers (x64, x86, ARM64), AppImage, deb, rpm, Flatpak and FreeBSD packages. #307 (bounded large-PDF memory): shared resource indexes and parser metadata, lazy inspection, bounded caches; a 9,156-page file inspects in ≈ 1.8 s at ≈ 500 MiB instead of exceeding 10 GiB. Edits let go of the previous working file before writing the next (a 1 GiB file peaks at two copies while editing, not three; gap 13). Shortcut display uses localized modifier/key names across menus, toolbar, palette, Help and tooltips (#496). Traditional Chinese accessibility statuses, rule semantics and Taiwan form/table terms have catalog regressions. Missing: a right-to-left layout, a Chinese UI face in releases, Hindi/Indonesian/Korean/Vietnamese, file-backed input and global memory budgets, performance budgets, keyboard-only operation |
|
||||
| M14 | 1.0 polish: performance, localization, installers | 120–250 | 15% | 80–150 | Done: PhotoCraft's translation system (`i18n/`: TSV catalogs, `tl!`, command-id and plural entries, system-language detection, strict catalog tests); every dialog, panel and notice goes through `tl!`. Catalogs of ≈ 2,000–2,140 entries for Japanese, Simplified and Traditional Chinese, Russian, Bulgarian, German, Spanish, French, Telugu, Hungarian, Ukrainian, Italian, Brazilian Portuguese and Arabic (logical-order catalog put into display order; layout not yet mirrored); Czech covers the menus. Indonesian and Vietnamese add full 2,578-key catalogs (2026-10-13). Signed and notarized macOS builds, signed Windows installers (x64, x86, ARM64), AppImage, deb, rpm, Flatpak and FreeBSD packages. #307 (bounded large-PDF memory): shared resource indexes and parser metadata, lazy inspection, bounded caches; a 9,156-page file inspects in ≈ 1.8 s at ≈ 500 MiB instead of exceeding 10 GiB. Edits let go of the previous working file before writing the next (a 1 GiB file peaks at two copies while editing, not three; gap 13). Shortcut display uses localized modifier/key names across menus, toolbar, palette, Help and tooltips (#496). Traditional Chinese accessibility statuses, rule semantics and Taiwan form/table terms have catalog regressions. Missing: a right-to-left layout, a Chinese UI face in releases, Hindi/Korean catalogs and their Devanagari and Hangul faces, file-backed input and global memory budgets, performance budgets, keyboard-only operation |
|
||||
| | **Total** | **2,085–3,840** | **≈ 49% ready** | **≈ 770–1,400** | Area view of the same work: [target-app-parity.md](target-app-parity.md#by-feature-area) |
|
||||
|
||||
## Upcoming milestones, ranked
|
||||
|
||||
@@ -181,7 +181,7 @@ features total and are shown for orientation; the others add to it.
|
||||
| File formats and conversion | ≈ 40% | 12% | 120–220 | within features | PDF read/write strong, encrypted output included (#774 fixed); export Word is layout-poor (#773); no Excel/PowerPoint export, no Office/HTML/PostScript import | [file-format-parity.md](file-format-parity.md) |
|
||||
| PDF specification and standards | ≈ 60% | 8% | 150–260 | within features | ISO 32000 syntax, encryption, annotations and AcroForm strong; rendering and fonts borrowed from `hayro`; PDF 2.0 extras, linearization, public-key security absent; PDF/A partial, PDF/X/UA none | [pdf-spec-parity.md](pdf-spec-parity.md) |
|
||||
| Hardware | ≈ 35% | 4% | 25–50 | additional | Page raster is CPU only (Acrobat has GPU rendering); Windows CNG and macOS Keychain keys sign, no PKCS #11; no scanners; web printing goes through the browser's print dialog as pictures, not vectors | [hardware-parity.md](hardware-parity.md) |
|
||||
| Localization | ≈ 59% over the 12 key languages | 5% | 35–60 | additional | 15 catalogs, 13 at 94–100%; Chinese UI shows tofu in releases (#826, #688); Arabic not mirrored; no Hindi, Indonesian, Korean, Vietnamese | [localization-parity.md](localization-parity.md) |
|
||||
| Localization | ≈ 76% over the 12 key languages | 5% | 25–50 | additional | 17 catalogs; Chinese UI shows tofu in releases (#826, #688); Arabic not mirrored; no Hindi or Korean | [localization-parity.md](localization-parity.md) |
|
||||
| Performance | ≈ 40% | 6% | 25–45 | additional | 500-page smooth scrolling and large-file memory work (#307) done; no performance budgets (`misc.performance-budgets`), no lazy loading of GB files; blurry text at 1080p/1440p (#730) | [gaps.md](gaps.md) |
|
||||
| Stability | ≈ 50% | 8% | 45–80 | additional | Never-crash rules and nightly fuzzing; 245 open issues on 2026-10-10, many of them `fix(...)` reports of wrong results in shipped features (the #799–#821 batch is fixed) | [gaps.md](gaps.md) |
|
||||
| Platforms | ≈ 70% | 5% | 15–30 | additional | macOS (signed, notarized), Windows x64/x86/ARM64 (signed), Linux (AppImage, deb, rpm, Flatpak), FreeBSD, web (WASM). Acrobat has no Linux, FreeBSD or full web editor; we lack web OCR and web recovery | [hardware-parity.md](hardware-parity.md) |
|
||||
|
||||
Reference in New Issue
Block a user