Add Spanish (es) interface catalog (#251)
Registers Español in the language list, with a full catalog covering commands, dialogs, panels and notices, plus tests and docs. Claude-Session: https://claude.ai/code/session_01A3kqJC1D3PAgT3dHxnqYNy Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
parent
7b1c015d06
commit
4db49900c6
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Every asset PdfCraft includes, bundles or uses to build its published material, with its author, source and licence. The policy is in [AGENTS.md](AGENTS.md) §1. The machine-readable list, with SHA-256 hashes, is [ATTRIBUTION.toml](ATTRIBUTION.toml). Licence texts are kept beside the assets and summarised in [NOTICE](NOTICE).
|
||||
|
||||
## In this repository (247)
|
||||
## In this repository (248)
|
||||
|
||||
| Asset | Title | Author | Licence | Source | Used for |
|
||||
|---|---|---|---|---|---|
|
||||
|
|
@ -12,6 +12,7 @@ Every asset PdfCraft includes, bundles or uses to build its published material,
|
|||
| `crates/ui-egui/src/i18n/zh-hant.tsv` | Traditional Chinese (Taiwan) interface translation catalog | PdfCraft contributors | MIT OR Apache-2.0 | Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources | Traditional Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs |
|
||||
| `crates/ui-egui/src/i18n/cs.tsv` | Czech interface translation catalog | PdfCraft contributors | MIT OR Apache-2.0 | Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources | Czech UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs |
|
||||
| `crates/ui-egui/src/i18n/pt-br.tsv` | Brazilian Portuguese interface translation catalog | PdfCraft contributors | MIT OR Apache-2.0 | Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources | Brazilian Portuguese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs |
|
||||
| `crates/ui-egui/src/i18n/es.tsv` | Spanish interface translation catalog | PdfCraft contributors | MIT OR Apache-2.0 | Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources | Spanish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs |
|
||||
| `crates/ui-egui/src/i18n/ja.tsv` | Japanese interface translation catalog | PdfCraft contributors | MIT OR Apache-2.0 | Contributor-original: clean-room translations of PdfCraft's English UI labels, no proprietary localisation resources | Japanese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs |
|
||||
| `assets/icons/accessibility.svg` | Lucide icon "accessibility" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/accessibility.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
|
||||
| `assets/icons/align-left.svg` | Lucide icon "align-left" | Lucide Contributors (portions Cole Bemis, Feather) | ISC | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/align-left.svg) | UI icon, embedded via crates/ui-egui/src/icon_data.rs |
|
||||
|
|
|
|||
|
|
@ -54,6 +54,17 @@ kind = "translation"
|
|||
usage = "Brazilian Portuguese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "0ee4d2f630623ef7e06413a7c34a4a0269a2600fba682f06988a1346be4d07a5"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/es.tsv"
|
||||
title = "Spanish 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 = "Spanish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "db79f5eb4ac0072bb42186838d0a9e089652c7bd95f679fd2431c79c74725b74"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ja.tsv"
|
||||
title = "Japanese interface translation catalog"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -70,7 +70,7 @@ fn plural_pt(n: u64) -> usize {
|
|||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 6] = [
|
||||
pub static LANGUAGES: [LangInfo; 7] = [
|
||||
LangInfo { code: "en", name: "English", source: "", plural: plural_one_other, catalog: OnceLock::new() },
|
||||
LangInfo { code: "ja", name: "日本語", source: include_str!("ja.tsv"), plural: plural_none, catalog: OnceLock::new() },
|
||||
// Simplified Chinese; `zh`, `zh-CN`, `zh-SG` and `zh-Hans-*` locales resolve here (see `candidates`).
|
||||
|
|
@ -81,6 +81,8 @@ pub static LANGUAGES: [LangInfo; 6] = [
|
|||
LangInfo { code: "cs", name: "Čeština", source: include_str!("cs.tsv"), plural: plural_cs, catalog: OnceLock::new() },
|
||||
// Brazilian Portuguese; `pt`, `pt-BR` and `pt-PT` locales all resolve here (see `candidates`).
|
||||
LangInfo { code: "pt-br", name: "Português (Brasil)", source: include_str!("pt-br.tsv"), plural: plural_pt, catalog: OnceLock::new() },
|
||||
// Spanish (European vocabulary); every `es-*` locale (`es-ES`, `es-MX`, `es-419` ...) resolves here.
|
||||
LangInfo { code: "es", name: "Español", source: include_str!("es.tsv"), plural: plural_one_other, catalog: OnceLock::new() },
|
||||
];
|
||||
|
||||
impl LangInfo {
|
||||
|
|
@ -662,6 +664,45 @@ mod tests {
|
|||
assert_eq!(restored.language, "pt-br");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spanish_is_registered() {
|
||||
let es = Lang::from_code("es").expect("es registered");
|
||||
assert_eq!(es.name(), "Español");
|
||||
assert_eq!(normalize_pref("ES"), Some("es"));
|
||||
assert_eq!(lang_from_tag("es_ES.UTF-8"), Some(es));
|
||||
assert_eq!(lang_from_tag("es-MX"), Some(es));
|
||||
assert_eq!(first_supported("es-ES\r\nen-US"), Some(es));
|
||||
assert_eq!(tr(es, "File"), "Archivo");
|
||||
assert_eq!(tr(es, "Save as…"), "Guardar como…");
|
||||
assert_eq!(tr(es, "Informe del usuario.pdf"), "Informe del usuario.pdf");
|
||||
assert_eq!(trn(es, 1, "{n} page", "{n} pages"), "1 página");
|
||||
assert_eq!(trn(es, 3, "{n} page", "{n} pages"), "3 páginas");
|
||||
let mut app = crate::PdfCraftApp::default();
|
||||
app.set_option("language", "es").unwrap();
|
||||
assert_eq!(app.language, "es");
|
||||
let mut restored = crate::PdfCraftApp::default();
|
||||
restored.restore(&app.persist());
|
||||
assert_eq!(restored.language, "es");
|
||||
}
|
||||
|
||||
/// Spanish translates every registered command and every All tools group, section and item.
|
||||
#[test]
|
||||
fn spanish_covers_commands_and_catalogue() {
|
||||
let es = Lang::from_code("es").expect("es registered");
|
||||
for command in pdfcraft_engine::commands::COMMANDS {
|
||||
assert!(has(es, command.label), "missing command: {}", command.label);
|
||||
}
|
||||
for group in pdfcraft_engine::catalog::TOOL_GROUPS {
|
||||
assert!(has(es, group.label), "missing group: {}", group.label);
|
||||
for section in group.sections {
|
||||
assert!(has(es, section.title), "missing section: {}", section.title);
|
||||
for item in section.items {
|
||||
assert!(has(es, item.label), "missing item: {}", item.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Czech covers every registered menu title and menu command label (#68), keeps ellipses, and
|
||||
/// only leaves untranslated what is deliberately the same in Czech.
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -87,15 +87,17 @@ fn ui_fonts_work_without_craft_fonts() {
|
|||
theme::install_fonts(&ctx);
|
||||
}
|
||||
|
||||
/// Latin-script catalogs (Czech, Brazilian Portuguese) are drawn entirely by the app's own faces
|
||||
/// Latin-script catalogs (Czech, Brazilian Portuguese, Spanish) 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]
|
||||
fn primary_ui_fonts_cover_latin_catalogs() {
|
||||
use skrifa::MetadataProvider as _;
|
||||
let defs = theme::font_definitions();
|
||||
for (code, catalog) in [("cs", include_str!("../src/i18n/cs.tsv")), ("pt-br", include_str!("../src/i18n/pt-br.tsv"))] {
|
||||
let mut text = String::from("áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽãõçâêôàÃÕÇÂÊÔÀ…");
|
||||
for (code, catalog) in
|
||||
[("cs", include_str!("../src/i18n/cs.tsv")), ("pt-br", include_str!("../src/i18n/pt-br.tsv")), ("es", include_str!("../src/i18n/es.tsv"))]
|
||||
{
|
||||
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()));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Interface language
|
||||
|
||||
Choose **Menu > Edit > Preferences…** (Command-comma on macOS, Ctrl-comma elsewhere) **> Interface language** and select **Auto**, **English**, **日本語**, **简体中文**, **繁體中文**, **Čeština** or **Português (Brasil)**. 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)** or **Español**. The change applies immediately and persists between launches. Command ids, document contents and file names are unchanged.
|
||||
|
||||
**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, 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. A system language without a catalog, such as French, shows English. On Windows, **Auto** uses the Windows display languages, in preference order, when no supported locale is selected by those environment variables. 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.
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ 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`), in any case. `ui.state` reports `language` as written in the preferences (`auto`, `en`, `ja`, `zh-hans`, `zh-hant`, `cs` or `pt-br`). 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`, `es`), in any case. `ui.state` reports `language` as written in the preferences (`auto`, `en`, `ja`, `zh-hans`, `zh-hant`, `cs`, `pt-br` or `es`). 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 and Simplified Chinese translate them (about 1,800 entries each); Czech and Brazilian Portuguese 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.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue