-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(chrome-ext): localization panel (#1513)
## Proposed change ![image](https://github.com/AmadeusITGroup/otter/assets/52541061/e04504ed-037a-460c-b3ee-8a204d4d1f38) ## Related issues - 🚀 Feature #1499 <!-- Please make sure to follow the contributing guidelines on https://github.com/amadeus-digital/Otter/blob/main/CONTRIBUTING.md -->
- Loading branch information
Showing
16 changed files
with
529 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
194 changes: 194 additions & 0 deletions
194
.../chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
import { AsyncPipe } from '@angular/common'; | ||
import { ChangeDetectionStrategy, Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core'; | ||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; | ||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; | ||
import type { | ||
GetTranslationValuesContentMessage, | ||
IsTranslationDeactivationEnabledContentMessage, | ||
LanguagesContentMessage, | ||
LocalizationMetadata, | ||
LocalizationsContentMessage, | ||
SwitchLanguageContentMessage | ||
} from '@o3r/localization'; | ||
import { combineLatest, Observable, Subscription } from 'rxjs'; | ||
import { filter, map, shareReplay, startWith, throttleTime } from 'rxjs/operators'; | ||
import { ChromeExtensionConnectionService } from '../../services/connection.service'; | ||
|
||
@Component({ | ||
selector: 'o3r-localization-panel-pres', | ||
templateUrl: './localization-panel-pres.template.html', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
encapsulation: ViewEncapsulation.None, | ||
standalone: true, | ||
imports: [ | ||
NgbAccordionModule, | ||
ReactiveFormsModule, | ||
FormsModule, | ||
AsyncPipe | ||
] | ||
}) | ||
export class LocalizationPanelPresComponent implements OnDestroy { | ||
public isTranslationDeactivationEnabled$: Observable<boolean>; | ||
public localizations$: Observable<LocalizationMetadata>; | ||
public filteredLocalizations$: Observable<LocalizationMetadata>; | ||
public languages$: Observable<string[]>; | ||
public form = new FormGroup({ | ||
search: new FormControl(''), | ||
lang: new FormControl(''), | ||
showKeys: new FormControl(false), | ||
translations: new UntypedFormGroup({}) | ||
}); | ||
|
||
private readonly connectionService = inject(ChromeExtensionConnectionService); | ||
|
||
private readonly subscription = new Subscription(); | ||
|
||
constructor() { | ||
this.connectionService.sendMessage( | ||
'requestMessages', | ||
{ | ||
only: [ | ||
'localizations', | ||
'languages', | ||
'switchLanguage', | ||
'isTranslationDeactivationEnabled' | ||
] | ||
} | ||
); | ||
this.languages$ = this.connectionService.message$.pipe( | ||
filter((message): message is LanguagesContentMessage => message.dataType === 'languages'), | ||
map((message) => message.languages), | ||
shareReplay({bufferSize: 1, refCount: true}) | ||
); | ||
this.localizations$ = this.connectionService.message$.pipe( | ||
filter((message): message is LocalizationsContentMessage => message.dataType === 'localizations'), | ||
map((message) => message.localizations.filter((localization) => !localization.dictionary)), | ||
shareReplay({bufferSize: 1, refCount: true}) | ||
); | ||
this.filteredLocalizations$ = combineLatest([ | ||
this.localizations$, | ||
this.form.controls.search.valueChanges.pipe( | ||
map((search) => search?.toLowerCase()), | ||
throttleTime(500), | ||
startWith('') | ||
) | ||
]).pipe( | ||
map(([localizations, search]) => search | ||
? localizations.filter(({ key, description, tags, ref }) => | ||
[key, description, ...(tags || []), ref].some((value) => value?.toLowerCase().includes(search)) | ||
) | ||
: localizations | ||
), | ||
startWith([]) | ||
); | ||
this.isTranslationDeactivationEnabled$ = this.connectionService.message$.pipe( | ||
filter((message): message is IsTranslationDeactivationEnabledContentMessage => message.dataType === 'isTranslationDeactivationEnabled'), | ||
map((message) => message.enabled), | ||
shareReplay({bufferSize: 1, refCount: true}) | ||
); | ||
const currLang$ = this.connectionService.message$.pipe( | ||
filter((message): message is SwitchLanguageContentMessage => message.dataType === 'switchLanguage'), | ||
map((message) => message.language), | ||
shareReplay({bufferSize: 1, refCount: true}) | ||
); | ||
this.subscription.add(currLang$.subscribe((lang) => { | ||
this.form.controls.lang.setValue(lang); | ||
this.connectionService.sendMessage('requestMessages', { | ||
only: ['getTranslationValuesContentMessage'] | ||
}); | ||
})); | ||
this.subscription.add( | ||
this.form.controls.lang.valueChanges.subscribe((language) => { | ||
if (language) { | ||
this.connectionService.sendMessage('switchLanguage', { language }); | ||
this.connectionService.sendMessage( | ||
'requestMessages', | ||
{ | ||
only: [ | ||
'getTranslationValuesContentMessage' | ||
] | ||
} | ||
); | ||
} | ||
}) | ||
); | ||
this.subscription.add( | ||
this.form.controls.showKeys.valueChanges.subscribe((value) => { | ||
this.connectionService.sendMessage('displayLocalizationKeys', { toggle: !!value }); | ||
}) | ||
); | ||
this.subscription.add( | ||
this.connectionService.message$.pipe( | ||
filter((message): message is GetTranslationValuesContentMessage => message.dataType === 'getTranslationValuesContentMessage'), | ||
map((message) => message.translations) | ||
).subscribe((translations) => { | ||
const translationControl = this.form.controls.translations; | ||
Object.entries(translations).forEach(([key, value]) => { | ||
const control = translationControl.controls[key]; | ||
if (!control) { | ||
const newControl = new FormControl<string>(value); | ||
translationControl.addControl(key, newControl); | ||
this.subscription.add( | ||
newControl.valueChanges.pipe( | ||
throttleTime(500) | ||
).subscribe((newValue) => this.onLocalizationChange(key, newValue ?? '')) | ||
); | ||
} else { | ||
control.setValue(value, { emitEvent: false }); | ||
} | ||
}); | ||
}) | ||
); | ||
this.subscription.add( | ||
this.isTranslationDeactivationEnabled$.subscribe((enabled) => { | ||
const control = this.form.controls.showKeys; | ||
if (enabled) { | ||
control.enable(); | ||
} else { | ||
control.disable(); | ||
} | ||
}) | ||
); | ||
this.subscription.add( | ||
this.languages$.subscribe((languages) => { | ||
const control = this.form.controls.lang; | ||
if (languages.length >= 2) { | ||
control.enable(); | ||
} else { | ||
control.disable(); | ||
} | ||
}) | ||
); | ||
} | ||
|
||
/** | ||
* Change localization key value | ||
* @param localizationKey | ||
* @param newValue | ||
*/ | ||
private onLocalizationChange(localizationKey: string, newValue: string) { | ||
this.connectionService.sendMessage('updateLocalization', { | ||
key: localizationKey, | ||
value: newValue | ||
}); | ||
} | ||
|
||
/** | ||
* Reset localization change for current language | ||
*/ | ||
public resetChange() { | ||
this.connectionService.sendMessage('reloadLocalizationKeys', {}); | ||
this.connectionService.sendMessage( | ||
'requestMessages', | ||
{ | ||
only: [ | ||
'getTranslationValuesContentMessage' | ||
] | ||
} | ||
); | ||
} | ||
|
||
public ngOnDestroy() { | ||
this.subscription.unsubscribe(); | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
...chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.template.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<form class="mb-2" [formGroup]="form"> | ||
<div class="mb-2 d-flex align-items-center gap-4"> | ||
@if (languages$ | async; as languages) { | ||
<div class="input-group" [style.maxWidth]="'300px'"> | ||
<label class="input-group-text" for="language">Language</label> | ||
<select class="form-select" id="language" formControlName="lang" [attr.aria-describedby]="languages.length < 2 ? 'language-hint' : null"> | ||
@for (lang of languages; track lang) { | ||
<option [value]="lang">{{ lang }}</option> | ||
} | ||
</select> | ||
@if (languages.length < 2) { | ||
<div id="language-hint" class="form-text text-warning w-100"> | ||
You have only one language. | ||
</div> | ||
} | ||
</div> | ||
} | ||
<div class="form-check d-flex flex-column gap-2"> | ||
<div> | ||
<input class="form-check-input" type="checkbox" formControlName="showKeys" id="displayLocalizationKey" [attr.aria-describedby]="(isTranslationDeactivationEnabled$ | async) ? null : 'show-keys-hint'"> | ||
<label class="form-check-label" for="displayLocalizationKey"> | ||
Display localization keys | ||
</label> | ||
</div> | ||
@if ((isTranslationDeactivationEnabled$ | async) === false) { | ||
<div id="show-keys-hint" class="text-warning" [style.fontSize]="'.5em'" [style.marginLeft]="'-2.5em'"> | ||
Translation deactivation is not enabled. Please set the LocalizationConfiguration property "enableTranslationDeactivation" accordingly. | ||
</div> | ||
} | ||
</div> | ||
@if ((localizations$ | async)?.length) { | ||
<button (click)="resetChange()" type="button" class="btn btn-outline-danger"> | ||
Reset change for {{ form.controls.lang.value }} | ||
</button> | ||
} | ||
</div> | ||
@if ((localizations$ | async)?.length) { | ||
<div class="mb-2 input-group"> | ||
<label class="input-group-text" for="search-localization"> | ||
<i class="mx-1 icon-search" aria-label="Search"></i> | ||
</label> | ||
<input class="form-control" formControlName="search" type="text" id="search-localization" placeholder="Search for localization" /> | ||
</div> | ||
} | ||
</form> | ||
@if ((localizations$ | async)?.length) { | ||
@if (filteredLocalizations$ | async; as filteredLocalizations) { | ||
@if (filteredLocalizations.length) { | ||
<form [formGroup]="form.controls.translations"> | ||
<div ngbAccordion [closeOthers]="true" #acc="ngbAccordion"> | ||
@for (localization of filteredLocalizations; track localization.key) { | ||
<div ngbAccordionItem> | ||
<h3 ngbAccordionHeader> | ||
<button ngbAccordionButton>{{localization.key}}</button> | ||
</h3> | ||
<div ngbAccordionCollapse> | ||
<div ngbAccordionBody> | ||
<ng-template> | ||
<div class="form-group"> | ||
<input [attr.aria-describedby]="localization.key" class="form-control" type="text" [formControlName]="localization.key" /> | ||
<div [id]="localization.key" class="form-text">{{localization.description}}</div> | ||
</div> | ||
</ng-template> | ||
</div> | ||
</div> | ||
</div> | ||
} | ||
</div> | ||
</form> | ||
} @else { | ||
<h3>No localization found for your search.</h3> | ||
} | ||
} | ||
} @else { | ||
<h3>No metadata provided for localization.</h3> | ||
<p> | ||
To provide metadata you can read the following | ||
<a href="https://github.com/AmadeusITGroup/otter/blob/main/docs/dev-tools/chrome-devtools.md#how-to-enable-more-features-by-providing-metadata-files" target="_blank" rel="noopener"> | ||
documentation | ||
</a> | ||
</p> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.