feat(i18n): wrap user-facing strings across the editor - #721
Conversation
Translation support landed with only four demonstration strings, which left visible inconsistencies: the sidebar rendered a translated "All Pages" directly above an untranslated "Settings". Wraps label, tooltip, placeholder, title and Tooltip text attributes along with toast, confirm and alert messages, taking the catalog from 163 to 337 entries. The extractor reads `__()` from both the template and the script block of a .vue file, so both forms reach the POT. Only prose is wrapped. Bindings, interpolated values and the "Builder" product name are left alone, and nothing compares a label against a literal anywhere in the frontend, so no behaviour depends on these strings. Also guards against Crowdin's in-context pseudo language, which exports `crwdns...` ids that score as fully translated and would render as those ids across the editor. Esperanto cannot be disabled in the project, so the test fails the build instead if any catalog carries them. Co-Authored-By: Claude <noreply@anthropic.com>
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; the previously reported commented-out strings are no longer extracted. Reviews (4): Last reviewed commit: "feat(i18n): translate template text node..." | Re-trigger Greptile |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #721 +/- ##
===========================================
+ Coverage 66.01% 66.05% +0.04%
===========================================
Files 37 37
Lines 4976 4982 +6
===========================================
+ Hits 3285 3291 +6
Misses 1691 1691 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Wrapping labels inside commented-out markup put Align, Flow, Place Items and Place Self into the catalog, giving translators work for controls that do not render. Reverted those five, per review on frappe#721. Also wraps the six frappe.throw messages that were still untranslated. The two f-strings become _("...{0}").format(...), since the extractor cannot read an interpolated literal. Co-Authored-By: Claude <noreply@anthropic.com>
emit() sends these straight to progressMessage and errorMessage in the
generator modal, so they render as-is. The dynamic stage becomes
_("Building {0}").format(name).
last_stage only dedupes repeated emits and nothing on either side compares
against these values, so translating them changes no behaviour.
Co-Authored-By: Claude <noreply@anthropic.com>
The earlier sweep only rewrote attributes, so prose sitting between tags was skipped. That left empty and loading states untranslated, which is the copy a user reads most closely: "No matching pages found.", "No components saved", "Select a block to edit properties", "Loading...". Wraps 87 text nodes across 46 files, taking the catalog to 411 entries. Comments and pre/code/script content are left alone, as is anything containing markup, interpolation or code punctuation. Co-Authored-By: Claude <noreply@anthropic.com>
Wrap the display fields the earlier sweep missed: object literal labels and titles, static description attributes, property section names, keyboard shortcut groups, ternary branches, plain text nodes and toast messages. Lookup keys stay untranslated throughout. Settings tabs keep their value, which is persisted to localStorage. Block template categories keep their stored values and translate at render. The command palette keeps group, which it matches against a key. Shortcut groups are wrapped in every file that registers a shortcut. Translating only one of them makes the shortcuts dialog render a translated and an untranslated heading for the same group, since frappe-ui buckets by the string itself. The extraction test pinned "Select (v)", which no longer exists: the shortcut key now sits outside the translated string so a translator cannot alter it. Retarget the fixture at a string unique to that file. Co-Authored-By: Claude <noreply@anthropic.com>
Translate all 841 messages. Only 34 were filled in before, and they came from frappe's own catalog, where several are machine translated wrong: Value read as "price", Script as "a written story", Back to Home as "I am going home", Save as "rescue". Those are rewritten rather than carried over. Unify terminology while filling the rest, so one English term does not appear under two Hindi words. Insert now uses the standard Insert menu term instead of the same word used for Add. Database records stay untranslated: Builder Token names, block template fixture names, doctype names, keyboard key codes and the CSS keyword unset, which is compared as a sentinel in cssUtils. Co-Authored-By: Claude <noreply@anthropic.com>
|
I checked the normal dashboard/editor journey at the current head (
The existing unpublish source correctly preserves its quotes and |
|
This will collide with #715 . We need to decide which one makes more sense to merge first. |
# Conflicts: # builder/builder/tests/test_i18n.py # builder/locale/ar.po # builder/locale/bs.po # builder/locale/cs.po # builder/locale/da.po # builder/locale/de.po # builder/locale/es.po # builder/locale/fa.po # builder/locale/fr.po # builder/locale/hr.po # builder/locale/it.po # builder/locale/nl.po # builder/locale/pt.po # builder/locale/ru.po # builder/locale/sr.po # builder/locale/sv.po # builder/locale/tr.po # frontend/src/components/BlockContextMenu.vue # frontend/src/components/BuilderCommandPalette.vue # frontend/src/components/BuilderLeftPanel.vue # frontend/src/components/BuilderSettings.vue # frontend/src/components/BuilderToolbar.vue # frontend/src/components/MainMenu.vue # frontend/src/pages/PageBuilder.vue # frontend/src/utils/builderBlockCopyPaste.ts # frontend/src/utils/useBuilderEvents.ts
|
Too many files changed for review (177 files, 100 file limit). Bypass the limit by tagging |
|
Thanks for rebasing this onto the registry refactor. I rechecked the normal customer path at
Dynamic prose such as |
# Conflicts: # builder/export_import_standard_page.py
The develop merge adopted the extendible-UI refactor and dropped a cluster of wraps in the process: both context-menu confirmations, the Template / Read Only badge, the Generate with AI and homepage tooltips, the Get Started dialog and every plain shortcut group. commandShortcuts() kept translating its groups, so the shortcuts modal bucketed one General under two headings, one translated and one not. Covers the rest of the review on frappe#721: dashboard empty states, the New button, folder deletion, page duplicate/delete/revert messages, upload progress and results, the version restore confirmation and the default Confirm and Alert dialog titles. Dynamic prose formats through stable placeholders: Edited {0}, Last updated {0} by {1}, Uploading {0}%. Group headings translate at render from raw keys, so the literals now live in commandGroupLabels and settingsGroupLabels where the extractor can see them; Edit, Canvas, Tools and Current Page were missing from the catalog entirely. The dashboard empty state loses the quotes around + New: the .vue extractor is regex-based and cannot read a string that escapes both quote characters. Hindi stays complete at 908 entries. Co-Authored-By: Claude <noreply@anthropic.com>
The dev server serves the static index.html, which carries no boot payload, so window.translated_messages stayed undefined and every __() fell back to English regardless of the user's language. Fetch the catalog from frappe.translate.get_boot_translations before installing the router, so module-scope __() calls in route chunks see it. Production keeps the synchronous boot injection and skips the fetch. Co-Authored-By: Claude <noreply@anthropic.com>
The robots.txt description carried a literal {{ __("...") }} inside a
plain attribute string, which vue never evaluates, so the editor
rendered the raw expression. The description now composes in script
with the guide link as a {0} placeholder.
The extraction regex cannot cross a newline inside a call's argument
list, so the description sits in script where prettier keeps each
argument on one line. Also wraps statics the sweep missed: the
analytics empty-state hint, the persona survey subtitle and greeting,
the More Styles empty state and the settings dialog heading.
Co-Authored-By: Claude <noreply@anthropic.com>
The script dialog composed its title from capitalize(mode), which no
catalog can carry; each mode and script type now maps to a full msgid.
The note beside it translated only the Note: prefix, leaving the
sentence in English.
Also wraps text nodes that earlier sweeps missed because they sliced
the template at the first closing template tag, which in files with
nested slot templates cut most of the file: palette footer hints,
Add Script, Add Domain, Clear Image and Gradient, component update
notice, Upload CSV, Download sample, Replace and the page usage
counts, which now format through {0}.
Co-Authored-By: Claude <noreply@anthropic.com>
|
@mardausdennis give it a try again? |
|
Rechecked at A few rendered strings are still unwrapped:
The count-bearing ones would need stable sources like the ones you already introduced elsewhere: One thing worth keeping in mind for future callsites: |
|
Opened #746 with the rest of them, including the settings surfaces that earlier sweeps hadn't reached — domains, redirects and users were still almost entirely English. Two things I left out and explained in the description rather than guessing: the permission messages passed to |
|
🎉 This PR is included in version 1.33.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Translation support landed with only four demonstration strings, which left
visible inconsistencies: the sidebar rendered a translated "All Pages"
directly above an untranslated "Settings".
Wraps label, tooltip, placeholder, title and Tooltip text attributes along
with toast, confirm and alert messages, taking the catalog from 163 to 337
entries. The extractor reads
__()from both the template and the scriptblock of a .vue file, so both forms reach the POT.
Only prose is wrapped. Bindings, interpolated values and the "Builder"
product name are left alone, and nothing compares a label against a literal
anywhere in the frontend, so no behaviour depends on these strings.
Continuation of #706