|
| 1 | +# PD-5321: Angular i18n Guidelines (Paragraph-First) |
| 2 | + |
| 3 | +## Why this guideline exists |
| 4 | + |
| 5 | +Translations in this codebase currently mix two patterns: |
| 6 | + |
| 7 | +- Legacy segmented strings (many small translation units for one sentence/paragraph) |
| 8 | +- Paragraph-level translation units (single cohesive message) |
| 9 | + |
| 10 | +This inconsistency creates fragmented translation memory and extra translator effort. |
| 11 | +The standard moving forward is **paragraph-first translation**: translate complete user-facing thoughts as one unit whenever possible. |
| 12 | + |
| 13 | +## Core principle |
| 14 | + |
| 15 | +Use Angular i18n to mark **complete semantic messages** (typically full paragraphs, headings, labels, or complete button text), not fragmented pieces. |
| 16 | + |
| 17 | +## Standard rules |
| 18 | + |
| 19 | +1. **Translate full paragraphs as one unit.** |
| 20 | + Avoid splitting one sentence/paragraph across multiple elements solely for styling. |
| 21 | +2. **Keep meaning and grammar together.** |
| 22 | + Articles, nouns, verbs, punctuation, and dynamic placeholders that form a single thought should stay in one translation unit. |
| 23 | +3. **Use placeholders for dynamic values.** |
| 24 | + Interpolate variables in a single i18n-marked sentence. |
| 25 | +4. **Use ICU for plural/select logic.** |
| 26 | + Keep pluralization and gender/select logic inside one translatable unit. |
| 27 | +5. **Use `i18n` metadata (`meaning|description@@id`) for clarity and stability.** |
| 28 | + Add translator context where ambiguity exists. |
| 29 | +6. **Do not use HTML structure to force translation segmentation.** |
| 30 | + If visual emphasis is needed, prefer styling that does not break translation context. |
| 31 | + |
| 32 | +## Anti-pattern (do not use) |
| 33 | + |
| 34 | +Fragmenting one message into multiple translatable chunks: |
| 35 | + |
| 36 | +```html |
| 37 | +<p> |
| 38 | + <span i18n>You have</span> |
| 39 | + <strong>{{ workCount }}</strong> |
| 40 | + <span i18n>works pending review.</span> |
| 41 | +</p> |
| 42 | +``` |
| 43 | + |
| 44 | +Problems: |
| 45 | + |
| 46 | +- Translators cannot reorder words naturally for other languages. |
| 47 | +- Grammar agreement can break. |
| 48 | +- Translation memory is fragmented. |
| 49 | + |
| 50 | +## Preferred pattern |
| 51 | + |
| 52 | +Single message with placeholders: |
| 53 | + |
| 54 | +```html |
| 55 | +<p i18n="dashboard pending work message|Shown in works summary card@@pending_work_summary"> |
| 56 | + You have {{ workCount }} works pending review. |
| 57 | +</p> |
| 58 | +``` |
| 59 | + |
| 60 | +## Additional examples |
| 61 | + |
| 62 | +### 1) Paragraph-level content |
| 63 | + |
| 64 | +**Bad (segmented):** |
| 65 | + |
| 66 | +```html |
| 67 | +<p> |
| 68 | + <span i18n>Connect your account to import</span> |
| 69 | + <span i18n>works and affiliations</span> |
| 70 | + <span i18n>automatically.</span> |
| 71 | +</p> |
| 72 | +``` |
| 73 | + |
| 74 | +**Good (single paragraph unit):** |
| 75 | + |
| 76 | +```html |
| 77 | +<p i18n="connection help text|Displayed on account setup screen@@connect_import_help"> |
| 78 | + Connect your account to import works and affiliations automatically. |
| 79 | +</p> |
| 80 | +``` |
| 81 | + |
| 82 | +### 2) Dynamic values |
| 83 | + |
| 84 | +**Good:** |
| 85 | + |
| 86 | +```html |
| 87 | +<p i18n="search results count|Results summary above table@@search_results_summary"> |
| 88 | + Showing {{ shown }} of {{ total }} results. |
| 89 | +</p> |
| 90 | +``` |
| 91 | + |
| 92 | +### 3) Pluralization (ICU) |
| 93 | + |
| 94 | +**Good:** |
| 95 | + |
| 96 | +```html |
| 97 | +<p i18n="notifications count|User inbox summary@@notifications_count"> |
| 98 | + {count, plural, =0 {You have no notifications.} one {You have one notification.} other {You have # notifications.}} |
| 99 | +</p> |
| 100 | +``` |
| 101 | + |
| 102 | +### 4) Attributes and controls |
| 103 | + |
| 104 | +```html |
| 105 | +<input |
| 106 | + i18n-placeholder="search field placeholder|Global search input@@global_search_placeholder" |
| 107 | + placeholder="Search by keyword" |
| 108 | +/> |
| 109 | + |
| 110 | +<button i18n="save button label|Primary save action@@save_button"> |
| 111 | + Save changes |
| 112 | +</button> |
| 113 | +``` |
| 114 | + |
| 115 | +## Implementation checklist (PR-level) |
| 116 | + |
| 117 | +- [ ] Full paragraph or complete message is marked as one i18n unit. |
| 118 | +- [ ] No unnecessary message splitting for style-only reasons. |
| 119 | +- [ ] Dynamic values are placeholders within the same message. |
| 120 | +- [ ] Plural/select grammar uses ICU when needed. |
| 121 | +- [ ] `meaning|description@@id` metadata is present for non-obvious strings. |
| 122 | +- [ ] Content reads naturally if translated with different word order. |
| 123 | + |
| 124 | +## Migration guidance |
| 125 | + |
| 126 | +When touching existing templates: |
| 127 | + |
| 128 | +1. Identify adjacent i18n strings that form one user-facing message. |
| 129 | +2. Merge them into a single i18n-marked element. |
| 130 | +3. Preserve styling without splitting translatable content (move styling to CSS classes where possible). |
| 131 | +4. Add metadata and stable custom IDs for translator context. |
| 132 | +5. Validate extraction output and review translation units for readability. |
| 133 | + |
| 134 | +## Scope and exceptions |
| 135 | + |
| 136 | +Valid exceptions where splitting is acceptable: |
| 137 | + |
| 138 | +- Truly independent UI strings (e.g., separate labels, independent actions) |
| 139 | +- Reusable standalone components with isolated semantics |
| 140 | +- Accessibility-only text that serves a different purpose from visual copy |
| 141 | + |
| 142 | +If uncertain, prefer a single message unit and ask in code review. |
| 143 | + |
0 commit comments