From 82314691ce98cedf9fc8112070db66eaa6fe3340 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Thu, 19 Feb 2026 22:09:50 -0500 Subject: [PATCH 01/13] Translation for basic translatable input and translatable textarea --- ...abstract-translatable-field.component.scss | 14 +++++ .../abstract-translatable-field.component.ts | 48 +++++++++++++++ .../translatable-asset-chooser.component.ts | 17 ++++-- .../translatable-input.component.html | 11 +++- .../translatable-input.component.ts | 16 +++-- ...translatable-rich-text-editor.component.ts | 32 ++++++---- .../translatable-textarea.component.html | 11 +++- .../translatable-textarea.component.ts | 16 +++-- ...nslation-suggestions-dialog.component.html | 15 +++++ ...nslation-suggestions-dialog.component.scss | 7 +++ ...ation-suggestions-dialog.component.spec.ts | 23 +++++++ ...ranslation-suggestions-dialog.component.ts | 60 +++++++++++++++++++ .../teacherProjectTranslationService.ts | 12 ++++ 13 files changed, 245 insertions(+), 37 deletions(-) create mode 100644 src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html create mode 100644 src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.scss create mode 100644 src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts create mode 100644 src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss index 7fdb11acd66..7444978a9d6 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss @@ -18,4 +18,18 @@ mat-form-field { padding: 4px 8px; margin-top: 4px; } + + .translatable-input-hints { + display: flex; + } + + .translation-suggestions { + margin-left: 8px; + text-decoration: underline; + color: blue; + } + + .translation-suggestions:hover { + cursor: pointer; + } } diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index e58bde1ec7f..bdc10026312 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -1,4 +1,5 @@ import { Input, Signal, Output, computed, Directive } from '@angular/core'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Subject, Subscription, debounceTime } from 'rxjs'; import { Language } from '../../../../../app/domain/language'; import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; @@ -6,6 +7,7 @@ import { TeacherProjectService } from '../../../services/teacherProjectService'; import { generateRandomKey } from '../../../common/string/string'; import { toObservable } from '@angular/core/rxjs-interop'; import { Translations } from '../../../../../app/domain/translations'; +import { TranslationSuggestionsDialogComponent } from '../translation-suggestions-dialog/translation-suggestions-dialog.component'; import { copy } from '../../../common/object/object'; @Directive() @@ -27,6 +29,7 @@ export abstract class AbstractTranslatableFieldComponent { protected translationText: string; protected translationTextChanged: Subject = new Subject(); constructor( + protected dialog: MatDialog, protected projectService: TeacherProjectService, protected projectTranslationService: TeacherProjectTranslationService ) {} @@ -75,4 +78,49 @@ export abstract class AbstractTranslatableFieldComponent { currentTranslations[this.i18nId] = { value: text, modified: new Date().getTime() }; this.projectTranslationService.saveCurrentTranslations(currentTranslations).subscribe(); } + + protected async translateText(): Promise { + if (this.translationText) { + this.openDialog(); + } else { + this.projectTranslationService + .getTranslationSuggestion( + this.defaultLanguage.language, + this.currentLanguage().language, + this.getDefaultLanguageTextContent() + ) + .subscribe((suggestedTranslation: string) => { + this.translationText = suggestedTranslation; + this.saveTranslation(); + }); + } + } + + protected abstract getDefaultLanguageTextContent(): string; + + private openDialog(): void { + const dialogRef = this.createDialogRef(); + dialogRef.afterClosed().subscribe((result: string) => { + if (result || result === '') { + this.translationText = result; + this.saveTranslation(); + } + }); + } + + private saveTranslation(): void { + this.saveTranslationText(this.translationText); + } + + private createDialogRef(): MatDialogRef { + return this.dialog.open(TranslationSuggestionsDialogComponent, { + width: '40%', + data: { + defaultLanguage: this.defaultLanguage, + currentLanguage: this.currentLanguage(), + defaultLanguageContent: this.getDefaultLanguageTextContent(), + currentLanguageContent: this.translationText + } + }); + } } diff --git a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts index 2af2a3b7d12..fe82cf8edf1 100644 --- a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts @@ -11,10 +11,10 @@ import { TeacherProjectService } from '../../../services/teacherProjectService'; import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; @Component({ - selector: 'translatable-asset-chooser', - imports: [CommonModule, MatButtonModule, MatIconModule, MatTooltipModule], - templateUrl: './translatable-asset-chooser.component.html', - styleUrl: './translatable-asset-chooser.component.scss' + selector: 'translatable-asset-chooser', + imports: [CommonModule, MatButtonModule, MatIconModule, MatTooltipModule], + templateUrl: './translatable-asset-chooser.component.html', + styleUrl: './translatable-asset-chooser.component.scss' }) export class TranslatableAssetChooserComponent extends AbstractTranslatableFieldComponent { @Input() tooltip: String = $localize`Choose image`; @@ -23,11 +23,11 @@ export class TranslatableAssetChooserComponent extends AbstractTranslatableField }; constructor( - private dialog: MatDialog, + protected dialog: MatDialog, protected projectService: TeacherProjectService, protected projectTranslationService: TeacherProjectTranslationService ) { - super(projectService, projectTranslationService); + super(dialog, projectService, projectTranslationService); } protected chooseAsset(): void { @@ -45,4 +45,9 @@ export class TranslatableAssetChooserComponent extends AbstractTranslatableField } }); } + + protected getDefaultLanguageTextContent(): string { + return this.content['prompt']; + //TODO: ... + } } diff --git a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html index a7d8c7594cd..e423f1e5ed5 100644 --- a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html @@ -11,9 +11,14 @@ @if (hint) { {{ hint }} } - - translate {{ defaultLanguage.language }}: - {{ content[key] }} + +
+ translate {{ defaultLanguage.language }}: + {{ content[key] }} +
+
+ auto_awesome Translate with AI +
} @else { diff --git a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts index 72cd1a71662..32104934a41 100644 --- a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts @@ -6,10 +6,14 @@ import { AbstractTranslatableFieldComponent } from '../abstract-translatable-fie import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'translatable-input', - imports: [CommonModule, FormsModule, MatIconModule, MatInputModule], - styleUrl: '../abstract-translatable-field/abstract-translatable-field.component.scss', - templateUrl: './translatable-input.component.html', - encapsulation: ViewEncapsulation.None + selector: 'translatable-input', + imports: [CommonModule, FormsModule, MatIconModule, MatInputModule], + styleUrl: '../abstract-translatable-field/abstract-translatable-field.component.scss', + templateUrl: './translatable-input.component.html', + encapsulation: ViewEncapsulation.None }) -export class TranslatableInputComponent extends AbstractTranslatableFieldComponent {} +export class TranslatableInputComponent extends AbstractTranslatableFieldComponent { + protected getDefaultLanguageTextContent(): string { + return this.content['title']; + } +} diff --git a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts index 9c52c2a0ef2..609307f573c 100644 --- a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts @@ -7,22 +7,22 @@ import { ConfigService } from '../../../services/configService'; import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; import { TeacherProjectService } from '../../../services/teacherProjectService'; import { CommonModule } from '@angular/common'; -import { MatDialogModule } from '@angular/material/dialog'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; import { FlexLayoutModule } from '@angular/flex-layout'; @Component({ - selector: 'translatable-rich-text-editor', - imports: [ - CommonModule, - FlexLayoutModule, - MatButtonModule, - MatDialogModule, - MatTabsModule, - WiseTinymceEditorModule - ], - templateUrl: './translatable-rich-text-editor.component.html', - styleUrl: './translatable-rich-text-editor.component.scss' + selector: 'translatable-rich-text-editor', + imports: [ + CommonModule, + FlexLayoutModule, + MatButtonModule, + MatDialogModule, + MatTabsModule, + WiseTinymceEditorModule + ], + templateUrl: './translatable-rich-text-editor.component.html', + styleUrl: './translatable-rich-text-editor.component.scss' }) export class TranslatableRichTextEditorComponent extends AbstractTranslatableFieldComponent { protected html: string = ''; @@ -30,10 +30,11 @@ export class TranslatableRichTextEditorComponent extends AbstractTranslatableFie constructor( private configService: ConfigService, + protected dialog: MatDialog, protected projectService: TeacherProjectService, protected projectTranslationService: TeacherProjectTranslationService ) { - super(projectService, projectTranslationService); + super(dialog, projectService, projectTranslationService); } ngOnChanges(): void { @@ -71,4 +72,9 @@ export class TranslatableRichTextEditorComponent extends AbstractTranslatableFie protected saveTranslationText(text: string): void { super.saveTranslationText(insertWiseLinks(this.configService.removeAbsoluteAssetPaths(text))); } + + protected getDefaultLanguageTextContent(): string { + return this.content['prompt']; + //TODO: ... + } } diff --git a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html index 3a173e6ec05..32e37ce5f13 100644 --- a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html @@ -12,9 +12,14 @@ @if (hint) { {{ hint }} } - - translate {{ defaultLanguage.language }}: - {{ content[key] }} + +
+ translate {{ defaultLanguage.language }}: + {{ content[key] }} +
+
+ auto_awesome Translate with AI +
} @else { diff --git a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts index a87e5e3d7a4..7e5d7580ac6 100644 --- a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts @@ -6,10 +6,14 @@ import { AbstractTranslatableFieldComponent } from '../abstract-translatable-fie import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'translatable-textarea', - imports: [CommonModule, FormsModule, MatIconModule, MatInputModule], - styleUrl: '../abstract-translatable-field/abstract-translatable-field.component.scss', - templateUrl: './translatable-textarea.component.html', - encapsulation: ViewEncapsulation.None + selector: 'translatable-textarea', + imports: [CommonModule, FormsModule, MatIconModule, MatInputModule], + styleUrl: '../abstract-translatable-field/abstract-translatable-field.component.scss', + templateUrl: './translatable-textarea.component.html', + encapsulation: ViewEncapsulation.None }) -export class TranslatableTextareaComponent extends AbstractTranslatableFieldComponent {} +export class TranslatableTextareaComponent extends AbstractTranslatableFieldComponent { + protected getDefaultLanguageTextContent(): string { + return this.content['prompt']; + } +} diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html new file mode 100644 index 00000000000..a7d03b55540 --- /dev/null +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html @@ -0,0 +1,15 @@ + +

English text

+

{{ this.data.defaultLanguageContent }}

+

Spanish text (current translation)

+

{{ this.data.currentLanguageContent }}

+

Spanish text (AI suggested translation)

+

{{ this.translation }}

+

+ Do you want to replace the current translation with the AI suggested translation? +

+
+ + + + diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.scss b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.scss new file mode 100644 index 00000000000..2991245a003 --- /dev/null +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.scss @@ -0,0 +1,7 @@ +.text-content { + margin-left: 15px; +} + +.replace-translation { + margin-top: 20px; +} \ No newline at end of file diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts new file mode 100644 index 00000000000..e773167da73 --- /dev/null +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TranslationSuggestionsDialogComponent } from './translation-suggestions-dialog.component'; + +describe('TranslationSuggestionsDialogComponent', () => { + let component: TranslationSuggestionsDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TranslationSuggestionsDialogComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TranslationSuggestionsDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts new file mode 100644 index 00000000000..62e77ef2436 --- /dev/null +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts @@ -0,0 +1,60 @@ +import { Component, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { + MAT_DIALOG_DATA, + MatDialogActions, + MatDialogContent, + MatDialogRef +} from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { Language } from '../../../../../app/domain/language'; +import { TranslateClient, TranslateTextCommand } from '@aws-sdk/client-translate'; +import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; + +interface TranslationSuggestionsDialogData { + defaultLanguage: Language; + currentLanguage: Language; + defaultLanguageContent: string; + currentLanguageContent?: string; +} + +@Component({ + selector: 'translation-suggestions-dialog', + imports: [ + MatFormFieldModule, + MatInputModule, + FormsModule, + MatButtonModule, + MatDialogContent, + MatDialogActions + ], + templateUrl: './translation-suggestions-dialog.component.html', + styleUrl: './translation-suggestions-dialog.component.scss' +}) +export class TranslationSuggestionsDialogComponent { + readonly dialogRef = inject(MatDialogRef); + readonly data = inject(MAT_DIALOG_DATA); + protected translation; + + constructor(protected projectTranslationService: TeacherProjectTranslationService) { + this.generateTranslationSuggestion(); + } + + private generateTranslationSuggestion(): void { + this.projectTranslationService + .getTranslationSuggestion( + this.data.defaultLanguage.language, + this.data.currentLanguage.language, + this.data.defaultLanguageContent + ) + .subscribe((suggestedTranslation: string) => { + this.translation = suggestedTranslation; + }); + } + + protected onClose(saveTranslation: boolean): void { + this.dialogRef.close(saveTranslation && this.translation); + } +} diff --git a/src/assets/wise5/services/teacherProjectTranslationService.ts b/src/assets/wise5/services/teacherProjectTranslationService.ts index 627dfd30a2e..f11ad3136bf 100644 --- a/src/assets/wise5/services/teacherProjectTranslationService.ts +++ b/src/assets/wise5/services/teacherProjectTranslationService.ts @@ -48,4 +48,16 @@ export class TeacherProjectTranslationService extends ProjectTranslationService }) ); } + + getTranslationSuggestion( + defaultLanguage: string, + currentLanguage: string, + defaultLanguageText: string + ): Observable { + return this.http.post( + `/api/author/project/translate/translationSuggestions`, + { srcLang: defaultLanguage, targetLang: currentLanguage, srcText: defaultLanguageText }, + { responseType: 'text' } + ); + } } From e92a4f15ac39fc2e30625a253d46ad0d416ad538 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Fri, 20 Feb 2026 19:30:26 -0500 Subject: [PATCH 02/13] Fixed issues with translatable input and textarea --- .../abstract-translatable-field.component.ts | 10 ++++------ .../translatable-asset-chooser.component.ts | 5 ----- .../translatable-input.component.html | 8 +++++--- .../translatable-input.component.ts | 6 +----- .../translatable-textarea.component.html | 8 +++++--- .../translatable-textarea.component.ts | 6 +----- .../translation-suggestions-dialog.component.html | 12 +++++++++--- .../translation-suggestions-dialog.component.ts | 10 ++++------ 8 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index bdc10026312..964490f9bbe 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -87,7 +87,7 @@ export abstract class AbstractTranslatableFieldComponent { .getTranslationSuggestion( this.defaultLanguage.language, this.currentLanguage().language, - this.getDefaultLanguageTextContent() + this.content[this.key] ) .subscribe((suggestedTranslation: string) => { this.translationText = suggestedTranslation; @@ -96,8 +96,6 @@ export abstract class AbstractTranslatableFieldComponent { } } - protected abstract getDefaultLanguageTextContent(): string; - private openDialog(): void { const dialogRef = this.createDialogRef(); dialogRef.afterClosed().subscribe((result: string) => { @@ -116,9 +114,9 @@ export abstract class AbstractTranslatableFieldComponent { return this.dialog.open(TranslationSuggestionsDialogComponent, { width: '40%', data: { - defaultLanguage: this.defaultLanguage, - currentLanguage: this.currentLanguage(), - defaultLanguageContent: this.getDefaultLanguageTextContent(), + defaultLanguage: this.defaultLanguage.language, + currentLanguage: this.currentLanguage().language, + defaultLanguageContent: this.content[this.key], currentLanguageContent: this.translationText } }); diff --git a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts index fe82cf8edf1..9c7d122a7aa 100644 --- a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts @@ -45,9 +45,4 @@ export class TranslatableAssetChooserComponent extends AbstractTranslatableField } }); } - - protected getDefaultLanguageTextContent(): string { - return this.content['prompt']; - //TODO: ... - } } diff --git a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html index e423f1e5ed5..f585e8ad243 100644 --- a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html @@ -16,9 +16,11 @@ translate {{ defaultLanguage.language }}: {{ content[key] }} -
- auto_awesome Translate with AI -
+ @if (content[key]) { +
+ auto_awesome Translate with AI +
+ } } @else { diff --git a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts index 32104934a41..4cb7cb901ba 100644 --- a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.ts @@ -12,8 +12,4 @@ import { MatIconModule } from '@angular/material/icon'; templateUrl: './translatable-input.component.html', encapsulation: ViewEncapsulation.None }) -export class TranslatableInputComponent extends AbstractTranslatableFieldComponent { - protected getDefaultLanguageTextContent(): string { - return this.content['title']; - } -} +export class TranslatableInputComponent extends AbstractTranslatableFieldComponent {} diff --git a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html index 32e37ce5f13..165432a2a91 100644 --- a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html @@ -17,9 +17,11 @@ translate {{ defaultLanguage.language }}: {{ content[key] }} -
- auto_awesome Translate with AI -
+ @if (content[key]) { +
+ auto_awesome Translate with AI +
+ } } @else { diff --git a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts index 7e5d7580ac6..57cc6aea059 100644 --- a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.ts @@ -12,8 +12,4 @@ import { MatIconModule } from '@angular/material/icon'; templateUrl: './translatable-textarea.component.html', encapsulation: ViewEncapsulation.None }) -export class TranslatableTextareaComponent extends AbstractTranslatableFieldComponent { - protected getDefaultLanguageTextContent(): string { - return this.content['prompt']; - } -} +export class TranslatableTextareaComponent extends AbstractTranslatableFieldComponent {} diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html index a7d03b55540..c443962adaf 100644 --- a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.html @@ -1,9 +1,15 @@ -

