Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular UI: External Localization Infrastructure Changes #14561

Merged
merged 8 commits into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,798 changes: 2,578 additions & 1,220 deletions npm/ng-packs/packages/core/src/lib/proxy/generate-proxy.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/mul
})
export class AbpTenantService {
apiName = 'abp';

findTenantById = (id: string) =>
this.restService.request<any, FindTenantResultDto>({
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-id/${id}`,
},
{ apiName: this.apiName });

{ apiName: this.apiName });
findTenantByName = (name: string) =>
this.restService.request<any, FindTenantResultDto>({
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-name/${name}`,
},
{ apiName: this.apiName });
{ apiName: this.apiName });

constructor(private restService: RestService) { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import type { ApplicationApiDescriptionModel, ApplicationApiDescriptionModelRequ
})
export class AbpApiDefinitionService {
apiName = 'abp';

getByModel = (model: ApplicationApiDescriptionModelRequestDto) =>
this.restService.request<any, ApplicationApiDescriptionModel>({
method: 'GET',
url: '/api/abp/api-definition',
params: { includeTypes: model.includeTypes },
},
{ apiName: this.apiName });
{ apiName: this.apiName });

constructor(private restService: RestService) { }
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ApplicationConfigurationDto } from './models';
import type { ApplicationConfigurationDto, ApplicationConfigurationRequestOptions } from './models';
import { RestService } from '../../../../../../services/rest.service';
import { Injectable } from '@angular/core';

Expand All @@ -7,13 +7,14 @@ import { Injectable } from '@angular/core';
})
export class AbpApplicationConfigurationService {
apiName = 'abp';

get = () =>
get = (options: ApplicationConfigurationRequestOptions) =>
this.restService.request<any, ApplicationConfigurationDto>({
method: 'GET',
url: '/api/abp/application-configuration',
params: { includeLocalizationResources: options.includeLocalizationResources },
},
{ apiName: this.apiName });
{ apiName: this.apiName });

constructor(private restService: RestService) { }
constructor(private restService: RestService) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { ApplicationLocalizationDto, ApplicationLocalizationRequestDto } from './models';
import { Injectable } from '@angular/core';
import { RestService } from '../../../../../../services/rest.service';

