fix(i18n): route dirty-guard and html-editor strings through TranslateService
Some checks failed
Architecture Governance / architecture (push) Has been cancelled

window.confirm/window.prompt calls in projectEditorDirtyGuard and
MarketplaceHtmlEditorComponent, and the hardcoded Preview/Code toggle
label in its template, bypassed the app's translation pipeline. Add
builder.confirmLeaveUnsaved, promptLinkUrl, promptImageUrl,
htmlEditorCode and htmlEditorPreview keys (en/ru/hy), resolve them via
TranslateService.t() before passing to confirm/prompt, and use
TranslatePipe for the toggle button label.
This commit is contained in:
sdarbinyan
2026-07-13 15:57:54 +04:00
parent 68442919d3
commit a7df1980ed
7 changed files with 32 additions and 6 deletions

View File

@@ -3,7 +3,7 @@
@for (item of toolbar; track item.id) { @for (item of toolbar; track item.id) {
<button type="button" (click)="runCommand(item)">{{ item.label }}</button> <button type="button" (click)="runCommand(item)">{{ item.label }}</button>
} }
<button type="button" (click)="toggleCode()">{{ showCode() ? 'Preview' : 'Code' }}</button> <button type="button" (click)="toggleCode()">{{ (showCode() ? 'builder.htmlEditorPreview' : 'builder.htmlEditorCode') | translate }}</button>
</div> </div>
<textarea <textarea

View File

@@ -1,4 +1,6 @@
import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, signal } from '@angular/core'; import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, inject, signal } from '@angular/core';
import { TranslateService } from '../../../../i18n/translate.service';
import { TranslatePipe } from '../../../../i18n/translate.pipe';
export interface HtmlEditorToolbarCommand { export interface HtmlEditorToolbarCommand {
id: string; id: string;
@@ -23,12 +25,14 @@ export const HTML_EDITOR_TOOLBAR: HtmlEditorToolbarCommand[] = [
@Component({ @Component({
selector: 'app-marketplace-html-editor', selector: 'app-marketplace-html-editor',
standalone: true, standalone: true,
imports: [], imports: [TranslatePipe],
templateUrl: './marketplace-html-editor.component.html', templateUrl: './marketplace-html-editor.component.html',
styleUrls: ['./marketplace-html-editor.component.scss'], styleUrls: ['./marketplace-html-editor.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class MarketplaceHtmlEditorComponent implements OnChanges, AfterViewInit { export class MarketplaceHtmlEditorComponent implements OnChanges, AfterViewInit {
private readonly translate = inject(TranslateService);
@Input() html = ''; @Input() html = '';
@Output() htmlChange = new EventEmitter<string>(); @Output() htmlChange = new EventEmitter<string>();
@ViewChild('surface', { static: true }) surface!: ElementRef<HTMLDivElement>; @ViewChild('surface', { static: true }) surface!: ElementRef<HTMLDivElement>;
@@ -58,13 +62,13 @@ export class MarketplaceHtmlEditorComponent implements OnChanges, AfterViewInit
runCommand(item: HtmlEditorToolbarCommand): void { runCommand(item: HtmlEditorToolbarCommand): void {
this.surface.nativeElement.focus(); this.surface.nativeElement.focus();
if (item.command === 'createLink') { if (item.command === 'createLink') {
const url = window.prompt('URL'); const url = window.prompt(this.translate.t('builder.promptLinkUrl'));
if (!url) { if (!url) {
return; return;
} }
document.execCommand('createLink', false, url); document.execCommand('createLink', false, url);
} else if (item.command === 'insertImage') { } else if (item.command === 'insertImage') {
const url = window.prompt('Image URL'); const url = window.prompt(this.translate.t('builder.promptImageUrl'));
if (!url) { if (!url) {
return; return;
} }

View File

@@ -2,11 +2,13 @@ import { inject } from '@angular/core';
import { CanDeactivateFn } from '@angular/router'; import { CanDeactivateFn } from '@angular/router';
import { ProjectEditorFacade } from '../facade/project-editor.facade'; import { ProjectEditorFacade } from '../facade/project-editor.facade';
import { ProjectEditorPageComponent } from '../pages/project-editor-page.component'; import { ProjectEditorPageComponent } from '../pages/project-editor-page.component';
import { TranslateService } from '../../../i18n/translate.service';
export const projectEditorDirtyGuard: CanDeactivateFn<ProjectEditorPageComponent> = () => { export const projectEditorDirtyGuard: CanDeactivateFn<ProjectEditorPageComponent> = () => {
const facade = inject(ProjectEditorFacade); const facade = inject(ProjectEditorFacade);
if (!facade.dirty()) { if (!facade.dirty()) {
return true; return true;
} }
return window.confirm('You have unsaved changes. Leave anyway?'); const translate = inject(TranslateService);
return window.confirm(translate.t('builder.confirmLeaveUnsaved'));
}; };

View File

@@ -517,6 +517,11 @@ export const en: Translations = {
unsavedChanges: 'Unsaved changes', unsavedChanges: 'Unsaved changes',
save: 'Save', save: 'Save',
publish: 'Publish', publish: 'Publish',
confirmLeaveUnsaved: 'You have unsaved changes. Leave anyway?',
promptLinkUrl: 'URL',
promptImageUrl: 'Image URL',
htmlEditorCode: 'Code',
htmlEditorPreview: 'Preview',
}, },
staticPages: { staticPages: {
notFound: 'Page not found', notFound: 'Page not found',

View File

@@ -517,6 +517,11 @@ export const hy: Translations = {
unsavedChanges: 'Չպահված փոփոխություններ', unsavedChanges: 'Չպահված փոփոխություններ',
save: 'Պահպանել', save: 'Պահպանել',
publish: 'Հրապարակել', publish: 'Հրապարակել',
confirmLeaveUnsaved: 'Կան չպահված փոփոխություններ։ Այնուամենայնիվ դուրս գա՞լ։',
promptLinkUrl: 'URL',
promptImageUrl: 'Նկարի URL',
htmlEditorCode: 'Կոդ',
htmlEditorPreview: 'Նախադիտում',
}, },
staticPages: { staticPages: {
notFound: 'Էջը չի գտնվել', notFound: 'Էջը չի գտնվել',

View File

@@ -517,6 +517,11 @@ export const ru: Translations = {
unsavedChanges: 'Есть несохранённые изменения', unsavedChanges: 'Есть несохранённые изменения',
save: 'Сохранить', save: 'Сохранить',
publish: 'Опубликовать', publish: 'Опубликовать',
confirmLeaveUnsaved: 'Есть несохранённые изменения. Всё равно уйти?',
promptLinkUrl: 'URL',
promptImageUrl: 'URL изображения',
htmlEditorCode: 'Код',
htmlEditorPreview: 'Предпросмотр',
}, },
staticPages: { staticPages: {
notFound: 'Страница не найдена', notFound: 'Страница не найдена',

View File

@@ -515,6 +515,11 @@ export interface Translations {
unsavedChanges: string; unsavedChanges: string;
save: string; save: string;
publish: string; publish: string;
confirmLeaveUnsaved: string;
promptLinkUrl: string;
promptImageUrl: string;
htmlEditorCode: string;
htmlEditorPreview: string;
}; };
staticPages: { staticPages: {
notFound: string; notFound: string;