@bespunky/angular-zen
Version:
The Angular tools you always wished were there.
796 lines (781 loc) • 37 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Optional, Inject, NgModule, SkipSelf, Directive } from '@angular/core';
import { Destroyable, CoreModule } from '@bespunky/angular-zen/core';
import * as i1 from '@bespunky/angular-zen/router-x';
import { UrlReflectionService, RouteAware } from '@bespunky/angular-zen/router-x';
import { from, of } from 'rxjs';
import { access } from '@bespunky/angular-zen/utils';
import * as i2 from '@angular/router';
/**
* An injection token representing the the global language integration configuration provided by an app to its libraries.
* Provided by `LanguageIntegrationModule.forRoot()`.
*/
const LanguageIntegration = new InjectionToken('LanguageIntegration.Config');
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/**
* Uses the language integration configuration provided by an app to provide language services for a library.
* @see `LanguageIntegrationModule.forRoot()`.
*
* @export
* @class LanguageIntegrationService
* @extends {Destroyable}
*/
class LanguageIntegrationService extends Destroyable {
/**
* Creates an instance of LanguageIntegrationService.
*
* @param {LanguageIntegrationConfig} [config] The language integration configuration provided using `LanguageIntegrationModule.forRoot()`.
*/
constructor(config) {
super();
this.config = config;
this.initReadyObservable();
if (config)
this.initMultiLanguageSupport();
}
initReadyObservable() {
const ready = this.config?.ready;
// Using of() instead of EMPTY as EMPTY only calls `complete` but not `next`.
// This allows users to subscribe more intuitively.
this.$ready = ready ? from(ready) : of();
}
initMultiLanguageSupport() {
this.subscribe(this.config.changed, lang => this.currentLang = lang);
// User's responsability to provide a completing observables.
this.loadDefaultLanguage().subscribe(defaultLang => this.defaultLang = defaultLang);
this.loadSupportedLanguages().subscribe(languages => this.supportedLangs = languages);
}
loadDefaultLanguage() {
const defaultLang = this.config.default;
return typeof defaultLang === 'string' ? of(defaultLang) : from(defaultLang());
}
loadSupportedLanguages() {
const supported = this.config.supported;
return Array.isArray(supported) ? of(supported) : from(supported());
}
/**
* A subscribable event emitting every time the integrated app changes a language.
* The new language name is emitted with each change.
*
* This will be `undefined` if the language integration module hasn't been imported by the app.
*
* @readonly
* @type {Observable<string> | undefined}
*/
get changed() {
return this.config?.changed;
}
/**
* The default language used by the integrated app.
*
* This will be `undefined` in the following cases:
* 1. The language integration module hasn't been imported by the app.
* 2. The default language hasn't resolved yet.
*
* @readonly
* @type {string | undefined}
*/
get default() {
return this.defaultLang;
}
/**
* The languages supported by the integrated app.
*
* This will be `undefined` in the following cases:
* 1. The language integration module hasn't been imported by the app.
* 2. Supported languages haven't resolved yet.
*
* @readonly
* @type {string[] | undefined}
*/
get supported() {
return this.supportedLangs;
}
/**
* The current language used by the integrated app.
*
* This will be `undefined` in the following cases:
* 1. The language integration module hasn't been imported by the app.
* 2. The `changed` event hasn't emitted yet.
*
* @readonly
* @type {string | undefined}
*/
get current() {
return this.currentLang;
}
/**
* Indicated whether the language integration module has been imported into the app.
* If this is `false`, this service will serve no purpose.
*
* @readonly
* @type {boolean}
*/
get enabled() {
return !!(this.config);
}
/**
* A resolvable async object which emits once when the intgrated language services are ready for operation.
*
* This will complete immediately if the the language integration module hasn't been imported, or a `ready` observable hasn't
* been provided when importing the module.
*
* @readonly
* @type {Observable<void>}
*/
get ready() {
return this.$ready;
}
/**
* Retrieves the list of alternative languages to the specified language supported by the integrated app.
*
* @param {string} lang The language for which to get the alternative languages.
* @returns {string[]} An array of alternative languages supported by the integrated app.
* @throws If the language integration module hasn't been imported into the app.
*/
alternateLanguagesFor(lang) {
this.ensureEnabled();
return this.supported.filter(supportedLocale => supportedLocale !== lang);
}
/**
* Translates a value (typically a translation id) into the current language used by the integrated app.
*
* @param {string} value The value to translate (typically a translation id).
* @param {Record<string, unknown>} [params] (Optional) Any params needed for translating the value.
* @returns {string} The translation of the specified value and params in the current language used by the integrated app.
* @throws If the language integration module hasn't been imported into the app.
*/
translate(value, params) {
this.ensureEnabled();
return this.config.translate(value, params);
}
/**
* Dives deep into an object or an array and replaces the indicated properties in-place with their translation.
*
* The `paths` argument is an array of paths representing deep properties which should be translated.
* For example:
*
* ```typescript
* // If we have a user object, we can translate its city and role properties
* {
* id: 15,
* addresses: [
* { city: 'Tel Aviv', ... },
* { city: 'Rishon LeTzion' }
* ],
* system: {
* role: 'Admin'
* }
* }
*
* // Our paths would be:
* `addresses[0].city`
* `addresses[1].city`
* `system.role`
* ```
*
* @param {Record<string, unknown>} data The object which holds the translatable properties. Can be a deeply nested object.
* @param {string[]} paths The paths of the translatable properties to translate and replace.
* @throws If the language integration module hasn't been imported into the app.
*/
translateProperties(data, paths) {
this.ensureEnabled();
paths.forEach(path => {
const valueAccessor = access(data, path);
const value = valueAccessor.get();
if (typeof value !== 'string')
return;
valueAccessor.set(this.translate(value));
});
}
/**
* Ensures that the language integration module has been imported and a configuration object has been provided.
*
* @throws {Error} The language integration module hasn't been imported by the app.
*/
ensureEnabled() {
if (this.enabled)
return true;
throw new Error(`
Language integration hasn't been enabled.
Did you import the language integration module in your app module using 'LanguageIntegrationModule.forRoot()'?
`);
}
}
LanguageIntegrationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationService, deps: [{ token: LanguageIntegration, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
LanguageIntegrationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [LanguageIntegration]
}] }]; } });
/**
* The base class for url localization implementors. This can be used as an injectable token to get a hold of the currently
* configured url localizer class.
*
* @export
* @abstract
* @class UrlLocalizer
*/
class UrlLocalizer {
/**
* Creates an instance of UrlLocalizer.
*
* @param {UrlReflectionService} urlReflection The url reflection service.
*/
constructor(urlReflection) {
this.urlReflection = urlReflection;
}
}
UrlLocalizer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlLocalizer, deps: [{ token: i1.UrlReflectionService }], target: i0.ɵɵFactoryTarget.Injectable });
UrlLocalizer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlLocalizer });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlLocalizer, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i1.UrlReflectionService }]; } });
/**
* An injection token for the provided url localization configuration.
* `LanguageIntegrationModule.forRoot()` facilitates the injection of this token. No need to inject directly.
*/
const UrlLocalization = new InjectionToken('LanguageIntegration.UrlLocalizationConfig');
/**
* Provides tools for localization and delocalization of the currently navigated url by adding or removing
* a route segment dedicated for language.
*
* @export
* @class RoutePositionUrlLocalizer
* @extends {UrlLocalizer}
*/
class RoutePositionUrlLocalizer extends UrlLocalizer {
constructor({ strategy }, urlReflection, language) {
super(urlReflection);
this.language = language;
this.position = strategy;
}
/**
* Localizes the currently navigated url by adding or updating the language segment of the route.
* If `position` is positive, language lookup will be performed from the beginning of route.
* If `position` is negative, language lookup will be performed from the end of route.
* If `position` points to an index out of bounds, the last/first element will be treated as the language.
*
* #### Example
* Position 1 - /en/some/route - first segment from the left.
* Position 2 - /some/en/route - second segment from the left.
* Position 5 - /some/route/en - out of bounds. last segment from the left.
*
* Position -1 - /some/route/en - first segment from the right.
* Position -2 - /some/en/route - second segment from the right.
* Position -5 - /en/some/route - out of bounds. last segment from the right.
*
* @param {string} lang
* @returns {string} The currently navigated url localized to the specified language.
*/
localize(lang) {
return this.transformUrl((segments, langIndex, isLanguage) => this.insertOrReplaceLanguage(lang, segments, langIndex, isLanguage));
}
/**
* Delocalizes the currently navigated url by removing the language segment from the route.
* If `position` is positive, language lookup will be performed from the beginning of route.
* If `position` is negative, language lookup will be performed from the end of route.
* If `position` points to an index out of bounds, the last/first element will be treated as the language.
* If no language exists at the language position, returns the url unchanged.
*
* #### Example
* Position 1 - /en/some/route - first segment from the left.
* Position 2 - /some/en/route - second segment from the left.
* Position 5 - /some/route/en - out of bounds. last segment from the left.
*
* Position -1 - /some/route/en - first segment from the right.
* Position -2 - /some/en/route - second segment from the right.
* Position -5 - /en/some/route - out of bounds. last segment from the right.
*
* @returns {string} The delocalized currently navigated url.
*/
delocalize() {
return this.transformUrl((segments, langIndex, isLanguage) => this.removeLanguage(segments, langIndex, isLanguage),
// When removing the language segment, the index should always be in range. Unlike when inserting, an overflowing index
// will not be converted to the last index automatically by `.splice()`.
(segments, langIndex) => langIndex >= segments.length ? segments.length - 1 : langIndex);
}
transformUrl(transform, sanitizeIndex) {
// Position of zero will not touch the url as zero's functionality is not defined
if (this.position === 0)
return this.urlReflection.fullUrl;
const segments = this.urlReflection.routeSegments;
// Convert the position to replace/add to an index (might result in large negatives or positives, exceeding array bounds)
let langIndex = this.indexOfPosition();
this.accessSegmentsSafely(segments, () => {
if (sanitizeIndex)
langIndex = sanitizeIndex(segments, langIndex);
// Determine if a language segment exists at the specified index
const isLanguage = this.isLanguage(segments[langIndex]);
return transform(segments, langIndex, isLanguage);
});
return this.composeUrl(segments);
}
/**
* Updates the specified route segments array with the specified language.
*
* @protected
* @param {string} lang The new language to set to the route.
* @param {string[]} segments The current route segments.
* @param {number} langIndex The index of the expected language segment.
* @param {boolean} isLanguage `true` if the current value at `langIndex` is a supported language; otherwise `false`.
*/
insertOrReplaceLanguage(lang, segments, langIndex, isLanguage) {
// Define how many items to remove. If the segment is already a language and should be replaced, zero items should be removed.
const deleteCount = isLanguage ? 1 : 0;
// Replace existing language segment or add a new one. If the specified index exceeds the length of the array, splice will add a new item at the end/beginning of the array.
segments.splice(langIndex, deleteCount, lang);
}
/**
* Removes the language segment from a route segments array.
* If the language index points to a non-language segment, returns without changing the segments.
*
* @protected
* @param {string[]} segments The current route segments.
* @param {number} langIndex The index of the expected langauge segment.
* @param {boolean} isLanguage `true` if the current value at `langIndex` is a supported language; otherwise `false`.
*/
removeLanguage(segments, langIndex, isLanguage) {
if (!isLanguage)
return;
segments.splice(langIndex, 1);
}
/**
* Accessing segments by index requires the `this.position` to be translated into an index.
* As the position can either be positive or negative, there are two different formulas for index calculation.
* In turn, this means two different scenarios with different edge cases.
*
* To unify the cases and reduce complexity, when position is negative, this method reverses the segments array, runs the segments manipulation, then reverses it again to restore the original order.
* This way the indexing is always done from one side of the array.
*
* @protected
* @param {string[]} segments The segments about to be manipulated.
* @param {() => void} accessSegments The function that needs safe access by index to the
*/
accessSegmentsSafely(segments, accessSegments) {
if (this.isNegativeLookup)
segments.reverse();
accessSegments();
if (this.isNegativeLookup)
segments.reverse();
}
/**
* Indicates whether the configured language position is positive, resulting in a lookup from the left (positive lookup).
* `true` if positive lookup should be performed; otherwise `false`.
*
* @readonly
* @protected
* @type {boolean}
*/
get isPositiveLookup() {
return this.position > 0;
}
/**
* Indicates whether the configured language position is negative, resulting in a lookup from the right (negative lookup).
* `true` if negative lookup should be performed; otherwise `false`.
*
* @readonly
* @protected
* @type {boolean}
*/
get isNegativeLookup() {
return this.position < 0;
}
/**
* Calculates the absolute index for the configured language position.
*
* @protected
* @returns {number}
*/
indexOfPosition() {
return Math.abs(this.position) - 1;
}
/**
* Checks whether the specified value is a language supported by the language integration services.
*
* @protected
* @param {string} value The value to check.
* @returns {boolean} `true` if the value is a supported language; otherwise `false`.
*/
isLanguage(value) {
return this.language.supported?.includes(value) || false;
}
/**
* Concats the host url as given by the url reflection service with the segments and the current query string to create
* a fully qualified url.
*
* @protected
* @param {string[]} segments The route segments to place in the url.
* @returns {string} The fully qualified url composed of the host url as given by the url reflection service, the specified route segments, and the current query params.
*/
composeUrl(segments) {
const { hostUrl, queryString } = this.urlReflection;
return `${hostUrl}/${segments.join('/')}${queryString}`;
}
}
RoutePositionUrlLocalizer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutePositionUrlLocalizer, deps: [{ token: UrlLocalization }, { token: i1.UrlReflectionService }, { token: LanguageIntegrationService }], target: i0.ɵɵFactoryTarget.Injectable });
RoutePositionUrlLocalizer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutePositionUrlLocalizer, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutePositionUrlLocalizer, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [UrlLocalization]
}] }, { type: i1.UrlReflectionService }, { type: LanguageIntegrationService }]; } });
/**
* Provides tools for localization and delocalization of the currently navigated url by adding or removing
* a named query param dedicated for language.
*
* @export
* @class QueryParamsUrlLocalizer
* @extends {UrlLocalizer}
*/
class QueryParamsUrlLocalizer extends UrlLocalizer {
constructor({ strategy }, urlReflection) {
super(urlReflection);
this.paramName = strategy;
}
/**
* Localizes the currently navigated url by adding or updating the query param specifying the language.
*
* @param {string} lang The new language of the url.
* @returns {string} The currently navigated url localized into the specified language.
*/
localize(lang) {
const currentUrlTree = this.parseUrlTree();
const localizedParams = this.replaceLanguageParam(this.urlReflection.queryParams, lang);
const localizedRoute = this.replaceQueryParamsInUrlTree(currentUrlTree, localizedParams);
const localizedRouteUrl = this.urlReflection.router.serializeUrl(localizedRoute);
return this.composeUrl(localizedRouteUrl);
}
/**
* Delocalizes the currently navigated url by removing the query param specifying the language.
*
* @returns {string} The currently navigated url without the query param for language.
*/
delocalize() {
return this.localize('');
}
/**
* Returns the `UrlTree` representing the currently navigated url.
*
* @protected
* @returns {UrlTree} The `UrlTree` representing the currently navigated url.
*/
parseUrlTree() {
const { router } = this.urlReflection;
// Parsing the url seems dumb as the router should have it parsed already, but the route object doesn't hold
// the tree and the router SOMETIMES holds it in getCurrentNavigation().
return router.parseUrl(router.url);
}
/**
* Updates the language param in a query params object.
*
* @protected
* @param {*} params The object representing the query params.
* @param {string} lang The new language to set to the language query param. If `null` or `undefined`, the language query param will be deleted from the object.
* @returns {*} The updated query params object.
*/
replaceLanguageParam(params, lang) {
if (lang)
params[this.paramName] = lang;
else
delete params[this.paramName];
return params;
}
/**
* Replaces the query params in a url tree object.
*
* @protected
* @param {UrlTree} url The url tree in which query params should be replaced.
* @param {Object} newParams The new query params object to set to the url tree.
* @returns {UrlTree} The updated url tree object.
*/
replaceQueryParamsInUrlTree(url, newParams) {
return Object.assign(url, { queryParams: newParams });
}
/**
* Concats the host url and the specified route url to compose a fully qualified url.
* Uses the host url provided by the url reflection service.
*
* @protected
* @param {string} routeUrl The route url to concat to the host url. Should be prefixed with '/'.
* @returns {string} The fully qualified url composed of the host url defined by the url reflection service and the specified route url.
*/
composeUrl(routeUrl) {
const { hostUrl } = this.urlReflection;
return `${hostUrl}${routeUrl}`;
}
}
QueryParamsUrlLocalizer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: QueryParamsUrlLocalizer, deps: [{ token: UrlLocalization }, { token: i1.UrlReflectionService }], target: i0.ɵɵFactoryTarget.Injectable });
QueryParamsUrlLocalizer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: QueryParamsUrlLocalizer, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: QueryParamsUrlLocalizer, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [UrlLocalization]
}] }, { type: i1.UrlReflectionService }]; } });
/**
* A noop implemetation for the `UrlLocalizer` class.
* Always returns unchanged urls.
*
* @export
* @class NoopUrlLocalizer
* @extends {UrlLocalizer}
*/
class NoopUrlLocalizer extends UrlLocalizer {
/**
* Returns the currently navigated url as is.
*
* @param {string} lang Ignore.
* @returns {string} The currently navigated url as is.
*/
localize(lang) {
return this.urlReflection.fullUrl;
}
/**
* Returns the currently navigated url as is.
*
* @returns {string} The currently navigated url as is.
*/
delocalize() {
return this.urlReflection.fullUrl;
}
}
NoopUrlLocalizer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: NoopUrlLocalizer, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
NoopUrlLocalizer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: NoopUrlLocalizer, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: NoopUrlLocalizer, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
/**
* The default configuration for url localization when loading the language integration module.
* Uses the `NoopUrlLocalizer` as strategy and does not force https. Localization and delocalization will always return an unchanged url url.
*/
const DefaultUrlLocalizationConfig = {
strategy: { useClass: NoopUrlLocalizer },
forceHttps: false
};
/**
* Creates the appropriate DI compatible provider for the `UrlLocalizer` class, depending on the strategy specified in the url localization configuration.
* If the configured strategy is a number, `RoutePositionUrlLocalizer` will be used.
* If the configured strategy is a string, `QueryParamsUrlLocalizer` will be used.
* If the configured strategy is a valid `UrlLocalizer` provider, the provider will be used as is.
* Otherwise, `NoopUrlLocalizer` will be used.
*
* @export
* @param {UrlLocalizationConfig} config The url localization configuration holding the strategy.
* @returns {(ClassProvider | FactoryProvider)} A DI compatible provider for the `UrlLocalizer` class with the implementation appropriate for the specified strategy.
*/
function provideUrlLocalizer(config) {
const strategy = config?.strategy;
const strategies = {
// Use route position strategy for numbers
number: () => ({ useFactory: (urlReflect, language) => new RoutePositionUrlLocalizer(config, urlReflect, language), deps: [UrlReflectionService, LanguageIntegrationService] }),
// Use query params strategy for strings
string: () => ({ useFactory: (urlReflect) => new QueryParamsUrlLocalizer(config, urlReflect), deps: [UrlReflectionService] }),
// Use the user's factory or class provider
object: () => strategy,
// Use the noop localizer when nothing provided (in case url localization config is not present)
undefined: () => ({ useClass: NoopUrlLocalizer })
};
// Create a basic provider to which the strategy will be assigned
const provider = { provide: UrlLocalizer };
// Override the useClass or useFactory with the detected strategy
return Object.assign(provider, strategies[typeof strategy]());
}
/**
* Creates the providers for the `UrlLocalization` token and the `UrlLocalizer` class.
*
* @export
* @param {UrlLocalizationConfig} [config] (Optional) The configuration for url localization tools. Default is `DefaultUrlLocalizationConfig`.
* @returns {Provider[]} The providers for the `UrlLocalization` token and the `UrlLocalizer` class.
*/
function provideUrlLocalization(config) {
config = Object.assign({}, DefaultUrlLocalizationConfig, config);
return [
{ provide: UrlLocalization, useValue: config },
provideUrlLocalizer(config)
];
}
/**
* Generates language integration tokens and services to be provided in a module.
* Used by `LanguageIntegrationModule.forRoot()`.
*
* @export
* @param {LanguageIntegrationProvider} { useFactory, deps, urlLocalization } The language integration provider configuration.
* @returns {Provider[]} An array of providers for language integration.
*/
function provideLanguageIntegration({ useFactory, deps, urlLocalization }) {
return [
{ provide: LanguageIntegration, useFactory, deps },
...provideUrlLocalization(urlLocalization)
];
}
/**
* Provides services for libraries requiring integration with their user's language services.
*
* @export
* @class LanguageIntegrationModule
*/
class LanguageIntegrationModule {
constructor(parentModule) {
if (parentModule)
throw new Error('`LanguageIntegrationModule` has already been loaded. Import it only once, in your app module, using `forRoot()`.');
}
/**
* Generates the language integration modules with the appropriate providers for the app to share its language services with
* libraries and supporting languages.
*
* @static
* @param {LanguageIntegrationProvider} configProvider The integration configuration. Tells the module how to operate with your language services.
*/
static forRoot(configProvider) {
return {
ngModule: LanguageIntegrationModule,
providers: provideLanguageIntegration(configProvider)
};
}
}
LanguageIntegrationModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationModule, deps: [{ token: LanguageIntegrationModule, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.NgModule });
LanguageIntegrationModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationModule, imports: [CoreModule] });
LanguageIntegrationModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationModule, imports: [CoreModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LanguageIntegrationModule, decorators: [{
type: NgModule,
args: [{
imports: [CoreModule]
}]
}], ctorParameters: function () { return [{ type: LanguageIntegrationModule, decorators: [{
type: Optional
}, {
type: SkipSelf
}] }]; } });
/**
* Integrates with the `LanguageIntegrationService` and facilitates language related work in route-aware services.
*
* @export
* @abstract
* @class LocalizedRouteAware
* @extends {RouteAware}
*/
// eslint-disable-next-line @angular-eslint/directive-class-suffix
class LocalizedRouteAware extends RouteAware {
/**
* Creates an instance of LocalizedRouteAware.
*
* @param {LanguageIntegrationService} language The instance of the language integration service.
* @param {Router} router The instance of Angular's router service.
* @param {ActivatedRoute} route The instance of Angular's activated route service.
* @param {RouterOutletComponentBus} [componentBus] (Optional) The component bus for router-x functionality.
* Provide this when you want your route-aware service to have access to the instance(s) of the activated component(s).
*/
constructor(language, router, route, componentBus) {
super(router, route, componentBus);
this.language = language;
if (this.language.enabled)
this.initLanguageSupport();
}
initLanguageSupport() {
this.subscribe(this.language.ready, this.onLanguageServicesReady.bind(this));
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.subscribe(this.language.changed, this.onLanguageChanged.bind(this));
}
/**
* Called when the app's language services have initialized and are ready for use.
* When language integration is disabled, or no ready observable have been provided by the app
* this will execute immediatelly on construction time.
*
* Override to implement.
*
* @virtual
* @protected
*/
onLanguageServicesReady() { void 0; }
/**
* Called when the current language used by the integrated app has changed. Override to implement.
*
* @virtual
* @protected
* @param {*} lang The language code of the new language.
*/
onLanguageChanged(lang) { void 0; }
}
LocalizedRouteAware.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LocalizedRouteAware, deps: [{ token: LanguageIntegrationService }, { token: i2.Router }, { token: i2.ActivatedRoute }, { token: i1.RouterOutletComponentBus }], target: i0.ɵɵFactoryTarget.Directive });
LocalizedRouteAware.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: LocalizedRouteAware, usesInheritance: true, ngImport: i0 });
LocalizedRouteAware.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LocalizedRouteAware });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LocalizedRouteAware, decorators: [{
type: Directive
}, {
type: Injectable
}], ctorParameters: function () { return [{ type: LanguageIntegrationService }, { type: i2.Router }, { type: i2.ActivatedRoute }, { type: i1.RouterOutletComponentBus }]; } });
/**
* Provides tools for localization and delocalization of the currently navigated url taking into
* account the url localization configuration provided when importing the language integration module.
*
* @export
* @class UrlLocalizationService
*/
class UrlLocalizationService {
/**
* Creates an instance of UrlLocalizationService.
*
* @param {UrlLocalizationConfig} config The url localization configuration provided for the `UrlLocalization` token.
* @param {UrlReflectionService} urlReflection The url reflection service.
* @param {UrlLocalizer} localizer The url localizer which will actually do the localization work.
* The instance and implementation depend on the strategy configured for url localization when importing the language integration module.
*/
constructor(config, urlReflection, localizer) {
this.config = config;
this.urlReflection = urlReflection;
this.localizer = localizer;
}
/**
* Localizes the currently navigated url using the configured localization strategy and forces https if needed.
*
* @param {string} lang The langugae to localize the currently navigated url to.
* @returns {string} The localized currently navigated url.
*/
localize(lang) {
return this.replaceHttpIfRequired(this.localizer.localize(lang));
}
/**
* Delocalizes the currently navigated url using the configured localization strategy and forces https if needed.
*
* @returns {string} The delocalized currently navigated url.
*/
delocalize() {
return this.replaceHttpIfRequired(this.localizer.delocalize());
}
/**
* Generates a localized version of the currently navigate url for each of the specified languages using the configured localization strategy.
*
* @param {string[]} langs The languages for which to generate the localized urls.
* @returns {{ [lang: string]: string }[]} An array of { [lang]: url } containing an object for each language and its corresponding localized url.
*/
generateLocalizedUrls(langs) {
return langs.map(lang => ({ [lang]: this.localizer.localize(lang) }));
}
replaceHttpIfRequired(url) {
return this.config?.forceHttps ? this.urlReflection.forceHttps(url) : url;
}
}
UrlLocalizationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlLocalizationService, deps: [{ token: UrlLocalization }, { token: i1.UrlReflectionService }, { token: UrlLocalizer }], target: i0.ɵɵFactoryTarget.Injectable });
UrlLocalizationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlLocalizationService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlLocalizationService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [UrlLocalization]
}] }, { type: i1.UrlReflectionService }, { type: UrlLocalizer }]; } });
/**
* Generated bundle index. Do not edit.
*/
export { DefaultUrlLocalizationConfig, LanguageIntegration, LanguageIntegrationModule, LanguageIntegrationService, LocalizedRouteAware, NoopUrlLocalizer, QueryParamsUrlLocalizer, RoutePositionUrlLocalizer, UrlLocalization, UrlLocalizationService, UrlLocalizer, provideLanguageIntegration, provideUrlLocalization, provideUrlLocalizer };
//# sourceMappingURL=bespunky-angular-zen-language.mjs.map