@Injectable({
providedIn: 'root',
})
export class AbpApplicationLocalizationService {
apiName = 'abp';

get = (input: ApplicationLocalizationRequestDto) =>
this.restService.request<any, ApplicationLocalizationDto>({
method: 'GET',
url: '/api/abp/application-localization',
params: { cultureName: input.cultureName, onlyDynamics: input.onlyDynamics },
},
{ apiName: this.apiName });

constructor(private restService: RestService) {}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as ObjectExtending from './object-extending';
export * from './abp-application-configuration.service';
export * from './abp-application-localization.service';
export * from './models';
export { ObjectExtending };
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { LanguageInfo } from '../../../localization/models';
import type { NameValue } from '../../../models';

export interface ApplicationAuthConfigurationDto {
policies: Record<string, boolean>;
grantedPolicies: Record<string, boolean>;
}

Expand All @@ -20,6 +19,11 @@ export interface ApplicationConfigurationDto {
timing: TimingDto;
clock: ClockDto;
objectExtensions: ObjectExtensionsDto;
extraProperties: Record<string, object>;
}

export interface ApplicationConfigurationRequestOptions {
includeLocalizationResources: boolean;
}

export interface ApplicationFeatureConfigurationDto {
Expand All @@ -32,13 +36,28 @@ export interface ApplicationGlobalFeatureConfigurationDto {

export interface ApplicationLocalizationConfigurationDto {
values: Record<string, Record<string, string>>;
resources: Record<string, ApplicationLocalizationResourceDto>;
languages: LanguageInfo[];
currentCulture: CurrentCultureDto;
defaultResourceName?: string;
languagesMap: Record<string, NameValue[]>;
languageFilesMap: Record<string, NameValue[]>;
}

export interface ApplicationLocalizationDto {
resources: Record<string, ApplicationLocalizationResourceDto>;
}

export interface ApplicationLocalizationRequestDto {
cultureName: string;
onlyDynamics: boolean;
}

export interface ApplicationLocalizationResourceDto {
texts: Record<string, string>;
baseResources: string[];
}

export interface ApplicationSettingConfigurationDto {
values: Record<string, string>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ControllerApiDescriptionModel {
controllerName?: string;
controllerGroupName?: string;
isRemoteService: boolean;
integrationService: boolean;
apiVersion?: string;
type?: string;
interfaces: ControllerInterfaceApiDescriptionModel[];
Expand All @@ -33,6 +34,14 @@ export interface ControllerApiDescriptionModel {

export interface ControllerInterfaceApiDescriptionModel {
type?: string;
name?: string;
methods: InterfaceMethodApiDescriptionModel[];
}

export interface InterfaceMethodApiDescriptionModel {
name?: string;
parametersOnMethod: MethodParameterApiDescriptionModel[];
returnValue: ReturnValueApiDescriptionModel;
}

export interface MethodParameterApiDescriptionModel {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Observable, Subject, of } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service';
import { AbpApplicationLocalizationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service';
import {
ApplicationConfigurationDto,
ApplicationGlobalFeatureConfigurationDto,
Expand All @@ -13,23 +14,46 @@ import { InternalStore } from '../utils/internal-store-utils';
})
export class ConfigStateService {
private readonly store = new InternalStore({} as ApplicationConfigurationDto);
private includeLocalizationResources = false;

get createOnUpdateStream() {
return this.store.sliceUpdate;
}

private updateSubject = new Subject<void>();

constructor(private abpConfigService: AbpApplicationConfigurationService) {
constructor(
private abpConfigService: AbpApplicationConfigurationService,
private abpApplicationLocalizationService: AbpApplicationLocalizationService,
) {
this.initUpdateStream();
}

private initUpdateStream() {
this.updateSubject
.pipe(switchMap(() => this.abpConfigService.get()))
.pipe(
switchMap(() =>
this.abpConfigService.get({
includeLocalizationResources: this.includeLocalizationResources,
}),
),
)
.pipe(switchMap(appState => this.getLocalizationAndCombineWithAppState(appState)))
.subscribe(res => this.store.set(res));
}

private getLocalizationAndCombineWithAppState(
appState: ApplicationConfigurationDto,
): Observable<ApplicationConfigurationDto> {
return this.abpApplicationLocalizationService
.get({ cultureName: appState.localization.currentCulture.name, onlyDynamics: false })
.pipe(
switchMap(result =>
of({ ...appState, localization: { ...appState.localization, ...result } }),
),
);
}

refreshAppState() {
this.updateSubject.next();
return this.createOnUpdateStream(state => state).pipe(take(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { BehaviorSubject, combineLatest, from, Observable, Subject } from 'rxjs'
import { filter, map, mapTo, switchMap } from 'rxjs/operators';
import { ABP } from '../models/common';
import { LocalizationWithDefault } from '../models/localization';
import { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models';
import {
ApplicationConfigurationDto,
ApplicationLocalizationResourceDto,
} from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models';
import { localizations$ } from '../tokens/localization.token';
import { CORE_OPTIONS } from '../tokens/options.token';
import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils';
Expand Down Expand Up @@ -58,18 +61,27 @@ export class LocalizationService {
private initLocalizationValues() {
localizations$.subscribe(val => this.addLocalization(val));

const remoteLocalizations$ = this.configState.getDeep$('localization.values') as Observable<
const legacyResources$ = this.configState.getDeep$('localization.values') as Observable<
Record<string, Record<string, string>>
>;

const remoteLocalizations$ = this.configState.getDeep$('localization.resources') as Observable<
Record<string, ApplicationLocalizationResourceDto>
>;

const currentLanguage$ = this.sessionState.getLanguage$();

const uiLocalizations$ = combineLatest([currentLanguage$, this.uiLocalizations$]).pipe(
map(([currentLang, localizations]) => localizations.get(currentLang)),
);

combineLatest([remoteLocalizations$, uiLocalizations$])
combineLatest([legacyResources$, remoteLocalizations$, uiLocalizations$])
.pipe(
map(([remote, local]) => {
map(([legacy, resource, local]) => {
if (!resource) {
return;
}
const remote = combineLegacyandNewResources(legacy || {}, resource);
if (remote) {
if (!local) {
local = new Map();
Expand Down Expand Up @@ -250,3 +262,39 @@ export class LocalizationService {
return localization || defaultValue || (key as string);
}
}

function recursivelyMergeBaseResources(baseResourceName: string, source: ResourceDto) {
const item = source[baseResourceName];

if (item.baseResources.length === 0) {
return item;
}

return item.baseResources.reduce((acc, baseResource) => {
const baseItem = recursivelyMergeBaseResources(baseResource, source);
const texts = { ...baseItem.texts, ...item.texts };
return { ...acc, texts };
}, item);
}

function mergeResourcesWithBaseResource(resource: ResourceDto): ResourceDto {
const entities = Object.keys(resource).map(key => {
const newValue = recursivelyMergeBaseResources(key, resource);
return [key, newValue];
});
return entities.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
}

function combineLegacyandNewResources(
legacy: LegacyLanguageDto,
resource: ResourceDto,
): LegacyLanguageDto {
const mergedResource = mergeResourcesWithBaseResource(resource);

return Object.entries(mergedResource).reduce((acc, [key, value]) => {
return { ...acc, [key]: value.texts };
}, legacy);
}

type LegacyLanguageDto = Record<string, Record<string, string>>;
type ResourceDto = Record<string, ApplicationLocalizationResourceDto>;