T3code/port backend phrases json#920
Conversation
Bundle Stats — Frontend bundle size changeHey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle. As this PR is updated, I'll keep you updated on how the bundle size is impacted. Total
Changeset
View detailed bundle breakdownAdded
Removed No assets were removed Bigger
Smaller
|
135757c to
68f48f1
Compare
Greptile SummaryThis PR moves backend and legacy UI phrases into JSON-backed i18n catalogs. The main changes are:
Confidence Score: 4/5The legacy tools dialog can show a raw message key on its error path. The main backend message-source path has legacy fallback coverage, and decorated JSP pages receive the new legacy JavaScript dictionary. A static TinyMCE tools page can call
What T-Rex did
|
| Filename | Overview |
|---|---|
| src/main/java/com/researchspace/service/JsonMessageSource.java | Adds JSON-backed Spring message resolution with fallback to the legacy message source. |
| src/main/java/com/axiope/webapp/taglib/I18nMessagesTag.java | Adds a JSP tag that serializes prefixed messages for legacy JavaScript pages. |
| src/main/webapp/scripts/global.js | Adds RS.msg, a legacy message dictionary, and client-side ICU-style formatting. |
| src/main/webapp/scripts/externalTinymcePlugins/tools/js/dialog.js | Updates labtools error labels to use RS.msg, with one static-dialog fallback issue. |
| pom.xml | Adds ICU4J and copies server JSON locale catalogs into the application classpath. |
Comments Outside Diff (1)
-
General comment
Server JSON migration drops required spaces at properties continuation boundaries
- Bug
- Several migrated JSON messages concatenate words or sentences where the source
.propertiesvalue uses a line continuation. For example,src/main/resources/bundles/system/system.propertiesdefineserrors.deletesysadminuserasremaining active, enabled..., butsrc/main/webapp/ui/src/modules/common/i18n/locales/en-US/server.system.jsoncontainsremainingactive, enabled.... Similarly,importExport.export.selection.help1inserver.workspace.jsonrendersexported.Theninstead ofexported. Then. These are user-visible phrase regressions in changed server JSON resources.
- Several migrated JSON messages concatenate words or sentences where the source
- Cause
- The migration appears to have removed the separating whitespace that Java
.propertiescontinuation lines contribute when the next physical line begins with spaces, instead concatenating the adjacent text directly in some JSON values.
- The migration appears to have removed the separating whitespace that Java
- Fix
- Regenerate or correct the affected JSON bundle values using Java
.propertiescontinuation semantics, preserving at least one intended separator where continuation lines join text. Add an equivalence check for.propertiesto server JSON migration output so these regressions are caught automatically.
- Regenerate or correct the affected JSON bundle values using Java
- Bug
Reviews (1): Last reviewed commit: "Merge branch 't3code/frontend-i18n-syste..." | Re-trigger Greptile
203527e to
fb8065d
Compare
Design note describing how to localise the remaining hard-coded strings in the legacy vanilla-JS layer (global.js, scripts/tags/*.js) by reusing the existing server MessageSource catalogue: inject an RS.i18n dictionary via the SiteMesh decorator and consume it through an RS.msg helper, rather than adding a second client i18n runtime or coupling to the React i18next instance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds JsonMessageSource, a Spring MessageSource that reads the frontend i18next JSON off the classpath (Maven copies the locales tree), flattening nested JSON to dotted keys and resolving via positional MessageFormat. Wired as the primary messageSource bean with the legacy .properties bundles as a fallback parent. - Ports 1208 .properties keys to flat-key server.*.json (flat storage is required: 34 keys are both leaf and parent, e.g. button.login). - Ports ~50 Velocity email templates + their subjects to $msg lookups; StrictEmailContentGenerator injects $msg centrally. - MessageSourceUtils gains explicit-locale overloads for upcoming per-user locale (emails/PDFs/async run outside a request thread). - Tests: JsonMessageSource, byte-equivalence vs legacy bundle, email render, and locale overloads (all -Dfast=true). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nalization Backend: introduces a single en-US locale seam (UserLocaleService) used by the email pipeline, export layer (PDF/Word/HTML archive/CSV), and Velocity templates; switches JsonMessageSource to ICU MessageFormat so the JSON catalogue supports plural/select; externalizes remaining hard-coded user-facing Java strings and grammar (welded fragments, verb ternaries, "(s)" plurals) into the i18next JSON catalogue; migrates all ~227 <fmt:message> JSP/tag usages to <spring:message> (dropping now-redundant <fmt:bundle> wrappers) so every JSP resolves through JsonMessageSource, and removes the now-dead JSTL localizationContext wiring. Frontend-adjacent: externalizes legacy (non-React) JavaScript strings via RS.msg()/server.legacyJs.json, backed by a new I18nMessagesTag that exposes the flattened JSON catalogue to legacy scripts as RS.i18n. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Executes the low-priority findings from the backend phrases cleanup scan (findings 3 and 8 required no action): - Finding 4: rename the doubled server.<module>.<module>.json files to server.<module>.json (filenames are cosmetic to JsonMessageSource, which flattens every file into one keyspace by dotted key). Regenerate resources.d.ts. Add a guardrail test asserting no dotted key is defined in more than one server.*.json file, since the merge has no collision check otherwise. - Finding 5: rename the 12 snake_case keys to camelCase (errors.*, group.members.*, gallery.api.*, system.property.description.*) in both the JSON catalogue and the .properties fallback, and update the 5 call sites. Left the sibling system-property/HTML-id literals (e.g. self_service_labgroups) untouched, since those aren't i18n keys. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Matches the frontend catalogue convention (nested objects, keySeparator '.') instead of flat dotted keys. JsonMessageSource already flattens nesting into dotted keys, so runtime lookups are unchanged — verified by a scripted round-trip (flatten(nested) byte-equal to the previous flat map for all 21 files) and by JsonMessageSourceEquivalenceTest still checking the same 1214 ported keys against the .properties bundles. Keys that are both a leaf and a prefix of deeper keys (100 cases, e.g. button.login vs button.login.with.google) cannot nest fully; the conflicting remainder stays as a dotted sibling key at that level, which flattens identically. JsonMessageSourceEquivalenceTest now flattens the nested files rather than reading them as flat maps, with a floor assertion so it can never again silently pass by iterating top-level segments only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Some keys were both a leaf value and a dotted prefix of deeper keys
(e.g. button.login vs button.login.with.google), so the previous nesting
pass had to leave a dotted sibling at that level. Renamed each such leaf
to a proper child of its own object:
- .label for category/field/button/menu headings that have their own
value-options or sub-items as children (e.g. button.login.label,
menu.admin.label, the netfilesystem details.* field labels).
- .default for the generic/base variant of a message that has a more
specific sibling (e.g. errors.emptyString.default vs .polite,
errors.invalidusername.default vs .relaxed).
- .text for the one description paragraph (apps.description.msteams
.general.text) whose "child" is an unrelated nested link message.
Updated every call site alongside the catalogue (Java getText/getMessage
calls, spring:message JSPs, Velocity $msg calls, legacy RS.msg() in
global.js, and the parallel .properties fallback keys). icon.email/
icon.information/icon.warning, menu.admin, menu.templates, and
apps.description.msteams.general were confirmed dead (no call site
anywhere in Java/JSP/JS/TS) before renaming, so only the catalogue and
.properties needed touching for those. errors.email was similarly dead
(the "{errors.email.format}" Bean Validation key elsewhere is unrelated).
All 21 server.*.json files are now fully nested with zero dotted-segment
keys. Verified via JsonMessageSourceEquivalenceTest (still 1214 keys,
byte-identical), the collision guardrail test, the full fast suite, and
i18n:check/tsc.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wires up the existing rsResolvedLocale request attribute (set per-request by LocaleFilter) to every reachable <html> tag's lang attribute, using the same fn:replace(locale, '_', '-') pattern already used for RS.i18nLocale, so it renders as a proper BCP-47 tag (e.g. "en-US") rather than Java's underscore form. Covers: the two SiteMesh decorators (default.jsp, externalPages.jsp, which together decorate the vast majority of pages including login/ signup/404/403), the 4 standalone full-page entrypoints excluded from decoration (about, inventory start, apps, gallery) plus identifierPublicPage and apiDocs, the 5 externalTinymcePlugins dialog pages, and both error.jsp fallback pages (which needed the fn taglib added since they don't include the shared taglibs.jsp). Left logout.jsp, createCloudGroupForm/Success.jsp, and oneDriveRedirect.jsp untouched: their own <html> tags are dead markup (confirmed via their controller mappings and decorators.xml) since SiteMesh's default decorator actually renders their output, which this change already covers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Design notes belong in .claude/ (gitignored), not committed to DevDocs.
The key-compression pass text-replaced old catalogue keys inside code
that merely looked like those keys, corrupting live references:
- userform.jsp: EL data bindings ${user.username/firstName/lastName/
enabled/usernameAlias} were rewritten to message-key EL that renders
nothing
- batchUserRegistration.js / global.js: JS identifiers (user.username,
user.password, button.addClass) clobbered into invalid syntax
- UserDaoHibernate: HQL property user.accountLocked renamed to a
nonexistent user.locked, breaking the query
- DeveloperGroupSetup + defaultDeployment.properties: the
default.user.password property was renamed differently on each side,
which would fail bean initialisation at boot
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase onto origin/main linearized away the earlier merge commit, losing its conflict-resolution content: the three identifier error messages upstream added to the deleted .properties bundle (ownership check on identifier delete, bulk IGSN allocation limits) need their keys renamed to this branch's camelCase convention and added to server.inventory.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2288d27 to
4c2274d
Compare
…hrases-json # Conflicts: # src/main/resources/bundles/ApplicationResources.properties
…hrases-json # Conflicts: # src/main/java/com/axiope/userimport/NotifyUserPostUserCreate.java # src/main/java/com/researchspace/service/impl/EmailBroadcastImpl.java # src/main/java/com/researchspace/service/impl/StrictEmailContentGenerator.java # src/main/java/com/researchspace/webapp/controller/SysAdminSupportController.java # src/main/java/com/researchspace/webapp/filter/LocaleFilter.java # src/test/java/com/axiope/service/cfg/EmailBroadcasterConfigTest.java # src/test/java/com/researchspace/service/impl/EmailBroadcastTest.java # src/test/java/com/researchspace/service/impl/EmailBroadcastVanillaJunit.java
Description
Migrates all backend phrases to the new JSON i18n format, and wire locale-aware handling to all parts of the backend:
Accept-Languageheader for clients to hint to the server which locale it accepts, which is then used by the server to generate localised error messages, although the recommendation is for clients to consume error codes where available. In a future PR, servers will also be able to broadcast, in API responses, which locales it can emit phrases in.The locale is currently fixed as
en-US. Due to there not being a locale switcher in the UI at the moment, a new deployment.properties property is added to allow translators to temporarily switch to another locale.Review Guide (via AI)
Most of this diff is mechanical (key replacements in JSPs/JS, catalogue JSON, test updates). The files below contain real design decisions or behavior changes.
New i18n infrastructure (read these first)
service/JsonMessageSource.javaMessageSourcethat reads the frontend's i18next JSON files. It overridesgetMessageInternalinstead of the usualresolveCodeso it can use ICU4J formatting — the Javadoc explains why. Frontend namespaces are exposed ascommon:/inventory:keys. Check the locale fallback order and how single quotes are handled when there are no arguments.service/UserLocaleService.javadeployment.experimental.localeproperty; falls back to en-US if blank or invalid.webapp/filter/LocaleFilter.javarequest.getLocale()for every request. The dead?locale=parameter handling is gone.service/LocaleBoundMessages.java,impl/LocaleAwareDateTool.java,service/ListFormatUtils.java$msgtied to the recipient's locale, locale-correct dates, and list joining. Small files, but every email and export goes through them.service/PrefixedMessageProvider.java+taglib/I18nMessagesTag.javalegacyjs.*messages into pages as a JSON object (RS.i18n). The interface exists becauseServiceLoggerAspctwraps every service bean in a proxy, and a proxy failsinstanceofchecks against the concrete class — see the comment. Also check the</script>escaping.taglib/NotificationTypeLabelTag.java+service/NotificationTypeMessages.javaNotificationTypeenum. The enum itself is untouched (it's persisted and lives in rspace-core-model).keyForthrows if a new enum constant has no mapping; a test checks every constant.model/record/init/BuiltinContentMessages.javaResourceBundlewrapper aroundJsonMessageSource, used when creating the built-in forms and templates.Behavior changes to watch for
webapp/WEB-INF/web.xml+applicationContext-resources.xml.propertiesbundles and their wiring are deleted. Everything resolves throughJsonMessageSourcenow — there is no fallback.service/impl/EmailBroadcastImp.java,StrictEmailContentGenerator.java,RequestNotificationMessageGenerator.javaservice/impl/AutoshareManagerImpl.javaapi/v1/controller/InventoryRecordValidator.java,SampleApiPostFullValidator.javanull, so a missing key shows up as the raw key (on purpose — it makes the bug visible). API note: some inventory error codes are sent raw in bulk-operation responses, so renamingerrors.inventory.move.*/container.*changed those strings —ui/src/util/alerts.tsxwas updated to match.webapp/controller/GroupController.java,StructuredDocumentController.java,SysAdminUserRegistrationController.java,userimport/UserImporterFromCSV.java+UserLineParserFromCSV.javaexport/pdf/PdfHtmlGenerator.java,csvexport/InventoryItemCsvExporter.java,AuditTrailSearchResultCsvGenerator.javaCSV_COMMENT_HEADERtogetCsvCommentHeader()). PDF comment lines use theexport.pdf.comments.linekey instead of string concatenation.model/record/init/{RtPCR,Elisa,Experiment,LabProtocol}.java"form.rtpcr." + fieldKey); now they're full literal strings, so you can grep for them. Elisa'schoice1..choice14loop is now a plain array. Field order and content should be identical —BuiltInsCreatedProperlyTestchecks creation but not order.velocityTemplates/slack/slackNotification.vm(deleted)properties/PropertyHolder.javagetWebappVersion()/getCopyrightYear(). The copyright year is now computed at runtime instead of stored in the catalogue; footer JSPs read both fromRS_DEPLOY_PROPS.deployments/defaultDeployment.propertiesdeployment.experimental.localeproperty (temporary, until the language switcher exists).Regression fixes worth double-checking (commit 8ecb660)
The key-compression pass accidentally rewrote code that only looked like catalogue keys. These are the repairs:
WEB-INF/pages/userform.jsp— seven data bindings restored (${user.username}etc.)scripts/pages/system/batchUserRegistration.js,scripts/global.js— broken JavaScript identifiers fixeddao/hibernate/UserDaoHibernate.java— HQL propertyuser.accountLockedrestoredservice/impl/DeveloperGroupSetup.java+defaultDeployment.properties— thedefault.user.passwordproperty matched up again on both sidesThe fast test suite doesn't run JSPs or JavaScript, so the best manual check is a dev-stack look at the profile page, batch user registration, and any Apprise dialog (
global.js).New tests
JsonMessageSourceTest(includes a guard against the same key appearing in two files), fourEmail*TemplateI18nTestclasses (render real templates and check no keys leak through),NotificationTypeMessagesTest(covers every enum constant), plus unit tests for each new class above.