mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-11 12:12:55 +00:00
feat: date-fns
This commit is contained in:
@@ -101,7 +101,7 @@ export function initializerFactory(
|
||||
// this language will be used as a fallback when a translation isn't found in the current language
|
||||
translateService.setDefaultLang('en');
|
||||
translateService.use(languageCode);
|
||||
const dateFnsLocale = await getDateFnsLocale(languageCode as SCLanguageCode);
|
||||
const dateFnsLocale = await getDateFnsLocale(languageCode as SCLanguageCode, translateService);
|
||||
setDefaultOptions({locale: dateFnsLocale});
|
||||
dateFnsConfigurationService.setLocale(dateFnsLocale);
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
>
|
||||
<ion-list-header>
|
||||
{{ frequency.children[0].item.repeatFrequency ? (frequency.children[0].item.repeatFrequency |
|
||||
dfnsParseDuration | dfnsFormatFrequency | sentencecase) : ('data.chips.add_events.popover.SINGLE' |
|
||||
translate | titlecase) }}
|
||||
dfnsParseDuration | dfnsFormatFrequencyPure | sentencecase) :
|
||||
('data.chips.add_events.popover.SINGLE' | translate | titlecase) }}
|
||||
</ion-list-header>
|
||||
</ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
@@ -111,8 +111,8 @@ import {
|
||||
ParseIsoPipeModule,
|
||||
} from 'ngx-date-fns';
|
||||
import {ParseDurationPipe} from '../../translation/date-time/parse-duration.pipe';
|
||||
import {DfnsFormatFrequencyPipe} from '../../translation/date-time/format-frequency.pipe';
|
||||
import {FormatRelativeDatePipe} from '../../translation/date-time/format-relative-date.pipe';
|
||||
import {DfnsFormatFrequencyPurePipe} from '../../translation/date-time/format-frequency.pipe';
|
||||
import {DfnsFormatRelativeDatePurePipe} from '../../translation/date-time/format-relative-date.pipe';
|
||||
|
||||
/**
|
||||
* Module for handling data
|
||||
@@ -207,9 +207,9 @@ import {FormatRelativeDatePipe} from '../../translation/date-time/format-relativ
|
||||
ParseDurationPipe,
|
||||
FormatDurationPipeModule,
|
||||
FormatRelativeToNowPipeModule,
|
||||
DfnsFormatFrequencyPipe,
|
||||
DfnsFormatFrequencyPurePipe,
|
||||
FormatDistanceToNowPipeModule,
|
||||
FormatRelativeDatePipe,
|
||||
DfnsFormatRelativeDatePurePipe,
|
||||
],
|
||||
providers: [
|
||||
CoordinatedSearchProvider,
|
||||
|
||||
@@ -16,7 +16,7 @@ import {ChangeDetectionStrategy, Component} from '@angular/core';
|
||||
import {MapPosition, PositionService} from '../../map/position.service';
|
||||
import {Geolocation} from '@capacitor/geolocation';
|
||||
import {BehaviorSubject, from} from 'rxjs';
|
||||
import {pauseWhen} from '../../../util/pause-when';
|
||||
import {pauseWhen} from '../../../util/rxjs/pause-when';
|
||||
import {map, retry, startWith, take} from 'rxjs/operators';
|
||||
import {SCSearchFilter, SCSearchSort} from '@openstapps/core';
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
<ion-segment [(ngModel)]="selectedDay" mode="md">
|
||||
<ion-segment-button *ngFor="let day of dishes | keyvalue" [value]="day.key">
|
||||
<ion-label class="ion-hide-sm-down"
|
||||
>{{ day.key | dfnsParseIso | dfnsFormatRelativeDate | sentencecase }}</ion-label
|
||||
>{{ day.key | dfnsParseIso | dfnsFormatRelativeDatePure | sentencecase }}</ion-label
|
||||
>
|
||||
<ion-label class="ion-hide-sm-up"
|
||||
>{{ day.key | dfnsParseIso | dfnsFormatRelativeDate | sentencecase }}</ion-label
|
||||
>{{ day.key | dfnsParseIso | dfnsFormatRelativeDatePure | sentencecase }}</ion-label
|
||||
>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<ion-item slot="header">
|
||||
<!-- TODO: when using date-fns, use https://date-fns.org/v2.30.0/docs/formatRelative -->
|
||||
<ion-label
|
||||
>{{ myCoursesDay[0] | dfnsParseIso | dfnsFormatRelativeDate | titlecase }} - {{ ('profile.courses.' +
|
||||
(myCoursesDay[1].length === 0 ? 'NO' : myCoursesDay[1].length === 1 ? 'ONE' : 'MANY' ) + '_EVENT') |
|
||||
translate: {count: myCoursesDay[1].length} }}</ion-label
|
||||
>{{ myCoursesDay[0] | dfnsParseIso | dfnsFormatRelativeDatePure | titlecase }} - {{
|
||||
('profile.courses.' + (myCoursesDay[1].length === 0 ? 'NO' : myCoursesDay[1].length === 1 ? 'ONE' :
|
||||
'MANY' ) + '_EVENT') | translate: {count: myCoursesDay[1].length} }}</ion-label
|
||||
>
|
||||
<ion-icon class="ion-accordion-toggle-icon" name="expand_more"></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
@@ -27,7 +27,7 @@ import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {MyCoursesComponent} from './page/my-courses.component';
|
||||
import {FormatPipeModule, ParseIsoPipeModule} from 'ngx-date-fns';
|
||||
import {FormatRelativeDatePipe} from '../../translation/date-time/format-relative-date.pipe';
|
||||
import {DfnsFormatRelativeDatePurePipe} from '../../translation/date-time/format-relative-date.pipe';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -50,7 +50,7 @@ const routes: Routes = [
|
||||
ThingTranslateModule,
|
||||
DataModule,
|
||||
FormatPipeModule,
|
||||
FormatRelativeDatePipe,
|
||||
DfnsFormatRelativeDatePurePipe,
|
||||
ParseIsoPipeModule,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -96,79 +96,6 @@ export class StringSplitPipe implements PipeTransform {
|
||||
return this.value as never;
|
||||
}
|
||||
}
|
||||
@Injectable()
|
||||
@Pipe({
|
||||
name: 'durationLocalized',
|
||||
pure: true,
|
||||
})
|
||||
export class DurationLocalizedPipe implements PipeTransform, OnDestroy {
|
||||
locale: string;
|
||||
|
||||
onLangChange?: Subscription;
|
||||
|
||||
value: string;
|
||||
|
||||
frequencyPrefixes: {[iso6391Code: string]: string} = {
|
||||
de: 'alle',
|
||||
en: 'every',
|
||||
es: 'cada',
|
||||
pt: 'a cada',
|
||||
fr: 'tous les',
|
||||
cn: '每',
|
||||
ru: 'kаждые',
|
||||
};
|
||||
|
||||
constructor(private readonly translate: TranslateService) {
|
||||
this.locale = translate.currentLang;
|
||||
}
|
||||
|
||||
private _dispose(): void {
|
||||
if (this.onLangChange?.closed === false) {
|
||||
this.onLangChange?.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value An ISO 8601 duration string
|
||||
* @param isFrequency Boolean indicating if this duration is to be interpreted as repeat frequency
|
||||
*/
|
||||
transform(value: string | unknown, isFrequency = false): string {
|
||||
this.updateValue(value, isFrequency);
|
||||
this._dispose();
|
||||
if (this.onLangChange?.closed === true) {
|
||||
this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.locale = event.lang;
|
||||
this.updateValue(value, isFrequency);
|
||||
});
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
updateValue(value: string | unknown, isFrequency = false): void {
|
||||
if (typeof value !== 'string') {
|
||||
logger.warn(`durationLocalized pipe unable to parse input: ${value}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFrequency) {
|
||||
const fequencyPrefix = Object.keys(this.frequencyPrefixes).filter(element =>
|
||||
this.locale.includes(element),
|
||||
);
|
||||
this.value = [
|
||||
fequencyPrefix.length > 0 ? this.frequencyPrefixes[fequencyPrefix[0]] : this.frequencyPrefixes.en,
|
||||
moment.duration(value).humanize(),
|
||||
].join(' ');
|
||||
} else {
|
||||
this.value = moment.duration(value).humanize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@Pipe({
|
||||
|
||||
@@ -1,68 +1,14 @@
|
||||
import {Injectable, OnDestroy, Pipe, PipeTransform} from '@angular/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {formatDuration} from 'date-fns';
|
||||
import {Injectable, Pipe, PipeTransform} from '@angular/core';
|
||||
import {formatFrequency} from './format-frequency';
|
||||
|
||||
@Injectable()
|
||||
@Pipe({
|
||||
name: 'dfnsFormatFrequency',
|
||||
name: 'dfnsFormatFrequencyPure',
|
||||
standalone: true,
|
||||
pure: false,
|
||||
pure: true,
|
||||
})
|
||||
export class DfnsFormatFrequencyPipe implements PipeTransform, OnDestroy {
|
||||
locale: string;
|
||||
|
||||
onLangChange?: Subscription;
|
||||
|
||||
value: string;
|
||||
|
||||
frequencyPrefixes: {[iso6391Code: string]: string} = {
|
||||
de: 'alle',
|
||||
en: 'every',
|
||||
es: 'cada',
|
||||
pt: 'a cada',
|
||||
fr: 'tous les',
|
||||
cn: '每',
|
||||
ru: 'kаждые',
|
||||
};
|
||||
|
||||
constructor(private readonly translate: TranslateService) {
|
||||
this.locale = translate.currentLang;
|
||||
}
|
||||
|
||||
private _dispose(): void {
|
||||
if (this.onLangChange?.closed === false) {
|
||||
this.onLangChange?.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value An ISO 8601 duration string
|
||||
*/
|
||||
transform(value: Duration): string {
|
||||
this.updateValue(value);
|
||||
this._dispose();
|
||||
if (this.onLangChange?.closed === true) {
|
||||
this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.locale = event.lang;
|
||||
this.updateValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
updateValue(value: Duration): void {
|
||||
const fequencyPrefix = Object.keys(this.frequencyPrefixes).filter(element =>
|
||||
this.locale.includes(element),
|
||||
);
|
||||
this.value = [
|
||||
fequencyPrefix.length > 0 ? this.frequencyPrefixes[fequencyPrefix[0]] : this.frequencyPrefixes.en,
|
||||
formatDuration(value),
|
||||
].join(' ');
|
||||
export class DfnsFormatFrequencyPurePipe implements PipeTransform {
|
||||
transform(duration: Duration): string {
|
||||
return formatFrequency(duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import {Duration, formatDuration, getDefaultOptions} from 'date-fns';
|
||||
import {LocaleExtension} from '../dfns-locale';
|
||||
|
||||
/**
|
||||
* Formats a duration to a frequency
|
||||
* @param duration the duration to format as a frequency
|
||||
* @example "alle 3 Tage"
|
||||
*/
|
||||
export function formatFrequency(duration: Duration): string {
|
||||
const {locale} = getDefaultOptions() as {locale: LocaleExtension};
|
||||
locale.formatFrequencyOptions;
|
||||
|
||||
// This will break...
|
||||
const formatted = formatDuration(duration);
|
||||
const plural = new Intl.PluralRules(locale.code).select(Number(/\d+/.exec(formatted)?.[0]));
|
||||
const formatString = locale.formatFrequencyOptions[plural] ?? locale.formatFrequencyOptions['many']!;
|
||||
|
||||
return formatString
|
||||
.replaceAll(/(['"](?=\w))|((?<=\w)['"])/g, '')
|
||||
.replaceAll(/{{\s*duration\s*}}/g, formatted)
|
||||
.replaceAll(/{{\s*suffix\s*}}/g, formatted.replace(/^\d+\s*/, ''));
|
||||
}
|
||||
@@ -1,67 +1,13 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {DateFnsConfigurationService} from 'ngx-date-fns';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {formatRelative} from 'date-fns';
|
||||
|
||||
const formatRelativeLocaleEn = {
|
||||
lastWeek: "'last' eeee",
|
||||
yesterday: "'yesterday'",
|
||||
today: "'today'",
|
||||
tomorrow: "'tomorrow'",
|
||||
nextWeek: "'next' eeee",
|
||||
other: 'pp',
|
||||
};
|
||||
|
||||
const formatRelativeLocaleDe = {
|
||||
lastWeek: "'letzten' eeee",
|
||||
yesterday: "'gestern'",
|
||||
today: "'heute'",
|
||||
tomorrow: "'morgen'",
|
||||
nextWeek: 'eeee',
|
||||
other: 'pp',
|
||||
};
|
||||
import {formatRelativeDate} from './format-relative-date';
|
||||
|
||||
@Pipe({
|
||||
name: 'dfnsFormatRelativeDate',
|
||||
pure: false,
|
||||
name: 'dfnsFormatRelativeDatePure',
|
||||
pure: true,
|
||||
standalone: true,
|
||||
})
|
||||
export class FormatRelativeDatePipe implements PipeTransform {
|
||||
locale: Locale;
|
||||
|
||||
value: string;
|
||||
|
||||
input: Date | number;
|
||||
|
||||
base: Date | number;
|
||||
|
||||
constructor(private dfnsConfig: DateFnsConfigurationService) {
|
||||
this.updateLocale();
|
||||
dfnsConfig.localeChanged.pipe(takeUntilDestroyed()).subscribe(this.updateLocale.bind(this));
|
||||
}
|
||||
|
||||
private updateLocale() {
|
||||
this.locale = {...this.dfnsConfig.locale()!};
|
||||
const format = {
|
||||
['de']: formatRelativeLocaleDe,
|
||||
['en-US']: formatRelativeLocaleEn,
|
||||
}[this.locale.code!];
|
||||
this.locale.formatRelative = function (token) {
|
||||
return format![token as keyof typeof format] as string;
|
||||
};
|
||||
this.updateValue();
|
||||
}
|
||||
|
||||
transform(value: Date | number, other: Date | number = Date.now()): string {
|
||||
this.input = value;
|
||||
this.base = other;
|
||||
this.updateValue();
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
updateValue() {
|
||||
if (!this.input || !this.base) return;
|
||||
this.value = formatRelative(this.input, this.base, {locale: this.locale});
|
||||
export class DfnsFormatRelativeDatePurePipe implements PipeTransform {
|
||||
transform(date: Date | number, baseDate: Date | number = Date.now()): string {
|
||||
return formatRelativeDate(date, baseDate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import {formatRelative, getDefaultOptions} from 'date-fns';
|
||||
import {LocaleExtension} from '../dfns-locale';
|
||||
|
||||
/**
|
||||
* @see {formatRelative}
|
||||
*/
|
||||
export function formatRelativeDate(
|
||||
date: Date | number,
|
||||
baseDate: Date | number,
|
||||
options?: {
|
||||
locale?: LocaleExtension;
|
||||
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
},
|
||||
): string {
|
||||
const locale = options?.locale ?? (getDefaultOptions() as {locale: LocaleExtension}).locale;
|
||||
const customLocale: LocaleExtension = {
|
||||
...locale,
|
||||
formatRelative(token: keyof LocaleExtension['formatRelativeDateOptions']) {
|
||||
return locale.formatRelativeDateOptions![token];
|
||||
},
|
||||
};
|
||||
return formatRelative(date, baseDate, {locale: customLocale, weekStartsOn: options?.weekStartsOn});
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import {OnDestroy, Pipe, PipeTransform} from '@angular/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {formatDuration} from 'date-fns';
|
||||
|
||||
@Pipe({
|
||||
name: 'dfnsFormatRelativeLive',
|
||||
pure: false,
|
||||
standalone: true,
|
||||
})
|
||||
export class FormatRelativeLivePipe implements PipeTransform, OnDestroy {
|
||||
locale: string;
|
||||
|
||||
onLangChange?: Subscription;
|
||||
|
||||
value: string;
|
||||
|
||||
constructor(private readonly translate: TranslateService) {
|
||||
this.locale = translate.currentLang;
|
||||
}
|
||||
|
||||
private _dispose(): void {
|
||||
if (this.onLangChange?.closed === false) {
|
||||
this.onLangChange?.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value An ISO 8601 duration string
|
||||
*/
|
||||
transform(value: Duration): string {
|
||||
this.updateValue(value);
|
||||
this._dispose();
|
||||
if (this.onLangChange?.closed === true) {
|
||||
this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.locale = event.lang;
|
||||
this.updateValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
updateValue(value: Duration): void {
|
||||
const fequencyPrefix = Object.keys(this.frequencyPrefixes).filter(element =>
|
||||
this.locale.includes(element),
|
||||
);
|
||||
this.value = [
|
||||
fequencyPrefix.length > 0 ? this.frequencyPrefixes[fequencyPrefix[0]] : this.frequencyPrefixes.en,
|
||||
formatDuration(value),
|
||||
].join(' ');
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
import {Injectable, OnDestroy, Pipe, PipeTransform} from '@angular/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {logger} from '../../_helpers/ts-logger';
|
||||
import opening_hours from 'opening_hours';
|
||||
import {addHours, formatRelative, isBefore, isToday} from 'date-fns';
|
||||
|
||||
@Injectable()
|
||||
@Pipe({
|
||||
name: 'openingHours',
|
||||
standalone: true,
|
||||
pure: false,
|
||||
})
|
||||
export class OpeningHoursPipe implements PipeTransform, OnDestroy {
|
||||
locale: string;
|
||||
|
||||
onLangChange?: Subscription;
|
||||
|
||||
value: string[] = [];
|
||||
|
||||
constructor(private readonly translate: TranslateService) {
|
||||
this.locale = translate.currentLang;
|
||||
}
|
||||
|
||||
private _dispose(): void {
|
||||
if (this.onLangChange?.closed === false) {
|
||||
this.onLangChange?.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._dispose();
|
||||
}
|
||||
|
||||
transform(aString: string | unknown): string[] {
|
||||
this.updateValue(aString);
|
||||
this._dispose();
|
||||
if (this.onLangChange?.closed === true) {
|
||||
this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.locale = event.lang;
|
||||
this.updateValue(aString);
|
||||
});
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
updateValue(aString: string | unknown) {
|
||||
if (typeof aString !== 'string') {
|
||||
logger.warn(`openingHours pipe unable to parse input: ${aString}`);
|
||||
|
||||
return;
|
||||
}
|
||||
let openingHours;
|
||||
|
||||
try {
|
||||
openingHours = new opening_hours(aString, {
|
||||
address: {
|
||||
country_code: 'de',
|
||||
state: 'Hessen',
|
||||
},
|
||||
lon: 8.667_97,
|
||||
lat: 50.129_16,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn(error);
|
||||
this.value = [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const isOpen: boolean = openingHours.getState();
|
||||
const isUnknown: boolean = openingHours.getUnknown();
|
||||
|
||||
const nextChange = openingHours.getNextChange();
|
||||
const nextChangeIsOpen: boolean = openingHours.getState(nextChange);
|
||||
const nextChangeUnknown: boolean = openingHours.getUnknown(nextChange);
|
||||
const nextChangeIsToday: boolean = isToday(nextChange!);
|
||||
|
||||
let stateKey = isOpen ? 'common.openingHours.state_open' : 'common.openingHours.state_closed';
|
||||
|
||||
stateKey = isUnknown ? 'common.openingHours.state_maybe' : stateKey;
|
||||
|
||||
this.value = [isOpen ? 'success' : 'danger', `${this.translate.instant(stateKey)}`];
|
||||
|
||||
if (isUnknown) {
|
||||
const comment = openingHours.getComment();
|
||||
this.value = ['light', `${this.translate.instant(stateKey)}`];
|
||||
if (typeof comment === 'string') {
|
||||
this.value.push(comment);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextChangeUnknown) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nextChangeKey: string | undefined;
|
||||
|
||||
let formattedCalender = formatRelative(nextChange!, Date.now());
|
||||
|
||||
if (isBefore(nextChange!, addHours(Date.now(), 1))) {
|
||||
this.value[0] = 'warning';
|
||||
nextChangeKey = nextChangeIsOpen
|
||||
? 'common.openingHours.opening_soon_warning'
|
||||
: 'common.openingHours.closing_soon_warning';
|
||||
this.value.push(
|
||||
`${this.translate.instant(nextChangeKey, {
|
||||
time: new Intl.DateTimeFormat(this.locale, {
|
||||
timeStyle: 'short',
|
||||
}).format(nextChange),
|
||||
})}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextChangeIsToday) {
|
||||
nextChangeKey = nextChangeIsOpen
|
||||
? 'common.openingHours.opening_today'
|
||||
: 'common.openingHours.closing_today';
|
||||
this.value.push(
|
||||
`${this.translate.instant(nextChangeKey, {
|
||||
time: new Intl.DateTimeFormat(this.locale, {
|
||||
timeStyle: 'short',
|
||||
}).format(nextChange),
|
||||
})}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
nextChangeKey = nextChangeIsOpen ? 'common.openingHours.opening' : 'common.openingHours.closing';
|
||||
formattedCalender = formattedCalender.slice(0, 1).toUpperCase() + formattedCalender.slice(1);
|
||||
this.value.push(
|
||||
`${this.translate.instant(nextChangeKey, {
|
||||
relativeDateTime: formattedCalender,
|
||||
})}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,19 @@
|
||||
*/
|
||||
import {SCLanguageCode} from '@openstapps/core';
|
||||
import type {Locale} from 'date-fns';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
|
||||
export interface LocaleExtension extends Locale {
|
||||
formatFrequencyOptions: Partial<Record<Intl.LDMLPluralRule, string>>;
|
||||
formatRelativeDateOptions: {
|
||||
lastWeek: string;
|
||||
yesterday: string;
|
||||
today: string;
|
||||
tomorrow: string;
|
||||
nextWeek: string;
|
||||
other: string;
|
||||
};
|
||||
}
|
||||
|
||||
type LocalesMap = Record<SCLanguageCode, () => Promise<{default: Locale}>>;
|
||||
|
||||
@@ -25,11 +38,23 @@ const LOCALES = {
|
||||
/**
|
||||
* Get a Date Fns Locale
|
||||
*/
|
||||
export async function getDateFnsLocale(code: SCLanguageCode): Promise<Locale> {
|
||||
if (code in LOCALES) {
|
||||
return LOCALES[code as keyof typeof LOCALES]().then(it => it.default);
|
||||
} else {
|
||||
export async function getDateFnsLocale(
|
||||
code: SCLanguageCode,
|
||||
translator: TranslateService,
|
||||
): Promise<LocaleExtension> {
|
||||
if (!(code in LOCALES)) {
|
||||
console.warn(`Unknown Locale "${code}" for Date Fns. Falling back to English.`);
|
||||
return LOCALES.en().then(it => it.default);
|
||||
}
|
||||
const key = code in LOCALES ? (code as keyof typeof LOCALES) : 'en';
|
||||
const translations = translator.translations[translator.currentLang];
|
||||
const frequencyExtension = translations['dateFns']['FORMAT_FREQUENCY'];
|
||||
const relativeDateExtension = translations['dateFns']['FORMAT_RELATIVE_DATE'];
|
||||
|
||||
return LOCALES[key]().then(it => {
|
||||
const locale = it.default as LocaleExtension;
|
||||
locale.formatFrequencyOptions = {...frequencyExtension};
|
||||
locale.formatRelativeDateOptions = {...relativeDateExtension};
|
||||
|
||||
return locale;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class ThingTranslateService {
|
||||
/** set the default language from configuration */
|
||||
this.translateService.onLangChange.pipe(takeUntilDestroyed()).subscribe((event: LangChangeEvent) => {
|
||||
this.translator.language = event.lang as keyof SCTranslations<SCLanguage>;
|
||||
getDateFnsLocale(event.lang as SCLanguageCode).then(locale => {
|
||||
getDateFnsLocale(event.lang as SCLanguageCode, translateService).then(locale => {
|
||||
setDefaultOptions({locale});
|
||||
this.dfnsConfiguration.setLocale(locale);
|
||||
});
|
||||
|
||||
@@ -8,6 +8,20 @@
|
||||
"export": "Exportieren",
|
||||
"share": "Teilen",
|
||||
"timeSuffix": "Uhr",
|
||||
"dateFns": {
|
||||
"FORMAT_FREQUENCY": {
|
||||
"one": "'jede' {{suffix}}",
|
||||
"many": "'alle' {{duration}}"
|
||||
},
|
||||
"FORMAT_RELATIVE_DATE": {
|
||||
"lastWeek": "'letzten' eeee",
|
||||
"yesterday": "'gestern'",
|
||||
"today": "'heute'",
|
||||
"tomorrow": "'morgen'",
|
||||
"nextWeek": "'nächsten' eeee",
|
||||
"other": "pp"
|
||||
}
|
||||
},
|
||||
"ratings": {
|
||||
"thank_you": "Vielen Dank für die Bewertung!"
|
||||
},
|
||||
|
||||
@@ -8,6 +8,20 @@
|
||||
"export": "Export",
|
||||
"share": "Share",
|
||||
"timeSuffix": "",
|
||||
"dateFns": {
|
||||
"FORMAT_FREQUENCY": {
|
||||
"one": "'every' {{suffix}}",
|
||||
"many": "'every' {{duration}}"
|
||||
},
|
||||
"FORMAT_RELATIVE_DATE": {
|
||||
"lastWeek": "'last' eeee",
|
||||
"yesterday": "'yesterday'",
|
||||
"today": "'today'",
|
||||
"tomorrow": "'tomorrow'",
|
||||
"nextWeek": "'next' eeee",
|
||||
"other": "pp"
|
||||
}
|
||||
},
|
||||
"ratings": {
|
||||
"thank_you": "Thank you for your feedback!"
|
||||
},
|
||||
|
||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -842,6 +842,9 @@ importers:
|
||||
deepmerge:
|
||||
specifier: 4.3.1
|
||||
version: 4.3.1
|
||||
duration-fns:
|
||||
specifier: 3.0.2
|
||||
version: 3.0.2
|
||||
form-data:
|
||||
specifier: 4.0.0
|
||||
version: 4.0.0
|
||||
@@ -863,9 +866,6 @@ importers:
|
||||
material-symbols:
|
||||
specifier: 0.10.0
|
||||
version: 0.10.0
|
||||
moment:
|
||||
specifier: 2.29.4
|
||||
version: 2.29.4
|
||||
ngx-date-fns:
|
||||
specifier: 10.0.1
|
||||
version: 10.0.1(@angular/common@16.1.4)(@angular/core@16.1.4)(date-fns@2.30.0)
|
||||
@@ -875,9 +875,6 @@ importers:
|
||||
ngx-markdown:
|
||||
specifier: 16.0.0
|
||||
version: 16.0.0(@angular/common@16.1.4)(@angular/core@16.1.4)(@angular/platform-browser@16.1.4)(@types/marked@4.3.1)(marked@4.3.0)(rxjs@7.8.1)(zone.js@0.13.1)
|
||||
ngx-moment:
|
||||
specifier: 6.0.2
|
||||
version: 6.0.2(moment@2.29.4)
|
||||
opening_hours:
|
||||
specifier: 3.8.0
|
||||
version: 3.8.0
|
||||
@@ -10328,6 +10325,10 @@ packages:
|
||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||
dev: true
|
||||
|
||||
/duration-fns@3.0.2:
|
||||
resolution: {integrity: sha512-w82IXh/6aWNHFA0qlQazJYJrZTWieTItuuGTE7YX4cxPaZTWhmVImbsBBiMK1/OhGDgiinuCpJoSFILYLDSKDg==}
|
||||
dev: false
|
||||
|
||||
/eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
|
||||
@@ -14333,15 +14334,6 @@ packages:
|
||||
prismjs: 1.29.0
|
||||
dev: false
|
||||
|
||||
/ngx-moment@6.0.2(moment@2.29.4):
|
||||
resolution: {integrity: sha512-HUvDyoJPZKLA3tc+GMQqDpVyCVT2SPfEiV7/CGj2Dwwsn//JhhQ8eTr+RzKqBzLysrXkCwlzulVVJaJ5A0FJEA==}
|
||||
peerDependencies:
|
||||
moment: ^2.19.3
|
||||
dependencies:
|
||||
moment: 2.29.4
|
||||
tslib: 2.4.1
|
||||
dev: false
|
||||
|
||||
/nice-napi@1.0.2:
|
||||
resolution: {integrity: sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==}
|
||||
os: ['!win32']
|
||||
|
||||
Reference in New Issue
Block a user