diff --git a/frontend/app/src/app/app.module.ts b/frontend/app/src/app/app.module.ts
index e1b3c561..e5adcaf5 100644
--- a/frontend/app/src/app/app.module.ts
+++ b/frontend/app/src/app/app.module.ts
@@ -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);
diff --git a/frontend/app/src/app/modules/data/chips/edit-event-selection.html b/frontend/app/src/app/modules/data/chips/edit-event-selection.html
index bac998d2..c9851109 100644
--- a/frontend/app/src/app/modules/data/chips/edit-event-selection.html
+++ b/frontend/app/src/app/modules/data/chips/edit-event-selection.html
@@ -32,8 +32,8 @@
>
{{ 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) }}
diff --git a/frontend/app/src/app/modules/data/data.module.ts b/frontend/app/src/app/modules/data/data.module.ts
index cb993fc7..7106f0c7 100644
--- a/frontend/app/src/app/modules/data/data.module.ts
+++ b/frontend/app/src/app/modules/data/data.module.ts
@@ -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,
diff --git a/frontend/app/src/app/modules/data/list/food-data-list.component.ts b/frontend/app/src/app/modules/data/list/food-data-list.component.ts
index 6379d815..3728e33c 100644
--- a/frontend/app/src/app/modules/data/list/food-data-list.component.ts
+++ b/frontend/app/src/app/modules/data/list/food-data-list.component.ts
@@ -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';
diff --git a/frontend/app/src/app/modules/data/types/place/special/mensa/place-mensa.html b/frontend/app/src/app/modules/data/types/place/special/mensa/place-mensa.html
index 31beccb8..3464d5eb 100644
--- a/frontend/app/src/app/modules/data/types/place/special/mensa/place-mensa.html
+++ b/frontend/app/src/app/modules/data/types/place/special/mensa/place-mensa.html
@@ -19,10 +19,10 @@
{{ day.key | dfnsParseIso | dfnsFormatRelativeDate | sentencecase }}{{ day.key | dfnsParseIso | dfnsFormatRelativeDatePure | sentencecase }}
{{ day.key | dfnsParseIso | dfnsFormatRelativeDate | sentencecase }}{{ day.key | dfnsParseIso | dfnsFormatRelativeDatePure | sentencecase }}
diff --git a/frontend/app/src/app/modules/profile/page/my-courses.html b/frontend/app/src/app/modules/profile/page/my-courses.html
index 0daa793a..e811324e 100644
--- a/frontend/app/src/app/modules/profile/page/my-courses.html
+++ b/frontend/app/src/app/modules/profile/page/my-courses.html
@@ -7,9 +7,9 @@
{{ myCoursesDay[0] | dfnsParseIso | dfnsFormatRelativeDate | titlecase }} - {{ ('profile.courses.' +
- (myCoursesDay[1].length === 0 ? 'NO' : myCoursesDay[1].length === 1 ? 'ONE' : 'MANY' ) + '_EVENT') |
- translate: {count: myCoursesDay[1].length} }}{{ myCoursesDay[0] | dfnsParseIso | dfnsFormatRelativeDatePure | titlecase }} - {{
+ ('profile.courses.' + (myCoursesDay[1].length === 0 ? 'NO' : myCoursesDay[1].length === 1 ? 'ONE' :
+ 'MANY' ) + '_EVENT') | translate: {count: myCoursesDay[1].length} }}
diff --git a/frontend/app/src/app/modules/profile/profile.module.ts b/frontend/app/src/app/modules/profile/profile.module.ts
index b6121a65..5de080e5 100644
--- a/frontend/app/src/app/modules/profile/profile.module.ts
+++ b/frontend/app/src/app/modules/profile/profile.module.ts
@@ -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,
],
})
diff --git a/frontend/app/src/app/translation/common-string-pipes.ts b/frontend/app/src/app/translation/common-string-pipes.ts
index 4ef5dcd5..529e80e4 100644
--- a/frontend/app/src/app/translation/common-string-pipes.ts
+++ b/frontend/app/src/app/translation/common-string-pipes.ts
@@ -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({
diff --git a/frontend/app/src/app/translation/date-time/format-frequency.pipe.ts b/frontend/app/src/app/translation/date-time/format-frequency.pipe.ts
index 471cee0c..00ac5429 100644
--- a/frontend/app/src/app/translation/date-time/format-frequency.pipe.ts
+++ b/frontend/app/src/app/translation/date-time/format-frequency.pipe.ts
@@ -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);
}
}
diff --git a/frontend/app/src/app/translation/date-time/format-frequency.ts b/frontend/app/src/app/translation/date-time/format-frequency.ts
new file mode 100644
index 00000000..c24a1684
--- /dev/null
+++ b/frontend/app/src/app/translation/date-time/format-frequency.ts
@@ -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*/, ''));
+}
diff --git a/frontend/app/src/app/translation/date-time/format-relative-date.pipe.ts b/frontend/app/src/app/translation/date-time/format-relative-date.pipe.ts
index 71118e07..5d0d310e 100644
--- a/frontend/app/src/app/translation/date-time/format-relative-date.pipe.ts
+++ b/frontend/app/src/app/translation/date-time/format-relative-date.pipe.ts
@@ -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);
}
}
diff --git a/frontend/app/src/app/translation/date-time/format-relative-date.ts b/frontend/app/src/app/translation/date-time/format-relative-date.ts
new file mode 100644
index 00000000..6709880f
--- /dev/null
+++ b/frontend/app/src/app/translation/date-time/format-relative-date.ts
@@ -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});
+}
diff --git a/frontend/app/src/app/translation/date-time/format-relative-live.pipe.ts b/frontend/app/src/app/translation/date-time/format-relative-live.pipe.ts
deleted file mode 100644
index d4924f49..00000000
--- a/frontend/app/src/app/translation/date-time/format-relative-live.pipe.ts
+++ /dev/null
@@ -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(' ');
- }
-}
diff --git a/frontend/app/src/app/translation/date-time/opening-hours.pipe.ts b/frontend/app/src/app/translation/date-time/opening-hours.pipe.ts
deleted file mode 100644
index 4f946ea0..00000000
--- a/frontend/app/src/app/translation/date-time/opening-hours.pipe.ts
+++ /dev/null
@@ -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;
- }
-}
diff --git a/frontend/app/src/app/translation/dfns-locale.ts b/frontend/app/src/app/translation/dfns-locale.ts
index 65f2bc2e..28ed9a49 100644
--- a/frontend/app/src/app/translation/dfns-locale.ts
+++ b/frontend/app/src/app/translation/dfns-locale.ts
@@ -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>;
+ formatRelativeDateOptions: {
+ lastWeek: string;
+ yesterday: string;
+ today: string;
+ tomorrow: string;
+ nextWeek: string;
+ other: string;
+ };
+}
type LocalesMap = Record Promise<{default: Locale}>>;
@@ -25,11 +38,23 @@ const LOCALES = {
/**
* Get a Date Fns Locale
*/
-export async function getDateFnsLocale(code: SCLanguageCode): Promise {
- 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 {
+ 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;
+ });
}
diff --git a/frontend/app/src/app/translation/thing-translate.service.ts b/frontend/app/src/app/translation/thing-translate.service.ts
index 77d5d89c..a27cee09 100644
--- a/frontend/app/src/app/translation/thing-translate.service.ts
+++ b/frontend/app/src/app/translation/thing-translate.service.ts
@@ -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;
- getDateFnsLocale(event.lang as SCLanguageCode).then(locale => {
+ getDateFnsLocale(event.lang as SCLanguageCode, translateService).then(locale => {
setDefaultOptions({locale});
this.dfnsConfiguration.setLocale(locale);
});
diff --git a/frontend/app/src/assets/i18n/de.json b/frontend/app/src/assets/i18n/de.json
index a49a6a89..74efa6e3 100644
--- a/frontend/app/src/assets/i18n/de.json
+++ b/frontend/app/src/assets/i18n/de.json
@@ -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!"
},
diff --git a/frontend/app/src/assets/i18n/en.json b/frontend/app/src/assets/i18n/en.json
index 115792f7..b8c950a3 100644
--- a/frontend/app/src/assets/i18n/en.json
+++ b/frontend/app/src/assets/i18n/en.json
@@ -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!"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 09f728a4..ee0130b6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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']