docs(i18n): document language packs across user, dev, and marketing docs
README + USER-GUIDE describe the sidebar picker and current coverage (home + shared chrome, per-tool bodies pending). DEVELOPER gains a how-to for adding packs and keys with the parity-test guarantee. TECHNICAL §10b records the in-house-JSON architecture and locks in the no-gettext decision (also logged in DECISIONS). REQUIREMENTS reflects the new interface surface and updated test count. COPY.md adds a "Language claim" slot so landing/email work can pick it up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ CLI (src/cli*.py) GUI (src/gui/app.py + pages/)
|
||||
|
||||
| Module | Public surface |
|
||||
|--------|----------------|
|
||||
| `i18n` | `t(key, lang=None, **fmt)`, `current_language()`, `set_language()`, `render_language_selector()`, `LANGUAGES` |
|
||||
| `core.dedup` | `deduplicate()`, `MatchStrategy`, `ColumnMatchStrategy`, `Algorithm`, `SurvivorRule`, `DeduplicationResult`, `MatchResult`, `build_default_strategies()` |
|
||||
| `core.normalizers` | `normalize_email/phone/name/address/string`, `NormalizerType`, `get_normalizer()` |
|
||||
| `core.io` | `read_file()`, `write_file()`, `list_sheets()`, `detect_encoding/delimiter/header_row`, `repair_bytes()` |
|
||||
@@ -95,6 +96,36 @@ DeduplicationResult # deduplicated_df, removed_df, match_groups, l
|
||||
|
||||
No other call sites change. Gate auto-discovers it via the registry.
|
||||
|
||||
### i18n — language packs
|
||||
|
||||
The GUI's user-facing strings live in `src/i18n/packs/<code>.json`, keyed by ISO-639-1 code. English (`en.json`) is canonical; missing keys in other packs fall back to English, and missing keys in English fall back to the literal dotted key so a typo is visible rather than silent.
|
||||
|
||||
**Look up a string in code:**
|
||||
```python
|
||||
from src.i18n import t
|
||||
st.button(t("upload.run_button"))
|
||||
st.warning(t("gate.warning", name=filename)) # {name} interpolated via str.format
|
||||
```
|
||||
|
||||
`t()` reads the active language from `st.session_state["ui_lang"]`. Outside a Streamlit run (tests, scripts) it falls back to English.
|
||||
|
||||
**Add a new language:**
|
||||
1. Copy `src/i18n/packs/en.json` to `src/i18n/packs/<code>.json` and translate values in place. Keep the key tree identical.
|
||||
2. Add a one-line entry to `LANGUAGES` in `src/i18n/__init__.py`: `{"code": "fr", "label": "Français"}`. The sidebar picker auto-renders.
|
||||
3. Run `pytest tests/test_lang_packs.py` — the parity test fails until every key from `en.json` exists in the new pack (and orphan keys not in English are also flagged).
|
||||
|
||||
**Add a new key:**
|
||||
1. Add it to `en.json` first (canonical pack).
|
||||
2. Add it to every other registered pack in the same commit. The parity test enforces this.
|
||||
3. Use the dotted key at the call site: `t("section.subsection.key")` or `t("section.key", name=value)` for placeholder interpolation.
|
||||
|
||||
**Authoring rules:**
|
||||
- Keys live under semantic sections (`home.*`, `upload.*`, `findings.*`, `tools.<id>.name`). Don't nest by language or by tool unless the string is genuinely tool-specific.
|
||||
- Use `{named}` placeholders (not positional `{0}`) so translators see what's being interpolated.
|
||||
- Strings can contain Streamlit markdown (`**bold**`) — pass through `st.markdown` / `st.caption` as usual.
|
||||
- Do **not** put strings inside the farewell-overlay JS payload without going through `_js_html_safe()` in `src/gui/components/_legacy.py`; the helper escapes both the JS string terminator and HTML special chars. The test `TestFarewellEscape` pins that contract.
|
||||
- The sidebar picker is mounted by `hide_streamlit_chrome()`, so every page that calls that helper automatically gets the picker. Pages that don't call it (rare) can call `render_language_selector()` directly.
|
||||
|
||||
### Add a format-standardizer field type
|
||||
|
||||
1. Add value to `FieldType` enum in `core/format_standardize.py`.
|
||||
|
||||
Reference in New Issue
Block a user