Draft
Conversation
4f09c13 to
d1f5338
Compare
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
d1f5338 to
325ce9c
Compare
5b7d3b8 to
ed0dcd6
Compare
Introduces dedicated Translation Memory entities for the upcoming shared TM feature, without affecting the existing "similar translations" suggestion feature which continues querying the translation table directly. - New entities: TranslationMemory, TranslationMemoryEntry, TranslationMemoryProject - Liquibase schema + GiST trigram index for similarity search - Auto-create project TM on project creation via ProjectCreationService - Write TM entries on translation save (only for projects with TM assignments) - Clean up TM data on project hard-deletion - TRANSLATION_MEMORY feature flag for future gating - Integration tests covering entity lifecycle, write pipeline, translation deletion cascade, and project deletion cleanup Existing "similar translations" feature is unchanged — it will be renamed on the frontend as part of a later phase.
Introduces the managed Translation Memory feature (Business plan).
All new code lives in the EE module and is gated by
@RequiresFeatures(Feature.TRANSLATION_MEMORY).
Organization-level shared TM CRUD:
- SharedTranslationMemoryService (EE): create/update/delete/find/list
- SharedTranslationMemoryController at /v2/organizations/{orgId}/translation-memories
- Rejects mutations of PROJECT-type TMs via the shared endpoint
- Requires MAINTAINER org role for mutations
Project-level TM configuration:
- ProjectTranslationMemoryConfigService (EE) with assign/unassign/update
- ProjectTranslationMemoryController at /v2/projects/{projectId}/translation-memories
- Individual CRUD endpoints (GET list, POST assign, DELETE unassign, PUT update)
- Cross-organization assignment is rejected
- Cannot unassign the project's own PROJECT-type TM
- Re-assigning an already-assigned TM returns 400
Managed TM suggestions (separate from the original "similar translations"):
- New ManagedTranslationMemorySuggestionService (EE) queries only the
translation_memory_entry table — it does not touch the translation table.
- New endpoint POST /v2/projects/{projectId}/suggest/managed-translation-memory
- Results come from all assigned TMs (project's own + any readable shared TMs)
- Original /suggest/translation-memory endpoint is completely untouched
and still queries the translation table for free plan users.
Supporting additions (CE):
- TranslationMemoryRepository: find / findByOrganizationId /
findByOrganizationIdAndTypePaged / findAssignedToProject
- Message enum: TRANSLATION_MEMORY_NOT_FOUND,
TRANSLATION_MEMORY_PROJECT_ASSIGNMENT_NOT_FOUND,
CANNOT_UNASSIGN_PROJECT_FROM_OWN_TRANSLATION_MEMORY,
CANNOT_MODIFY_PROJECT_TRANSLATION_MEMORY,
TRANSLATION_MEMORY_ALREADY_ASSIGNED_TO_PROJECT
- ActivityType enum: TRANSLATION_MEMORY_CREATE/UPDATE/DELETE,
ASSIGN_PROJECT / UNASSIGN_PROJECT / UPDATE_PROJECT_CONFIG
Test data:
- TranslationMemoryTestData gained sharedTm (pre-assigned) and
unassignedSharedTm (for assign tests); all tests use pre-built data
instead of inline setup.
Tests (17 new EE tests, all passing):
- SharedTranslationMemoryControllerTest (6 tests)
- ProjectTranslationMemoryControllerTest (7 tests)
- ManagedTranslationMemorySuggestionControllerTest (4 tests)
Existing CE tests for the original "similar translations" feature all
still pass; TranslationMemoryService.kt in the CE module is identical
to origin/main.
Not in this commit (follow-ups):
- Auto-translate / pre-translate integration with shared TMs
- "Keep or delete" choice on shared TM disconnect
- Lazy migration for existing Business plan projects (per pitch)
- TMX import/export
- Frontend UI
ed0dcd6 to
847a5bf
Compare
Adds keepData query param to DELETE /v2/projects/{id}/translation-memories/{tmId}.
When true, entries from the shared TM are snapshotted into the project's own
PROJECT-type TM before the assignment is removed — the project retains the data
after disconnect. Default (false) preserves previous behavior: assignment is
removed, shared TM and its entries remain intact for other projects.
Snapshot drops translation/key back-references since they belong to the original
source project and have no meaning in the destination.
…Memory Introduces TmAutoTranslateProvider — a feature-aware dispatcher for TM lookups used by auto-translate and batch pre-translate. - OSS impl: delegates to the classic TranslationMemoryService (translation table). - EE impl (@primary): for projects whose organization has Feature.TRANSLATION_MEMORY enabled, routes through ManagedTranslationMemorySuggestionService which queries translation_memory_entry. This exposes the project's own TM (including snapshots from keepData=true disconnect, TMX imports, and future manual edits) plus all shared TMs assigned to the project with read access. Projects without the feature fall through to the OSS path. Both softAutoTranslate (single-key) and autoTranslateUsingTm (batch) now go through the dispatcher.
New endpoints under /v2/organizations/{orgId}/translation-memories/{tmId}/entries
for listing, creating, updating, and deleting individual TM entries. Used for
direct editing of TM content — complements the write-on-save pipeline, shared-TM
snapshots, and future TMX import.
- Paginated list with optional free-text search (source or target) and
targetLanguageTag filter
- Organization MAINTAINER role required for create/update/delete; list uses
@UseDefaultPermissions
- Feature-gated via @RequiresFeatures(Feature.TRANSLATION_MEMORY)
- Entries created this way have no translation/key back-references, matching
the semantics of TMX imports and keepData snapshots
…O bounds Must-fix items: - snapshotEntriesIntoProjectTm: deduplicates against existing project-TM triples before saveAll. Hibernate's configured jdbc.batch_size=1000 groups the INSERTs automatically, so repeat disconnect-with-keepData is now idempotent and large snapshots batch correctly. - Partial unique index on translation_memory_entry(translation_memory_id, translation_id) WHERE translation_id IS NOT NULL: prevents concurrent write-on-save from producing duplicate rows. upsertEntry falls back to re-fetch-and-update on DataIntegrityViolationException so the race is handled silently. - Create/UpdateTranslationMemoryEntryRequest.sourceText/targetText: @SiZe(max = 10000) aligned with TolgeeProperties.maxTranslationTextLength default, preventing unbounded bodies. - findByProjectIdAndReadAccessTrue: add FLUSH_MODE=COMMIT query hint for consistency with the write variant — same hot-path flush risk applies to the auto-translate/suggestion read path. Should-fix follow-ups: - Drop redundant CascadeType.REMOVE on TranslationMemory.entries / projectAssignments. DB-level ON DELETE CASCADE already handles cleanup; the JPA cascade would force an N+1 load before delete. - Delete unused hasTmAssignments / existsByProjectId (dead code). - @min(0) on AssignSharedTranslationMemoryRequest.priority and UpdateProjectTranslationMemoryAssignmentRequest.priority — reject negative priority at the DTO boundary. - Clarify empty-tmIds guard and transactional contract with code comments. Tests: idempotent keepData re-snapshot; oversized entry payload → 400.
…ist, E2E tests - Editor TM panel: conditionally call managed-translation-memory endpoint when TRANSLATION_MEMORY feature is enabled (Business plan). - Org settings: new Translation memories page with list (shared + project TMs), create/edit/delete dialogs, empty state, feature-gated banner. - With-stats endpoint: entry count, assigned projects (first 3 names), project count — displayed in the list item. - Create dialog: supports assigning projects on creation (read+write). - Refactor: extract BaseLanguageSelect from GlossaryBaseLanguageSelect into shared component/languages/. - Fix: ON DELETE CASCADE on translation_memory_project FKs. - E2E: orgSettings.cy.ts with navigation, CRUD, permission tests. Compounds, test data controller, TRANSLATION_MEMORY feature flag.
- New ProjectSettingsTranslationMemory component: project memory row (always-on toggle) + shared TMs as priority-ordered chips + Configure button linking to org TM settings. - Fix auto-incrementing priority when assigning TMs from org-level create dialog (was hardcoded to 0). - E2E: projectSettings.cy.ts with section visibility, always-on state, chip display, configure navigation tests.
- New TranslationMemoryView: detail page at /translation-memories/:id with breadcrumb, search bar, multi-select language filter. - Entries grouped by sourceText+keyName, rendered in Translations-view style: source text row with flag, target language rows below. - Multi-select language filter with InfiniteMultiSearchSelect, base language shown as checked+disabled, persisted to localStorage. - Backend: entry query switched to native SQL with bytea::text casts, supports comma-separated language tags filter, ordered by source_text. - TM list items are now clickable — navigate to the detail page.
When no saved preference exists, all languages appear checked in the selector and no filter is sent to the API. Deselecting a language switches to explicit mode with all-minus-one.
- Org TM list: split into "Shared" and "Project" sections with compact grid for project TMs (no empty projects column/kebab). - TM detail: "Projects" tab with read/write checkboxes, remove button. Backend: GET .../assigned-projects endpoint + HATEOAS model. - Project settings: replaced chips with priority table, up/down arrows for reordering (sequential mutateAsync to avoid race conditions). - EE/OSS pattern: ProjectSettingsTranslationMemory exported via tg.ee module with Empty stub in OSS. - Entry browser: display all selected languages per group (empty rows for missing translations), search moved to inline toolbar. - Various fixes: checkbox toggle refetch, Manage button layout.
- Inline click-to-edit on target language cells (PUT existing, POST new entries for empty cells, DELETE with confirmation). Follows GlossaryListTranslationCell pattern: Enter saves, Escape cancels. - Create entry dialog: source text + multiple target languages at once, MUI Select for language picker. - Backend tests (9 new, 57 total passing): with-stats endpoint (entry count, project names, search), assigned-projects endpoint, create with assignedProjectIds + auto-incrementing priority, comma-separated language filter, ON DELETE CASCADE on TM with assignments. - TM list alignment: unified 4-column grid for both shared and project sections so language flags align vertically. - Fixed listWrapper border selector for split sections.
- tmContentBrowser.cy.ts (9 tests): navigation, entries display, search, inline edit/cancel/delete, create entry dialog, empty state. - tmProjectsTab.cy.ts (6 tests): tab switch, checkbox states, toggle read/write, remove project, empty state. - projectSettings.cy.ts (2 new): priority table display, arrow states. - orgSettings.cy.ts: lowercase test titles. - New compounds: E2TranslationMemoryView (entries + projects tab), E2ProjectTmSettings (project settings TM section). - Updated E2TranslationMemoriesView with findAndVisitTm method. - Fix: wrap language filter in Box with data-cy for testability.
…TM lists - Backend: added `type` query param to GET /translation-memories-with-stats endpoint. Filters by SHARED or PROJECT when provided, returns all when omitted. - Frontend: two separate API calls (type=SHARED, type=PROJECT) each with own PaginatedHateoasList and independent pagination. Replaces the client-side split with size=100. - Backend tests: 3 new tests for type filter (SHARED returns 2, PROJECT returns 1, no filter returns all). 21 total SharedTM controller tests passing.
- Import TMX 1.4b files into any TM with keep/override conflict resolution - Export TM entries as TMX file download - DOM parser with DTD loading disabled for robust XML handling - StAX writer for valid TMX output with xml:lang attributes - Entry count shows translation units (distinct source texts) not individual entries - Import result counts by translation unit for consistency with UI grouping - Frontend: import dialog with file dropzone + mode selector, export button - Backend tests (9) and E2E tests (3 new, 12 total in content browser)
627c037 to
b16f9dc
Compare
Store tuid from TMX <tu> elements in translation_memory_entry. Conflict resolution (keep/override) now matches by tuid + target language. Entries with different or null tuid are always created regardless of source text match.
Org settings: - Single flat list with All/Shared/Project only filter tabs - Inline search + filter in one row - Type badges (Shared/Project only) on every row - Kebab menu: Settings + Delete (red) Project settings: - Drag-and-drop priority reorder via @dnd-kit - Read/Write chips, settings gear icon per row - "Manage all TMs" button Unified TM settings dialog (replaces edit dialog + Projects tab): - Name, base language, project assignments with read/write toggles - Add project via single-select autocomplete (unassigned only) - Soft-delete with keep/discard confirmation and undo - Backend: assignedProjects with per-project access in update endpoint Remove Projects tab from TM detail view. Update E2E tests and compounds to match new UI.
Paginate by distinct source text instead of flat entries so the language filter only controls which translations are included per source text, not which source texts appear.
Free users now get suggestions from their project's own TM (entries from translation_memory_entry, written on every save). Shared TMs, management UI, and batch pre-translate by TM remain Business-only. Backend - Unify the suggestion endpoint: /suggest/translation-memory dispatches to the managed service when the project has any readable TM assignments, falls back to the legacy TranslationMemoryService for projects that pre-date the feature. - TranslationMemoryManagementService.getReadableTmIdsForSuggestions filters out shared TMs on free plan via EnabledFeaturesProvider. - Move ManagedTranslationMemorySuggestionService from EE to CE; pass organizationId through so the plan check works inside the service. - Replace TmAutoTranslateProviderEeImpl with the same dispatch in CE. - Hard-gate StartBatchJobController.translate (pre-translate by TM) with @RequiresFeatures(Feature.TRANSLATION_MEMORY) — free plan returns 400. - Remove the redundant /suggest/managed-translation-memory endpoint. Frontend - Tools panel always calls /suggest/translation-memory, no URL swap. - Pre-translate by TM batch operation is hidden for free users. - Regenerate API schema (drops the removed endpoint). Tests - New TranslationSuggestionControllerManagedTmTest covers paid + free dispatch. - Update BatchPreTranslateByTmTest: stub EnabledFeaturesProvider.isFeatureEnabled directly (Mockito mocks default interface methods, so stubbing get() is not enough); add 400 feature_not_enabled assertion for free plan. - Update TranslationMemoryEntryControllerTest language-filter assertion to match source-text pagination semantics (totalElements counts source texts, not language-filtered entries).
Backend - Add nullable translationMemoryName to TranslationMemoryItemView (with @jvmoverloads so the legacy 6-arg JPQL constructor expression in TranslationRepository still resolves). - Add translationMemoryName to the HATEOAS TranslationMemoryItemModel + assembler. - ManagedTranslationMemorySuggestionService SQL joins translation_memory and returns the TM name per row. Legacy TranslationMemoryService leaves the field null (no TM concept there). Frontend - TranslationMemoryItem renders a small muted chip next to the key name when translationMemoryName is present. Hidden for legacy-path results and for single-TM projects where the source is obvious.
The `/entries` list endpoint now paginates by distinct source text and returns a page of groups. Each group contains only the entries matching the `targetLanguageTag` filter, but the source row itself always appears on the page regardless of whether any translation matches. This makes the TM content browser show every source text as a row even when the user filters to a language that has no translations — with blank cells the user can click to add. Backend - New TranslationMemoryEntryGroupModel + assembler (source text, key name, entries filtered by language). - listEntryGroups() on the management service; groups include empty entries lists when no translation matches. - Controller list endpoint returns PagedModel<…EntryGroupModel>. Frontend - TranslationMemoryEntriesList reads translationMemoryEntryGroups and maps to EntryGroup directly. Language filter keeps being sent so the backend narrows the entries payload, not the rows. Tests - TranslationMemoryEntryControllerTest list cases rewritten to assert on the new translationMemoryEntryGroups shape and empty-entries behavior for unmatched language filters.
- Create entry dialog seeds the first target row via useEffect when org languages load after the dialog mounted. Previously the initial useState ran with availableLanguages=[] and targets stayed empty, failing the test in CI where languages took longer to load. - E2TranslationMemoryView import compound now writes and reads the generated fixture from the same path (cypress/fixtures/generated/), fixing the file-not-found error in CI.
Pre-translate uses the same plan-aware dispatch as the suggestion endpoint — free users pre-translate from their project TM only, paid users get shared TMs too. Remove the @RequiresFeatures hard gate and the frontend hidden flag so the batch option is available to everyone.
Entries with the same source text but different keys (or null key) now appear as separate rows in the TM content browser. Previously they were merged into one group and only one translation per language was visible.
Previously onTranslationSaved returned early on empty text, leaving the stale TM entry in place. Now it deletes the entry from all writable TMs when the translation text is cleared.
- Rename test methods to clearly describe scenario and expected outcome - Split the language-filter list test into two focused tests - Fix misleading comments (search test, tuid helpers) - Add documentation comments to TMX builder helpers and multi-step tests - Revert broken ObjectMapper-to-DSL conversion in SharedTranslationMemoryControllerTest (JsonUnit does not support JSONPath filter expressions; lazy-loading issue on detached entity)
Members can browse TM entries but cannot create, edit, import, or delete. Hide the "+ Entry" button, Import button, inline edit click, and delete button when the user is not an org owner or maintainer. Export stays visible (read-only). Also move inline French entry creation from the comma-separated language filter test into TranslationMemoryTestData. Update all affected assertion counts.
When the Formik field value is undefined and organization languages have loaded, automatically select the first available language. Improves create dialog UX for both TM and glossary.
Users with TRANSLATIONS_VIEW/EDIT see the TM list on project settings (read-only) but no longer see the drag handle, settings gear, "Manage all TMs" button, or the drag-to-reorder hint. Backend already requires PROJECT_EDIT for assign/unassign/update.
Users with PROJECT_EDIT but not org maintainer role see a simplified dialog from the gear icon: only read/write access toggles, no TM name/language/project assignment editing. Org maintainers still get the full TM settings dialog.
- BaseLanguageSelect: fix useEffect dependency array with ref guard to prevent re-render loop on auto-select - Add onError handlers to all TM mutation calls (settings save, entry CRUD, import, create dialog) with messageService.error toasts - Use TmxImportResult type from generated schema instead of `as any` - Memoize availableProjects in settings dialog - Add aria-label to remove-language icon button in create entry dialog - Fix prettier in ProjectTmAssignmentDialog
Extract TmxParsedEntry and TranslationMemoryEntryGroup into their own files instead of nesting them inside TmxParser and the management service respectively.
Existing projects created before the TM management feature have no project TM, so suggestions silently fell back to the legacy path even after the org gained TRANSLATION_MEMORY. Listen for OnOrganizationFeaturesChanged and create a project TM for each project in the org when the feature is gained. Backfill entries from existing translations in the same call via a native INSERT ... SELECT so the TM content browser is populated immediately. Add getOrCreateProjectTm(projectId) as a lazy safety net used by snapshot-on-disconnect; re-fetches inside the transaction so async callers don't trip over detached lazy proxies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two complementary migration paths for legacy projects that never received a project TM: - Spring Batch migration job runs on every app startup via the existing MigrationJobRunner framework. Iterates non-deleted projects without a project-type TM assignment and creates one for each whose organization has the TRANSLATION_MEMORY feature enabled. Dedupes via hashed job parameters so it only re-runs when the candidate set changes. - Lazy fallback in the suggestion endpoint (side panel hot path): ensureProjectTmIfFeatureEnabled is cheap when the TM exists (one indexed query) and short-circuits on free-plan orgs without a feature lookup cost. Together with the existing feature-gained listener this covers: plan upgrades while running (listener), orgs paid before the migration code shipped (startup job), and any residual gap (lazy fallback). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Placing the migration job in backend/data meant it was instantiated in every OSS test context as well. In CE, the feature check always returns false so the writer is a no-op, but the runner still queries all projects without a TM and launches the Spring Batch job. In batch-timing sensitive tests (BatchJobsGeneralWithRedisTest) this was enough to flake assertions that wait for Tolgee batch state transitions. The migration job is inherently EE-specific — it only does meaningful work when the TRANSLATION_MEMORY feature is enabled. Moving both the runner and configuration to ee-app ensures OSS installs and OSS test contexts don't instantiate it at all. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Displays "{N} entries" next to the search field on the TM content
browser, using the totalElements from the paginated response. Helps
users gauge TM size at a glance and provides immediate feedback on
filtering.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the main PR for the entire feature — it accumulates commits as the work progresses through the cycle.
Architecture decisions
POST /v2/projects/{id}/suggest/translation-memorydispatches at runtime: when the project has any readable TM assignments, it usesManagedTranslationMemorySuggestionService(queriestranslation_memory_entry); otherwise it falls back to the legacyTranslationMemoryService(queries thetranslationtable). No URL swap on the frontend, no breaking API change.getReadableTmIdsForSuggestions(projectId, organizationId)returns only the project's own PROJECT-type TM on free plan, or project + shared TMs on paid plan (Feature.TRANSLATION_MEMORY).getReadableTmIdsreturns empty and the dispatch picks the legacy path. No migration required.Status
✅ Done
Data model & write pipeline
TranslationMemory,TranslationMemoryEntry,TranslationMemoryProject(entity-backed join with read/write/priority).translation_memory_entry(target_language_tag, source_text).tuidcolumn on entries for TMX round-trip.ProjectCreationService.createProject()for every new project (all plans).TranslationService.save()for any project with writable TM assignments.ProjectHardDeletingServiceremoves project TM + assignments; cascade deletes via DB FK on translation removal.Org-level shared TM management (EE)
MAINTAINERorg role.Project-level TM configuration (EE)
keepData=truesnapshot on disconnect).@dnd-kit), Read/Write chips per row, settings gear (opens unified dialog), "Manage all TMs" button.Suggestion endpoint (CE)
/v2/projects/{id}/suggest/translation-memorydispatches between managed and legacy services.ManagedTranslationMemorySuggestionServicelives inbackend/data(CE), plan-aware viaEnabledFeaturesProvider.TmAutoTranslateProvidersimplified to a single CE impl with the same dispatch — EE override deleted.TMX import / export (EE)
tuid, groups bytuidor source text).tuid+ language).tuid/source).Plan gating
Feature.TRANSLATION_MEMORYflag, backend enforced on management endpoints + batch pre-translate.StartBatchJobController.translate()hard-gated with@RequiresFeatures(regression for free-plan API clients — documented)./suggest/translation-memoryagainst their project TM.🚧 To do
Deferred / cut
Plan gating UI rename ("Translation Memory" → "Similar translations" for free plan)— not needed: free plan now uses the same TM system, so the label stays "Translation Memory".Lazy migration for existing Business plan projects— handled implicitly by the dispatch (existing projects fall through to the legacy service; no backfill required).Tests
TranslationSuggestionControllerTmTest(legacy path),BatchPreTranslateByTmTest(paid happy path + free 400 gate).TranslationSuggestionControllerManagedTmTest(paid + free dispatch, project TM only on free),SharedTranslationMemoryControllerTest,ProjectTranslationMemoryControllerTest,TranslationMemoryEntryControllerTest,TranslationMemoryTmxControllerTest.orgSettings.cy.ts,projectSettings.cy.ts,tmContentBrowser.cy.ts. All pre-existing TM features covered.Test plan
POST /suggest/translation-memory→ 200;POST /start-batch-job/pre-translate-by-tm→ 400feature_not_enabled.