English text

+

+ {{ this.data.defaultLanguage }} text +

{{ this.data.defaultLanguageContent }}

-

Spanish text (current translation)

+

+ {{ this.data.currentLanguage }} text (current translation) +

{{ this.data.currentLanguageContent }}

-

Spanish text (AI suggested translation)

+

+ {{ this.data.currentLanguage }} text (AI suggested translation) +

{{ this.translation }}

Do you want to replace the current translation with the AI suggested translation? diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts index 62e77ef2436..214ad5c9491 100644 --- a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts @@ -9,13 +9,11 @@ import { } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { Language } from '../../../../../app/domain/language'; -import { TranslateClient, TranslateTextCommand } from '@aws-sdk/client-translate'; import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; interface TranslationSuggestionsDialogData { - defaultLanguage: Language; - currentLanguage: Language; + defaultLanguage: string; + currentLanguage: string; defaultLanguageContent: string; currentLanguageContent?: string; } @@ -45,8 +43,8 @@ export class TranslationSuggestionsDialogComponent { private generateTranslationSuggestion(): void { this.projectTranslationService .getTranslationSuggestion( - this.data.defaultLanguage.language, - this.data.currentLanguage.language, + this.data.defaultLanguage, + this.data.currentLanguage, this.data.defaultLanguageContent ) .subscribe((suggestedTranslation: string) => { From 8441e2c86f2a0d3830d69f39e184218cfacf382b Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Fri, 20 Feb 2026 19:33:03 -0500 Subject: [PATCH 03/13] Translation for HTML component with do-not-translate tags for HTML tags --- ...abstract-translatable-field.component.scss | 26 ++++++++++------- ...anslatable-rich-text-editor.component.html | 7 +++++ ...translatable-rich-text-editor.component.ts | 9 ++---- .../teacherProjectTranslationService.ts | 29 +++++++++++++++---- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss index 7444978a9d6..89ac4226b78 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.scss @@ -18,18 +18,22 @@ mat-form-field { padding: 4px 8px; margin-top: 4px; } +} - .translatable-input-hints { - display: flex; - } +.translatable-input-hints { + display: flex; +} - .translation-suggestions { - margin-left: 8px; - text-decoration: underline; - color: blue; - } +.translation-suggestions { + margin-left: 8px; + text-decoration: underline; + color: blue; +} - .translation-suggestions:hover { - cursor: pointer; - } +.translation-suggestions:hover { + cursor: pointer; } + +.translation-tools { + padding: 8px 0; +} \ No newline at end of file diff --git a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html index e8ce5dc807b..5e68a156689 100644 --- a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html @@ -50,6 +50,13 @@ + @if (content[key]) { +

+ + auto_awesome Translate with AI + +
+ } } @else { diff --git a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts index 609307f573c..053b4001eb0 100644 --- a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts @@ -9,6 +9,7 @@ import { TeacherProjectService } from '../../../services/teacherProjectService'; import { CommonModule } from '@angular/common'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; import { FlexLayoutModule } from '@angular/flex-layout'; @Component({ @@ -18,11 +19,12 @@ import { FlexLayoutModule } from '@angular/flex-layout'; FlexLayoutModule, MatButtonModule, MatDialogModule, + MatIconModule, MatTabsModule, WiseTinymceEditorModule ], templateUrl: './translatable-rich-text-editor.component.html', - styleUrl: './translatable-rich-text-editor.component.scss' + styleUrl: '../abstract-translatable-field/abstract-translatable-field.component.scss' }) export class TranslatableRichTextEditorComponent extends AbstractTranslatableFieldComponent { protected html: string = ''; @@ -72,9 +74,4 @@ export class TranslatableRichTextEditorComponent extends AbstractTranslatableFie protected saveTranslationText(text: string): void { super.saveTranslationText(insertWiseLinks(this.configService.removeAbsoluteAssetPaths(text))); } - - protected getDefaultLanguageTextContent(): string { - return this.content['prompt']; - //TODO: ... - } } diff --git a/src/assets/wise5/services/teacherProjectTranslationService.ts b/src/assets/wise5/services/teacherProjectTranslationService.ts index f11ad3136bf..e65f2611ef9 100644 --- a/src/assets/wise5/services/teacherProjectTranslationService.ts +++ b/src/assets/wise5/services/teacherProjectTranslationService.ts @@ -53,11 +53,30 @@ export class TeacherProjectTranslationService extends ProjectTranslationService defaultLanguage: string, currentLanguage: string, defaultLanguageText: string - ): Observable { - return this.http.post( - `/api/author/project/translate/translationSuggestions`, - { srcLang: defaultLanguage, targetLang: currentLanguage, srcText: defaultLanguageText }, - { responseType: 'text' } + ): Observable { + return this.http + .post( + `/api/author/project/translate/translationSuggestions`, + { + srcLang: defaultLanguage, + targetLang: currentLanguage, + srcText: this.addDoNotTranslateTags(defaultLanguageText) + }, + { responseType: 'text' } + ) + .pipe(map(this.removeDoNotTranslateTags)); + } + + private addDoNotTranslateTags(textToTranslate: string): string { + return textToTranslate.replaceAll( + /<.*?>/g, + (match) => '' + match + '' + ); + } + + private removeDoNotTranslateTags(translatedText: string): string { + return translatedText.replaceAll(/<.*?><\/span>/g, (match) => + match.slice(21, -7) ); } } From 19571a6941157008fa99cac87d9502fe5c8936d0 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Sat, 21 Feb 2026 23:32:25 -0500 Subject: [PATCH 04/13] Failed to fix the bug of multiple fields getting overwritten by the same translationText when switching languages or generating translations --- .../abstract-translatable-field.component.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index 964490f9bbe..d9a46c23aa4 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -90,8 +90,7 @@ export abstract class AbstractTranslatableFieldComponent { this.content[this.key] ) .subscribe((suggestedTranslation: string) => { - this.translationText = suggestedTranslation; - this.saveTranslation(); + this.saveTranslationText(suggestedTranslation); }); } } @@ -100,16 +99,11 @@ export abstract class AbstractTranslatableFieldComponent { const dialogRef = this.createDialogRef(); dialogRef.afterClosed().subscribe((result: string) => { if (result || result === '') { - this.translationText = result; - this.saveTranslation(); + this.saveTranslationText(result); } }); } - private saveTranslation(): void { - this.saveTranslationText(this.translationText); - } - private createDialogRef(): MatDialogRef { return this.dialog.open(TranslationSuggestionsDialogComponent, { width: '40%', From 36f90190b9a725068f4d3cd181a69a69c7dde2ac Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Sun, 22 Feb 2026 23:02:08 -0500 Subject: [PATCH 05/13] Fixed failing test --- package-lock.json | 270 ++++++++++++++++-- .../abstract-translatable-field.component.ts | 6 +- ...ation-suggestions-dialog.component.spec.ts | 22 +- 3 files changed, 258 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 901f8ca24df..fbd5b974ff7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24329,6 +24329,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -24346,6 +24347,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -24357,13 +24359,15 @@ "version": "9.2.2", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -24381,6 +24385,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -24396,6 +24401,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^7.0.4" }, @@ -24407,13 +24413,15 @@ "version": "1.1.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/@npmcli/agent": { "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", @@ -24430,6 +24438,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^4.0.0", @@ -24479,6 +24488,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/map-workspaces": "^4.0.1", "@npmcli/package-json": "^6.0.1", @@ -24498,6 +24508,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "semver": "^7.3.5" }, @@ -24510,6 +24521,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/promise-spawn": "^8.0.0", "ini": "^5.0.0", @@ -24529,6 +24541,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "npm-bundled": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" @@ -24545,6 +24558,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/name-from-folder": "^3.0.0", "@npmcli/package-json": "^6.0.0", @@ -24560,6 +24574,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "cacache": "^19.0.0", "json-parse-even-better-errors": "^4.0.0", @@ -24576,6 +24591,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/git": "^6.0.0", "@npmcli/installed-package-contents": "^3.0.0", @@ -24607,6 +24623,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -24616,6 +24633,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -24625,6 +24643,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/git": "^6.0.0", "glob": "^10.2.2", @@ -24643,6 +24662,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "which": "^5.0.0" }, @@ -24655,6 +24675,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -24667,6 +24688,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -24676,6 +24698,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/node-gyp": "^4.0.0", "@npmcli/package-json": "^6.0.0", @@ -24694,6 +24717,7 @@ "inBundle": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=14" } @@ -24703,6 +24727,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -24712,6 +24737,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@sigstore/protobuf-specs": "^0.4.1", "tuf-js": "^3.0.1" @@ -24725,6 +24751,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": "^16.14.0 || >=18.0.0" } @@ -24734,6 +24761,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -24743,6 +24771,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 14" } @@ -24752,6 +24781,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -24761,6 +24791,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -24772,25 +24803,29 @@ "version": "2.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/archy": { "version": "1.0.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/bin-links": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "cmd-shim": "^7.0.0", "npm-normalize-package-bin": "^4.0.0", @@ -24807,6 +24842,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -24819,6 +24855,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -24828,6 +24865,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", @@ -24851,6 +24889,7 @@ "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=18" } @@ -24860,6 +24899,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "bin": { "mkdirp": "dist/cjs/src/bin.js" }, @@ -24875,6 +24915,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -24892,6 +24933,7 @@ "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=18" } @@ -24901,6 +24943,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -24913,6 +24956,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": ">=10" } @@ -24928,6 +24972,7 @@ ], "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -24937,6 +24982,7 @@ "dev": true, "inBundle": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "ip-regex": "^5.0.0" }, @@ -24949,6 +24995,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "string-width": "^4.2.3", "strip-ansi": "^6.0.1" @@ -24962,6 +25009,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -24971,6 +25019,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -24982,19 +25031,22 @@ "version": "1.1.4", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.6", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -25009,6 +25061,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -25024,6 +25077,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "bin": { "cssesc": "bin/cssesc" }, @@ -25036,6 +25090,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -25053,6 +25108,7 @@ "dev": true, "inBundle": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.3.1" } @@ -25061,13 +25117,15 @@ "version": "0.2.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/encoding": { "version": "0.1.13", @@ -25075,6 +25133,7 @@ "inBundle": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -25084,6 +25143,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -25092,19 +25152,22 @@ "version": "2.0.3", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.2", "dev": true, "inBundle": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/npm/node_modules/fastest-levenshtein": { "version": "1.0.16", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4.9.1" } @@ -25114,6 +25177,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -25130,6 +25194,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^7.0.3" }, @@ -25142,6 +25207,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -25161,13 +25227,15 @@ "version": "4.2.11", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/hosted-git-info": { "version": "8.1.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "lru-cache": "^10.0.1" }, @@ -25179,13 +25247,15 @@ "version": "4.2.0", "dev": true, "inBundle": true, - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/npm/node_modules/http-proxy-agent": { "version": "7.0.2", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -25199,6 +25269,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -25213,6 +25284,7 @@ "inBundle": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -25225,6 +25297,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minimatch": "^9.0.0" }, @@ -25237,6 +25310,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.19" } @@ -25246,6 +25320,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -25255,6 +25330,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/package-json": "^6.0.0", "npm-package-arg": "^12.0.0", @@ -25273,6 +25349,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -25286,6 +25363,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -25298,6 +25376,7 @@ "dev": true, "inBundle": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "cidr-regex": "^4.1.1" }, @@ -25310,6 +25389,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -25318,13 +25398,15 @@ "version": "2.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/jackspeak": { "version": "3.4.3", "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -25339,13 +25421,15 @@ "version": "1.1.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -25355,6 +25439,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -25366,25 +25451,29 @@ "node >= 0.2.0" ], "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/just-diff": { "version": "6.0.2", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/just-diff-apply": { "version": "5.5.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/libnpmaccess": { "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "npm-package-arg": "^12.0.0", "npm-registry-fetch": "^18.0.1" @@ -25398,6 +25487,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/arborist": "^8.0.1", "@npmcli/installed-package-contents": "^3.0.0", @@ -25417,6 +25507,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/arborist": "^8.0.1", "@npmcli/run-script": "^9.0.1", @@ -25438,6 +25529,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/arborist": "^8.0.1" }, @@ -25450,6 +25542,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^18.0.1" @@ -25463,6 +25556,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^18.0.1" @@ -25476,6 +25570,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/arborist": "^8.0.1", "@npmcli/run-script": "^9.0.1", @@ -25491,6 +25586,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "ci-info": "^4.0.0", "normalize-package-data": "^7.0.0", @@ -25510,6 +25606,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "npm-registry-fetch": "^18.0.1" }, @@ -25522,6 +25619,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^18.0.1" @@ -25535,6 +25633,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/git": "^6.0.1", "@npmcli/run-script": "^9.0.1", @@ -25550,13 +25649,15 @@ "version": "10.4.3", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/make-fetch-happen": { "version": "14.0.3", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/agent": "^3.0.0", "cacache": "^19.0.1", @@ -25579,6 +25680,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -25588,6 +25690,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -25603,6 +25706,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -25612,6 +25716,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^7.0.3" }, @@ -25624,6 +25729,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", @@ -25641,6 +25747,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -25653,6 +25760,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -25665,6 +25773,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -25677,6 +25786,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -25689,6 +25799,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -25701,6 +25812,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -25713,6 +25825,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "minipass": "^7.1.2" }, @@ -25725,6 +25838,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -25736,13 +25850,15 @@ "version": "2.1.3", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/mute-stream": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -25752,6 +25868,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", @@ -25776,6 +25893,7 @@ "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=18" } @@ -25785,6 +25903,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "bin": { "mkdirp": "dist/cjs/src/bin.js" }, @@ -25800,6 +25919,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -25817,6 +25937,7 @@ "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", + "peer": true, "engines": { "node": ">=18" } @@ -25826,6 +25947,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "abbrev": "^3.0.0" }, @@ -25841,6 +25963,7 @@ "dev": true, "inBundle": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "hosted-git-info": "^8.0.0", "semver": "^7.3.5", @@ -25855,6 +25978,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -25864,6 +25988,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "npm-normalize-package-bin": "^4.0.0" }, @@ -25876,6 +26001,7 @@ "dev": true, "inBundle": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "semver": "^7.1.1" }, @@ -25888,6 +26014,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -25897,6 +26024,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "hosted-git-info": "^8.0.0", "proc-log": "^5.0.0", @@ -25912,6 +26040,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "ignore-walk": "^7.0.0" }, @@ -25924,6 +26053,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "npm-install-checks": "^7.1.0", "npm-normalize-package-bin": "^4.0.0", @@ -25939,6 +26069,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "npm-registry-fetch": "^18.0.0", "proc-log": "^5.0.0" @@ -25952,6 +26083,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/redact": "^3.0.0", "jsonparse": "^1.3.1", @@ -25971,6 +26103,7 @@ "dev": true, "inBundle": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -25980,6 +26113,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -25991,13 +26125,15 @@ "version": "1.0.1", "dev": true, "inBundle": true, - "license": "BlueOak-1.0.0" + "license": "BlueOak-1.0.0", + "peer": true }, "node_modules/npm/node_modules/pacote": { "version": "19.0.1", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "@npmcli/git": "^6.0.0", "@npmcli/installed-package-contents": "^3.0.0", @@ -26029,6 +26165,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "json-parse-even-better-errors": "^4.0.0", "just-diff": "^6.0.0", @@ -26043,6 +26180,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -26052,6 +26190,7 @@ "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", + "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -26068,6 +26207,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -26081,6 +26221,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -26090,6 +26231,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -26099,6 +26241,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -26108,6 +26251,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -26117,6 +26261,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -26130,6 +26275,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "read": "^4.0.0" }, @@ -26141,6 +26287,7 @@ "version": "0.12.0", "dev": true, "inBundle": true, + "peer": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" } @@ -26150,6 +26297,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "mute-stream": "^2.0.0" }, @@ -26162,6 +26310,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -26171,6 +26320,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" @@ -26184,6 +26334,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } @@ -26193,13 +26344,15 @@ "dev": true, "inBundle": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/npm/node_modules/semver": { "version": "7.7.2", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -26212,6 +26365,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -26224,6 +26378,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -26233,6 +26388,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": ">=14" }, @@ -26245,6 +26401,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@sigstore/bundle": "^3.1.0", "@sigstore/core": "^2.0.0", @@ -26262,6 +26419,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@sigstore/protobuf-specs": "^0.4.0" }, @@ -26274,6 +26432,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -26283,6 +26442,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@sigstore/bundle": "^3.1.0", "@sigstore/core": "^2.0.0", @@ -26300,6 +26460,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@sigstore/bundle": "^3.1.0", "@sigstore/core": "^2.0.0", @@ -26314,6 +26475,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -26324,6 +26486,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -26338,6 +26501,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -26352,6 +26516,7 @@ "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -26362,6 +26527,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -26371,13 +26537,15 @@ "version": "2.5.0", "dev": true, "inBundle": true, - "license": "CC-BY-3.0" + "license": "CC-BY-3.0", + "peer": true }, "node_modules/npm/node_modules/spdx-expression-parse": { "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -26387,19 +26555,22 @@ "version": "3.0.21", "dev": true, "inBundle": true, - "license": "CC0-1.0" + "license": "CC0-1.0", + "peer": true }, "node_modules/npm/node_modules/sprintf-js": { "version": "1.1.3", "dev": true, "inBundle": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/npm/node_modules/ssri": { "version": "12.0.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^7.0.3" }, @@ -26412,6 +26583,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -26427,6 +26599,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -26441,6 +26614,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -26454,6 +26628,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -26466,6 +26641,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -26478,6 +26654,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -26495,6 +26672,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -26507,6 +26685,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -26519,6 +26698,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": ">=8" } @@ -26528,6 +26708,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -26541,6 +26722,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -26552,19 +26734,22 @@ "version": "0.2.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/tiny-relative-date": { "version": "1.3.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/tinyglobby": { "version": "0.2.14", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" @@ -26581,6 +26766,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -26595,6 +26781,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -26607,6 +26794,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -26616,6 +26804,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "@tufjs/models": "3.0.1", "debug": "^4.3.6", @@ -26630,6 +26819,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.5" @@ -26643,6 +26833,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "unique-slug": "^5.0.0" }, @@ -26655,6 +26846,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "imurmurhash": "^0.1.4" }, @@ -26666,13 +26858,15 @@ "version": "1.0.2", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/validate-npm-package-license": { "version": "3.0.4", "dev": true, "inBundle": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -26683,6 +26877,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -26693,6 +26888,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -26701,13 +26897,15 @@ "version": "3.0.1", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/npm/node_modules/which": { "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "isexe": "^3.1.1" }, @@ -26723,6 +26921,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "engines": { "node": ">=16" } @@ -26732,6 +26931,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -26750,6 +26950,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -26767,6 +26968,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -26782,6 +26984,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -26793,13 +26996,15 @@ "version": "9.2.2", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { "version": "5.1.2", "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -26817,6 +27022,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -26832,6 +27038,7 @@ "dev": true, "inBundle": true, "license": "ISC", + "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -26844,7 +27051,8 @@ "version": "4.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/nth-check": { "version": "2.1.1", diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index d9a46c23aa4..2c9d7904e13 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -89,16 +89,14 @@ export abstract class AbstractTranslatableFieldComponent { this.currentLanguage().language, this.content[this.key] ) - .subscribe((suggestedTranslation: string) => { - this.saveTranslationText(suggestedTranslation); - }); + .subscribe((translation) => this.saveTranslationText(translation)); } } private openDialog(): void { const dialogRef = this.createDialogRef(); dialogRef.afterClosed().subscribe((result: string) => { - if (result || result === '') { + if (result) { this.saveTranslationText(result); } }); diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts index e773167da73..a36bfd1f918 100644 --- a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts @@ -1,16 +1,28 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; import { TranslationSuggestionsDialogComponent } from './translation-suggestions-dialog.component'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MockProvider } from 'ng-mocks'; +import { of } from 'rxjs'; -describe('TranslationSuggestionsDialogComponent', () => { +fdescribe('TranslationSuggestionsDialogComponent', () => { let component: TranslationSuggestionsDialogComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [TranslationSuggestionsDialogComponent] - }) - .compileComponents(); + imports: [TranslationSuggestionsDialogComponent], + providers: [ + MockProvider(TeacherProjectTranslationService), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: {} } + ] + }).compileComponents(); + + spyOn( + TestBed.inject(TeacherProjectTranslationService), + 'getTranslationSuggestion' + ).and.returnValue(of('Example translated text')); fixture = TestBed.createComponent(TranslationSuggestionsDialogComponent); component = fixture.componentInstance; From ab11e583a15fd865bfa4851f27bae58b8ea1f179 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Feb 2026 04:06:30 +0000 Subject: [PATCH 06/13] Updated messages --- src/messages.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messages.xlf b/src/messages.xlf index 4013aba5253..26a59d6082f 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -11048,7 +11048,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.Are you sure you want to replace the content in with content in for this item? src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts - 50,52 + 59,61 From e7f5f7a5e784d51cfa00b90e5f0f629839b0914a Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Sun, 22 Feb 2026 23:18:02 -0500 Subject: [PATCH 07/13] Added test for getTranslationSuggestion() --- .../teacherProjectTranslationService.spec.ts | 15 +++++++++++++++ ...anslation-suggestions-dialog.component.spec.ts | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/services/teacherProjectTranslationService.spec.ts b/src/app/services/teacherProjectTranslationService.spec.ts index d7ddb632fa6..c206f6c0b3c 100644 --- a/src/app/services/teacherProjectTranslationService.spec.ts +++ b/src/app/services/teacherProjectTranslationService.spec.ts @@ -48,4 +48,19 @@ describe('TeacherProjectTranslationService', () => { expect(request.request.body).toEqual({}); }); }); + describe('getTranslationSuggestion()', () => { + it('makes a POST request to backend with do not translate tags', () => { + service + .getTranslationSuggestion('srcLang', 'targetLang', 'srcText untagged') + .subscribe(); + const request = http.expectOne(`/api/author/project/translate/translationSuggestions`); + expect(request.request.method).toEqual('POST'); + expect(request.request.body).toEqual({ + srcLang: 'srcLang', + targetLang: 'targetLang', + srcText: + 'srcText untagged' + }); + }); + }); }); diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts index a36bfd1f918..5a129a2df31 100644 --- a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.spec.ts @@ -5,7 +5,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; -fdescribe('TranslationSuggestionsDialogComponent', () => { +describe('TranslationSuggestionsDialogComponent', () => { let component: TranslationSuggestionsDialogComponent; let fixture: ComponentFixture; From b6b0f4bcd05aad7f33f73a7fb2cac2f27dc65459 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Fri, 13 Mar 2026 15:09:18 -0400 Subject: [PATCH 08/13] Handle server error when trying to translate without app props set --- .../abstract-translatable-field.component.ts | 5 ++++- .../translation-suggestions-dialog.component.ts | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index 2c9d7904e13..99d698a65bb 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -89,7 +89,10 @@ export abstract class AbstractTranslatableFieldComponent { this.currentLanguage().language, this.content[this.key] ) - .subscribe((translation) => this.saveTranslationText(translation)); + .subscribe({ + next: (translation) => this.saveTranslationText(translation), + error: () => alert("AWS Translate settings have not been configured") + }); } } diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts index 214ad5c9491..f6822ba97ba 100644 --- a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts @@ -47,8 +47,9 @@ export class TranslationSuggestionsDialogComponent { this.data.currentLanguage, this.data.defaultLanguageContent ) - .subscribe((suggestedTranslation: string) => { - this.translation = suggestedTranslation; + .subscribe({ + next: (suggestedTranslation) => this.translation = suggestedTranslation, + error: () => alert("AWS Translate settings have not been configured") }); } From dd4be08ce5556b002bbf84ccaab7586d6b47c3ac Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Fri, 13 Mar 2026 15:10:52 -0400 Subject: [PATCH 09/13] Changed translate endpoint from translate/translationSuggestions to translate/suggest --- src/app/services/teacherProjectTranslationService.spec.ts | 2 +- src/assets/wise5/services/teacherProjectTranslationService.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/services/teacherProjectTranslationService.spec.ts b/src/app/services/teacherProjectTranslationService.spec.ts index c206f6c0b3c..9509e8eb58c 100644 --- a/src/app/services/teacherProjectTranslationService.spec.ts +++ b/src/app/services/teacherProjectTranslationService.spec.ts @@ -53,7 +53,7 @@ describe('TeacherProjectTranslationService', () => { service .getTranslationSuggestion('srcLang', 'targetLang', 'srcText untagged') .subscribe(); - const request = http.expectOne(`/api/author/project/translate/translationSuggestions`); + const request = http.expectOne(`/api/author/project/translate/suggest`); expect(request.request.method).toEqual('POST'); expect(request.request.body).toEqual({ srcLang: 'srcLang', diff --git a/src/assets/wise5/services/teacherProjectTranslationService.ts b/src/assets/wise5/services/teacherProjectTranslationService.ts index e65f2611ef9..680991fb95f 100644 --- a/src/assets/wise5/services/teacherProjectTranslationService.ts +++ b/src/assets/wise5/services/teacherProjectTranslationService.ts @@ -56,7 +56,7 @@ export class TeacherProjectTranslationService extends ProjectTranslationService ): Observable { return this.http .post( - `/api/author/project/translate/translationSuggestions`, + `/api/author/project/translate/suggest`, { srcLang: defaultLanguage, targetLang: currentLanguage, From b27f2b0505c0115be7e594fcfc9411285be83774 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Sat, 14 Mar 2026 13:42:20 -0400 Subject: [PATCH 10/13] Only show translate button if AWS props have been configured + if configured wrong, alert on click --- .../abstract-translatable-field.component.ts | 15 +++++++++++---- .../translatable-asset-chooser.component.ts | 4 +++- .../translatable-input.component.html | 2 +- .../translatable-rich-text-editor.component.html | 2 +- .../translatable-rich-text-editor.component.ts | 4 ++-- .../translatable-textarea.component.html | 2 +- .../translation-suggestions-dialog.component.ts | 5 +++-- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index 99d698a65bb..bd6133dc3b1 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -1,14 +1,15 @@ +import { ConfigService } from '../../../services/configService'; +import { copy } from '../../../common/object/object'; +import { generateRandomKey } from '../../../common/string/string'; import { Input, Signal, Output, computed, Directive } from '@angular/core'; +import { Language } from '../../../../../app/domain/language'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Subject, Subscription, debounceTime } from 'rxjs'; -import { Language } from '../../../../../app/domain/language'; import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService'; import { TeacherProjectService } from '../../../services/teacherProjectService'; -import { generateRandomKey } from '../../../common/string/string'; import { toObservable } from '@angular/core/rxjs-interop'; import { Translations } from '../../../../../app/domain/translations'; import { TranslationSuggestionsDialogComponent } from '../translation-suggestions-dialog/translation-suggestions-dialog.component'; -import { copy } from '../../../common/object/object'; @Directive() export abstract class AbstractTranslatableFieldComponent { @@ -29,6 +30,7 @@ export abstract class AbstractTranslatableFieldComponent { protected translationText: string; protected translationTextChanged: Subject = new Subject(); constructor( + protected configService: ConfigService, protected dialog: MatDialog, protected projectService: TeacherProjectService, protected projectTranslationService: TeacherProjectTranslationService @@ -79,6 +81,10 @@ export abstract class AbstractTranslatableFieldComponent { this.projectTranslationService.saveCurrentTranslations(currentTranslations).subscribe(); } + protected isTranslationServiceEnabled(): boolean { + return this.configService.getConfigParam('translationServiceEnabled'); + } + protected async translateText(): Promise { if (this.translationText) { this.openDialog(); @@ -91,7 +97,8 @@ export abstract class AbstractTranslatableFieldComponent { ) .subscribe({ next: (translation) => this.saveTranslationText(translation), - error: () => alert("AWS Translate settings have not been configured") + error: () => + alert($localize`There was an error translating the text. Please talk to WISE staff.`) }); } } diff --git a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts index 2ec78115985..390790affb1 100644 --- a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts @@ -1,4 +1,5 @@ import { Component, Input } from '@angular/core'; +import { ConfigService } from '../../../services/configService'; import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { MatTooltipModule } from '@angular/material/tooltip'; @@ -22,11 +23,12 @@ export class TranslatableAssetChooserComponent extends AbstractTranslatableField }; constructor( + protected configService: ConfigService, protected dialog: MatDialog, protected projectService: TeacherProjectService, protected projectTranslationService: TeacherProjectTranslationService ) { - super(dialog, projectService, projectTranslationService); + super(configService, dialog, projectService, projectTranslationService); } protected chooseAsset(): void { diff --git a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html index 933f7480d9d..33479f57295 100644 --- a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.html @@ -16,7 +16,7 @@ translate {{ defaultLanguage.language }}: {{ content[key] }} - @if (content[key]) { + @if (content[key] && isTranslationServiceEnabled()) {
auto_awesome Translate with AI
diff --git a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html index 893bf510acd..aed6f5d2ce6 100644 --- a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.html @@ -41,7 +41,7 @@ - @if (content[key]) { + @if (content[key] && isTranslationServiceEnabled()) {
auto_awesome Translate with AI diff --git a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts index ec4ddc300ba..ab0eb4690dc 100644 --- a/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts +++ b/src/assets/wise5/authoringTool/components/translatable-rich-text-editor/translatable-rich-text-editor.component.ts @@ -27,12 +27,12 @@ export class TranslatableRichTextEditorComponent extends AbstractTranslatableFie @ViewChild(MatTabGroup) private tabs: MatTabGroup; constructor( - private configService: ConfigService, + protected configService: ConfigService, protected dialog: MatDialog, protected projectService: TeacherProjectService, protected projectTranslationService: TeacherProjectTranslationService ) { - super(dialog, projectService, projectTranslationService); + super(configService, dialog, projectService, projectTranslationService); } ngOnChanges(): void { diff --git a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html index b8745a19def..d03ba1d7a03 100644 --- a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html +++ b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.html @@ -17,7 +17,7 @@ translate {{ defaultLanguage.language }}: {{ content[key] }}
- @if (content[key]) { + @if (content[key] && isTranslationServiceEnabled()) {
auto_awesome Translate with AI
diff --git a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts index f6822ba97ba..c56fa9e0027 100644 --- a/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts +++ b/src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts @@ -48,8 +48,9 @@ export class TranslationSuggestionsDialogComponent { this.data.defaultLanguageContent ) .subscribe({ - next: (suggestedTranslation) => this.translation = suggestedTranslation, - error: () => alert("AWS Translate settings have not been configured") + next: (suggestedTranslation) => (this.translation = suggestedTranslation), + error: () => + alert($localize`There was an error translating the text. Please talk to WISE staff.`) }); } From d686b721c8343e038dfc830fc4f13f829d8f6583 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Mon, 16 Mar 2026 13:55:57 -0400 Subject: [PATCH 11/13] Create i18n field in project JSON if undefined --- .../abstract-translatable-field.component.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts index bd6133dc3b1..e48c759ccd8 100644 --- a/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts +++ b/src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts @@ -76,6 +76,9 @@ export abstract class AbstractTranslatableFieldComponent { } protected saveTranslationText(text: string): void { + if (this.i18nId === undefined) { + this.createI18NField(); + } const currentTranslations = copy(this.projectTranslationService.currentTranslations()); currentTranslations[this.i18nId] = { value: text, modified: new Date().getTime() }; this.projectTranslationService.saveCurrentTranslations(currentTranslations).subscribe(); From 5b1dc309ff54d547e6a00e51a356524a40d610b6 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Mon, 16 Mar 2026 17:44:04 -0400 Subject: [PATCH 12/13] Fixed failing tests --- .../translatable-asset-chooser.component.spec.ts | 3 ++- .../translatable-input/translatable-input.component.spec.ts | 5 ++++- .../translatable-textarea.component.spec.ts | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.spec.ts b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.spec.ts index 56b20343fe7..8d3dd60f57c 100644 --- a/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.spec.ts +++ b/src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ConfigService } from '../../../services/configService'; import { TranslatableAssetChooserComponent } from './translatable-asset-chooser.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -21,7 +22,7 @@ describe('TranslatableAssetChooserComponent', () => { StudentTeacherCommonServicesModule, TranslatableAssetChooserComponent ], - providers: [TeacherProjectService, TeacherProjectTranslationService] + providers: [ConfigService, TeacherProjectService, TeacherProjectTranslationService] }); spyOn(TestBed.inject(TeacherProjectService), 'getLocale').and.returnValue( new ProjectLocale({ default: 'en-US' }) diff --git a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.spec.ts b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.spec.ts index de8a10a8ef8..913b95b8c7b 100644 --- a/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.spec.ts +++ b/src/assets/wise5/authoringTool/components/translatable-input/translatable-input.component.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ConfigService } from '../../../services/configService'; import { MockProviders } from 'ng-mocks'; import { ProjectLocale } from '../../../../../app/domain/projectLocale'; import { TeacherProjectService } from '../../../services/teacherProjectService'; @@ -12,7 +13,9 @@ describe('TranslatableInputComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [TranslatableInputComponent], - providers: [MockProviders(TeacherProjectService, TeacherProjectTranslationService)] + providers: [ + MockProviders(ConfigService, TeacherProjectService, TeacherProjectTranslationService) + ] }); const projectService = TestBed.inject(TeacherProjectService); spyOn(projectService, 'getLocale').and.returnValue(new ProjectLocale({ default: 'en-US' })); diff --git a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.spec.ts b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.spec.ts index 11aad65d97f..5aaf3f50f86 100644 --- a/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.spec.ts +++ b/src/assets/wise5/authoringTool/components/translatable-textarea/translatable-textarea.component.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ConfigService } from '../../../services/configService'; import { TranslatableTextareaComponent } from './translatable-textarea.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -19,7 +20,7 @@ describe('TranslatableTextareaComponent', () => { StudentTeacherCommonServicesModule, TranslatableTextareaComponent ], - providers: [TeacherProjectTranslationService, TeacherProjectService] + providers: [ConfigService, TeacherProjectTranslationService, TeacherProjectService] }); spyOn(TestBed.inject(TeacherProjectService), 'getLocale').and.returnValue( new ProjectLocale({ default: 'en-US' }) From 4de8e0bf4701937c64f381988ad8dea1c854c2e5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Mar 2026 21:49:07 +0000 Subject: [PATCH 13/13] Updated messages --- src/messages.xlf | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/messages.xlf b/src/messages.xlf index 26a59d6082f..22c8097f972 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -10840,6 +10840,17 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.5,9
+ + There was an error translating the text. Please talk to WISE staff. + + src/assets/wise5/authoringTool/components/abstract-translatable-field/abstract-translatable-field.component.ts + 104 + + + src/assets/wise5/authoringTool/components/translation-suggestions-dialog/translation-suggestions-dialog.component.ts + 53 + + Selected @@ -11020,7 +11031,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.Choose image src/assets/wise5/authoringTool/components/translatable-asset-chooser/translatable-asset-chooser.component.ts - 19 + 20