Skip to content

Commit ea3a62d

Browse files
Merge pull request #24731 from abpframework/feat/#24675-hybrid-localization
Angular - Hybrid localization support
2 parents ddb90af + 6a364d9 commit ea3a62d

File tree

13 files changed

+404
-4
lines changed

13 files changed

+404
-4
lines changed

npm/ng-packs/apps/dev-app/src/app/app.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export const appConfig: ApplicationConfig = {
3131
registerLocaleFn: registerLocaleForEsBuild(),
3232
sendNullsAsQueryParam: false,
3333
skipGetAppConfiguration: false,
34+
uiLocalization: {
35+
enabled: true,
36+
basePath: '/assets/localization',
37+
},
3438
}),
3539
),
3640
provideAbpOAuth(),

npm/ng-packs/apps/dev-app/src/app/app.routes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export const appRoutes: Routes = [
1010
path: 'dynamic-form',
1111
loadComponent: () => import('./dynamic-form-page/dynamic-form-page.component').then(m => m.DynamicFormPageComponent),
1212
},
13+
{
14+
path: 'localization-test',
15+
loadComponent: () => import('./localization-test/localization-test.component').then(m => m.LocalizationTestComponent),
16+
},
1317
{
1418
path: 'account',
1519
loadChildren: () => import('@abp/ng.account').then(m => m.createRoutes()),

npm/ng-packs/apps/dev-app/src/app/home/home.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<div class="container">
22
<div class="text-center mb-4">
33
<a routerLink="/dynamic-form" class="btn btn-primary">Go to Dynamic Form</a>
4+
<a routerLink="/localization-test" class="btn btn-secondary ms-2">Test Hybrid Localization</a>
45
</div>
56
<div class="p-5 text-center">
67
<div class="d-inline-block bg-success text-white p-1 h5 rounded mb-4" role="alert">
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Component, inject, OnInit } from '@angular/core';
2+
import { LocalizationPipe, UILocalizationService, SessionStateService } from '@abp/ng.core';
3+
import { CommonModule } from '@angular/common';
4+
import { CardComponent, CardBodyComponent } from '@abp/ng.theme.shared';
5+
import { AsyncPipe } from '@angular/common';
6+
7+
@Component({
8+
selector: 'app-localization-test',
9+
standalone: true,
10+
imports: [CommonModule, LocalizationPipe, CardComponent, CardBodyComponent, AsyncPipe],
11+
template: `
12+
<div class="container mt-5">
13+
<h2>Hybrid Localization Test</h2>
14+
15+
<abp-card cardClass="mt-4">
16+
<abp-card-body>
17+
<h5>Backend Localization (if available)</h5>
18+
<p><strong>MyProjectName::Welcome:</strong> {{ 'MyProjectName::Welcome' | abpLocalization }}</p>
19+
<p><strong>AbpAccount::Login:</strong> {{ 'AbpAccount::Login' | abpLocalization }}</p>
20+
</abp-card-body>
21+
</abp-card>
22+
23+
<abp-card cardClass="mt-4">
24+
<abp-card-body>
25+
<h5>UI Localization (from /assets/localization/{{ currentLanguage$ | async }}.json)</h5>
26+
<p><strong>MyProjectName::CustomKey:</strong> {{ 'MyProjectName::CustomKey' | abpLocalization }}</p>
27+
<p><strong>MyProjectName::TestMessage:</strong> {{ 'MyProjectName::TestMessage' | abpLocalization }}</p>
28+
</abp-card-body>
29+
</abp-card>
30+
31+
<abp-card cardClass="mt-4">
32+
<abp-card-body>
33+
<h5>UI Override (UI > Backend Priority)</h5>
34+
<p><strong>AbpAccount::Login:</strong> {{ 'AbpAccount::Login' | abpLocalization }}</p>
35+
<p class="text-muted">If backend has "Login", UI version should override it</p>
36+
</abp-card-body>
37+
</abp-card>
38+
39+
<abp-card cardClass="mt-4">
40+
<abp-card-body>
41+
<h5>Loaded UI Localizations</h5>
42+
<pre>{{ loadedLocalizations | json }}</pre>
43+
</abp-card-body>
44+
</abp-card>
45+
</div>
46+
`,
47+
})
48+
export class LocalizationTestComponent implements OnInit {
49+
private uiLocalizationService = inject(UILocalizationService);
50+
private sessionState = inject(SessionStateService);
51+
52+
loadedLocalizations: any = {};
53+
currentLanguage$ = this.sessionState.getLanguage$();
54+
55+
ngOnInit() {
56+
this.loadedLocalizations = this.uiLocalizationService.getLoadedLocalizations();
57+
}
58+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"MyProjectName": {
3+
"Welcome": "Welcome from UI (en.json)",
4+
"CustomKey": "This is a UI-only localization",
5+
"TestMessage": "UI localization is working!"
6+
},
7+
"AbpAccount": {
8+
"Login": "Sign In (UI Override)"
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"MyProjectName": {
3+
"Welcome": "UI'dan Hoş Geldiniz (tr.json)",
4+
"CustomKey": "Bu sadece UI'da olan bir localization",
5+
"TestMessage": "UI localization çalışıyor!"
6+
},
7+
"AbpAccount": {
8+
"Login": "Giriş Yap (UI Override)"
9+
}
10+
}

npm/ng-packs/packages/core/src/lib/models/common.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ export namespace ABP {
1616
othersGroup?: string;
1717
dynamicLayouts?: Map<string, string>;
1818
disableProjectNameInTitle?: boolean;
19+
uiLocalization?: UILocalizationOptions;
20+
}
21+
22+
export interface UILocalizationOptions {
23+
/**
24+
* Enable UI localization feature
25+
* When enabled, localization files are automatically loaded based on selected language
26+
* Files should be located at: {basePath}/{culture}.json
27+
* Example: /assets/localization/en.json
28+
* JSON format: { "ResourceName": { "Key": "Value" } }
29+
* Merges with backend localizations (UI > Backend priority)
30+
*/
31+
enabled?: boolean;
32+
/**
33+
* Base path for localization JSON files
34+
* Default: '/assets/localization'
35+
* Files should be located at: {basePath}/{culture}.json
36+
* Example: /assets/localization/en.json
37+
*/
38+
basePath?: string;
1939
}
2040

2141
export interface Child {

npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ import { RoutesHandler } from '../handlers';
2323
import { ABP, SortableItem } from '../models';
2424
import { AuthErrorFilterService } from '../abstracts';
2525
import { DEFAULT_DYNAMIC_LAYOUTS } from '../constants';
26-
import { LocalizationService, LocalStorageListenerService, AbpTitleStrategy } from '../services';
26+
import {
27+
LocalizationService,
28+
LocalStorageListenerService,
29+
AbpTitleStrategy,
30+
UILocalizationService,
31+
} from '../services';
2732
import { DefaultQueueManager, getInitialData } from '../utils';
2833
import { CookieLanguageProvider, IncludeLocalizationResourcesProvider, LocaleProvider } from './';
2934
import { timezoneInterceptor, transferStateInterceptor } from '../interceptors';
@@ -113,6 +118,11 @@ export function provideAbpCore(...features: CoreFeature<CoreFeatureKind>[]) {
113118
inject(LocalizationService);
114119
inject(LocalStorageListenerService);
115120
inject(RoutesHandler);
121+
// Initialize UILocalizationService if UI-only mode is enabled
122+
const options = inject(CORE_OPTIONS);
123+
if (options?.uiLocalization?.enabled) {
124+
inject(UILocalizationService);
125+
}
116126
await getInitialData();
117127
}),
118128
LocaleProvider,

npm/ng-packs/packages/core/src/lib/proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { Injectable, inject } from '@angular/core';
66
@Injectable({
77
providedIn: 'root',
88
})
9-
export class AbpApplicationConfigurationService {
10-
private restService = inject(RestService);
9+
export class AbpApplicationConfigurationService {
10+
private restService = inject(RestService);
1111

1212
apiName = 'abp';
1313

npm/ng-packs/packages/core/src/lib/services/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './http-wait.service';
88
export * from './lazy-load.service';
99
export * from './list.service';
1010
export * from './localization.service';
11+
export * from './ui-localization.service';
1112
export * from './multi-tenancy.service';
1213
export * from './permission.service';
1314
export * from './replaceable-components.service';

0 commit comments

Comments
 (0)