mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
Fix frequency translations (#8000)
* fix(translation): fall back to translation language for date/time locale * fix(translation): use correct dative ordinal inflection in German monthly nth weekday settings * fix(translation): use correct dative ordinal inflection in German monthly nth weekday settings * fix(date-time): reset date-time locale to active UI language when override is removed * fix(tasks): correct monthly repeat display precedence and validity checks * refactor(tasks): use UTC-anchored Sunday for deterministic weekday-name lookups * fix(tasks): standardize quick settings translation keys * test(tasks): improve getTaskRepeatInfoText test coverage for monthly nth-weekday and fallbacks * docs(i18n): explain dative form suffix for ORD_*_NTH translation keys * translation(de): translate all missing keys in de.json to German * fix(date-time): prevent crashes when DateAdapter is not provided or mocked * fix(tasks): use correct dative ordinal inflection in monthly nth weekday quick settings
This commit is contained in:
parent
09437a7e7a
commit
25896fd318
15 changed files with 741 additions and 408 deletions
|
|
@ -16,6 +16,14 @@ Super Productivity uses JSON files for translations, located in `src/assets/i18n
|
|||
|
||||
**English (`en.json`) is the fallback language.** If a translation is missing or empty, the app automatically displays the English text.
|
||||
|
||||
### Suffixes for Inflected/Dative Forms (`_NTH`)
|
||||
|
||||
Some keys have duplicates with an `_NTH` suffix (e.g., `ORD_FIRST` vs `ORD_FIRST_NTH`).
|
||||
|
||||
- `ORD_FIRST` is used as a standalone option in the quick-setting menu (e.g., "first").
|
||||
- `ORD_FIRST_NTH` is used inside full sentences (e.g., dative/inflected form in German or other inflected languages like "Monthly on the first Monday").
|
||||
- In languages without inflection (like English), these values are identical.
|
||||
|
||||
### Empty Values Are Intentional
|
||||
|
||||
When you see empty strings (`""`), this is **intentional** - it triggers the English fallback. Do not copy the English text into empty fields unless you're providing an actual translation.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { DateTimeFormatService } from './date-time-format.service';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { MockStore, provideMockStore } from '@ngrx/store/testing';
|
||||
import { DEFAULT_GLOBAL_CONFIG } from '../../features/config/default-global-config.const';
|
||||
import { DateAdapter, MatNativeDateModule } from '@angular/material/core';
|
||||
import {
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
} from 'src/app/core/locale.constants';
|
||||
import { GlobalConfigState } from '../../features/config/global-config.model';
|
||||
import { CustomDateAdapter } from './custom-date-adapter';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
describe('DateTimeFormatService', () => {
|
||||
let service: DateTimeFormatService;
|
||||
|
|
@ -31,6 +32,10 @@ describe('DateTimeFormatService', () => {
|
|||
providers: [
|
||||
DateTimeFormatService,
|
||||
{ provide: DateAdapter, useClass: CustomDateAdapter },
|
||||
{
|
||||
provide: TranslateService,
|
||||
useValue: { currentLang: 'en', defaultLang: 'en' },
|
||||
},
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
globalConfig: config,
|
||||
|
|
@ -49,6 +54,10 @@ describe('DateTimeFormatService', () => {
|
|||
providers: [
|
||||
DateTimeFormatService,
|
||||
{ provide: DateAdapter, useClass: CustomDateAdapter },
|
||||
{
|
||||
provide: TranslateService,
|
||||
useValue: { currentLang: 'en', defaultLang: 'en' },
|
||||
},
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
globalConfig: DEFAULT_GLOBAL_CONFIG,
|
||||
|
|
@ -231,4 +240,60 @@ describe('DateTimeFormatService', () => {
|
|||
expect(formattedKoKr).toBe('오후 2:00');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dateTimeLocale config fallback', () => {
|
||||
let mockStore: MockStore;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatNativeDateModule],
|
||||
providers: [
|
||||
DateTimeFormatService,
|
||||
{ provide: DateAdapter, useClass: CustomDateAdapter },
|
||||
{
|
||||
provide: TranslateService,
|
||||
useValue: {
|
||||
currentLang: 'fr',
|
||||
defaultLang: 'en',
|
||||
},
|
||||
},
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
globalConfig: {
|
||||
...DEFAULT_GLOBAL_CONFIG,
|
||||
localization: {
|
||||
...DEFAULT_GLOBAL_CONFIG.localization,
|
||||
dateTimeLocale: 'de',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
mockStore = TestBed.inject(MockStore);
|
||||
service = TestBed.inject(DateTimeFormatService);
|
||||
});
|
||||
|
||||
it('should fall back to active UI language when dateTimeLocale is removed', () => {
|
||||
// Initially, it should use the configured override 'de'
|
||||
TestBed.flushEffects();
|
||||
expect(service.currentLocale()).toBe('de');
|
||||
|
||||
// Now update config to remove the override (setting it to null)
|
||||
mockStore.setState({
|
||||
globalConfig: {
|
||||
...DEFAULT_GLOBAL_CONFIG,
|
||||
localization: {
|
||||
...DEFAULT_GLOBAL_CONFIG.localization,
|
||||
dateTimeLocale: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
TestBed.flushEffects();
|
||||
|
||||
// It should fall back to currentLang 'fr'
|
||||
expect(service.currentLocale()).toBe('fr');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
import { computed, effect, inject, Injectable } from '@angular/core';
|
||||
import { computed, effect, inject, Injectable, signal } from '@angular/core';
|
||||
import { GlobalConfigService } from '../../features/config/global-config.service';
|
||||
import { DateAdapter } from '@angular/material/core';
|
||||
import { DEFAULT_LOCALE, DateTimeLocale } from 'src/app/core/locale.constants';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DateTimeFormatService {
|
||||
private readonly _globalConfigService = inject(GlobalConfigService);
|
||||
private _dateAdapter = inject(DateAdapter);
|
||||
private _dateAdapter = inject(DateAdapter, { optional: true });
|
||||
private readonly _translateService = inject(TranslateService);
|
||||
private readonly _localeSig = signal<DateTimeLocale>(DEFAULT_LOCALE);
|
||||
|
||||
// Signal for the locale to use
|
||||
readonly currentLocale = computed<DateTimeLocale>(() => {
|
||||
return this._globalConfigService.localization()?.dateTimeLocale || DEFAULT_LOCALE;
|
||||
return this._globalConfigService.localization()?.dateTimeLocale || this._localeSig();
|
||||
});
|
||||
|
||||
/** Test formats to detect locale-specific time and date formats (e.g., 24h vs 12h, DD/MM vs MM/DD) */
|
||||
|
|
@ -55,13 +58,24 @@ export class DateTimeFormatService {
|
|||
// Use effect to reactively update date adapter locale when config changes
|
||||
effect(() => {
|
||||
const cfgValue = this._globalConfigService.localization()?.dateTimeLocale;
|
||||
if (cfgValue) this.setDateAdapterLocale(cfgValue);
|
||||
if (cfgValue) {
|
||||
this.setDateAdapterLocale(cfgValue);
|
||||
} else {
|
||||
const uiLang =
|
||||
this._translateService.currentLang ||
|
||||
this._translateService.defaultLang ||
|
||||
DEFAULT_LOCALE;
|
||||
this.setDateAdapterLocale(uiLang as DateTimeLocale);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Set the locale for the date adapter formatting */
|
||||
setDateAdapterLocale(locale: DateTimeLocale): void {
|
||||
this._dateAdapter.setLocale(locale);
|
||||
if (this._dateAdapter && typeof this._dateAdapter.setLocale === 'function') {
|
||||
this._dateAdapter.setLocale(locale);
|
||||
}
|
||||
this._localeSig.set(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ export class TagListComponent {
|
|||
repeatCfg,
|
||||
this._dateTimeFormatService.currentLocale(),
|
||||
this._dateTimeFormatService,
|
||||
this._translateService,
|
||||
);
|
||||
chips.push({
|
||||
title: this._translateService.instant(key, params),
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ describe('buildRepeatQuickSettingOptions', () => {
|
|||
'YEARLY_CURRENT_DATE',
|
||||
'CUSTOM',
|
||||
]);
|
||||
|
||||
const monthlyNthWeekdayOption = options.find(
|
||||
(o) => o.value === 'MONTHLY_NTH_WEEKDAY',
|
||||
);
|
||||
expect(monthlyNthWeekdayOption).toBeTruthy();
|
||||
// Verify it uses the NTH/dative variant to ensure correct grammar in non-English locales
|
||||
expect(monthlyNthWeekdayOption?.label).toContain('F.TASK_REPEAT.F.ORD_FIRST_NTH');
|
||||
expect(monthlyNthWeekdayOption?.label).not.toContain('F.TASK_REPEAT.F.ORD_FIRST:');
|
||||
});
|
||||
|
||||
// Regression test for #7945: a Date the form's `defaultValue` left unparsed
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { T } from '../../../t.const';
|
|||
import { RepeatQuickSetting } from '../task-repeat-cfg.model';
|
||||
|
||||
const ORDINAL_KEYS = [
|
||||
T.F.TASK_REPEAT.F.ORD_FIRST,
|
||||
T.F.TASK_REPEAT.F.ORD_SECOND,
|
||||
T.F.TASK_REPEAT.F.ORD_THIRD,
|
||||
T.F.TASK_REPEAT.F.ORD_FOURTH,
|
||||
T.F.TASK_REPEAT.F.ORD_FIRST_NTH,
|
||||
T.F.TASK_REPEAT.F.ORD_SECOND_NTH,
|
||||
T.F.TASK_REPEAT.F.ORD_THIRD_NTH,
|
||||
T.F.TASK_REPEAT.F.ORD_FOURTH_NTH,
|
||||
];
|
||||
|
||||
export const buildRepeatQuickSettingOptions = (
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ export class RepeatCfgPreviewComponent {
|
|||
this.repeatCfg(),
|
||||
this._dateTimeFormatService.currentLocale(),
|
||||
this._dateTimeFormatService,
|
||||
this._translateService,
|
||||
);
|
||||
return this._translateService.instant(key, params);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ export class DialogViewArchivedTaskComponent {
|
|||
repeatCfg,
|
||||
this._dateTimeFormatService.currentLocale(),
|
||||
this._dateTimeFormatService,
|
||||
this._translateService,
|
||||
);
|
||||
this.repeatCfgLabel.set(this._translateService.instant(key, params));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,18 @@ import {
|
|||
TaskRepeatCfg,
|
||||
} from '../../task-repeat-cfg/task-repeat-cfg.model';
|
||||
import { T } from '../../../t.const';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
const mockTranslateService = {
|
||||
instant: (key: string) => {
|
||||
if (key === T.F.TASK_REPEAT.F.ORD_FIRST_NTH) return 'first';
|
||||
if (key === T.F.TASK_REPEAT.F.ORD_SECOND_NTH) return 'second';
|
||||
if (key === T.F.TASK_REPEAT.F.ORD_THIRD_NTH) return 'third';
|
||||
if (key === T.F.TASK_REPEAT.F.ORD_FOURTH_NTH) return 'fourth';
|
||||
if (key === T.F.TASK_REPEAT.F.ORD_LAST_NTH) return 'last';
|
||||
return key;
|
||||
},
|
||||
} as unknown as TranslateService;
|
||||
|
||||
describe('getTaskRepeatInfoText()', () => {
|
||||
(
|
||||
|
|
@ -156,6 +168,74 @@ describe('getTaskRepeatInfoText()', () => {
|
|||
quickSetting: 'CUSTOM',
|
||||
},
|
||||
],
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_LAST_DAY,
|
||||
{ timeStr: '' },
|
||||
{
|
||||
repeatEvery: 1,
|
||||
repeatCycle: 'MONTHLY',
|
||||
quickSetting: 'CUSTOM',
|
||||
monthlyLastDay: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY,
|
||||
{ timeStr: '', ordinalStr: 'first', weekdayStr: 'Saturday' },
|
||||
{
|
||||
repeatEvery: 1,
|
||||
repeatCycle: 'MONTHLY',
|
||||
quickSetting: 'CUSTOM',
|
||||
monthlyWeekOfMonth: 1,
|
||||
monthlyWeekday: 6,
|
||||
},
|
||||
],
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY,
|
||||
{ timeStr: '', ordinalStr: 'last', weekdayStr: 'Monday' },
|
||||
{
|
||||
repeatEvery: 1,
|
||||
repeatCycle: 'MONTHLY',
|
||||
quickSetting: 'CUSTOM',
|
||||
monthlyWeekOfMonth: -1,
|
||||
monthlyWeekday: 1,
|
||||
},
|
||||
],
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY,
|
||||
{ timeStr: '', ordinalStr: 'second', weekdayStr: 'Wednesday' },
|
||||
{
|
||||
repeatEvery: 1,
|
||||
repeatCycle: 'MONTHLY',
|
||||
quickSetting: 'CUSTOM',
|
||||
monthlyWeekOfMonth: 2,
|
||||
monthlyWeekday: 3,
|
||||
monthlyLastDay: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_CURRENT_DATE,
|
||||
{ timeStr: '', dateDayStr: '24' },
|
||||
{
|
||||
repeatEvery: 1,
|
||||
repeatCycle: 'MONTHLY',
|
||||
quickSetting: 'CUSTOM',
|
||||
startDate: '2022-02-24',
|
||||
monthlyWeekOfMonth: 5,
|
||||
monthlyWeekday: 6,
|
||||
},
|
||||
],
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_CURRENT_DATE,
|
||||
{ timeStr: '', dateDayStr: '24' },
|
||||
{
|
||||
repeatEvery: 1,
|
||||
repeatCycle: 'MONTHLY',
|
||||
quickSetting: 'CUSTOM',
|
||||
startDate: '2022-02-24',
|
||||
monthlyWeekOfMonth: 1,
|
||||
monthlyWeekday: undefined,
|
||||
},
|
||||
],
|
||||
|
||||
[
|
||||
T.F.TASK_REPEAT.ADD_INFO_PANEL.CUSTOM_WEEKLY,
|
||||
|
|
@ -191,6 +271,8 @@ describe('getTaskRepeatInfoText()', () => {
|
|||
id: 'IDDD',
|
||||
},
|
||||
'en-US',
|
||||
undefined,
|
||||
mockTranslateService,
|
||||
),
|
||||
).toEqual([translationKey, translateParams]);
|
||||
});
|
||||
|
|
@ -208,6 +290,8 @@ describe('getTaskRepeatInfoText()', () => {
|
|||
startTime: 'INVALID_CLOCK_STRING',
|
||||
},
|
||||
'en-US',
|
||||
undefined,
|
||||
mockTranslateService,
|
||||
),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
|
@ -222,6 +306,8 @@ describe('getTaskRepeatInfoText()', () => {
|
|||
startTime: 'INVALID_CLOCK_STRING',
|
||||
},
|
||||
'en-US',
|
||||
undefined,
|
||||
mockTranslateService,
|
||||
);
|
||||
expect(key).toBe(T.F.TASK_REPEAT.ADD_INFO_PANEL.DAILY);
|
||||
expect(params).toEqual({ timeStr: '' });
|
||||
|
|
|
|||
|
|
@ -9,11 +9,14 @@ import { dateStrToUtcDate } from '../../../util/date-str-to-utc-date';
|
|||
import { getWeekdaysMin } from '../../../util/get-weekdays-min';
|
||||
import { DateTimeFormatService } from '../../../core/date-time-format/date-time-format.service';
|
||||
import { getEffectiveRepeatStartDate } from '../../task-repeat-cfg/store/get-effective-repeat-start-date.util';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { hasNthWeekdayAnchor } from '../../task-repeat-cfg/store/get-nth-weekday-of-month.util';
|
||||
|
||||
export const getTaskRepeatInfoText = (
|
||||
repeatCfg: TaskRepeatCfg,
|
||||
locale: string | undefined,
|
||||
dateTimeFormatService?: DateTimeFormatService,
|
||||
dateTimeFormatService: DateTimeFormatService | undefined,
|
||||
translateService: TranslateService,
|
||||
): [string, { [key: string]: string | number }] => {
|
||||
const timeStr =
|
||||
repeatCfg.startTime && isValidSplitTime(repeatCfg.startTime)
|
||||
|
|
@ -86,12 +89,10 @@ export const getTaskRepeatInfoText = (
|
|||
const enabledDayIndex = TASK_REPEAT_WEEKDAY_MAP.findIndex(
|
||||
(day) => repeatCfg[day],
|
||||
);
|
||||
const weekDayDate = new Date();
|
||||
weekDayDate.setDate(
|
||||
weekDayDate.getDate() + (enabledDayIndex - weekDayDate.getDay()),
|
||||
);
|
||||
const weekDayDate = new Date(Date.UTC(2026, 0, 4 + enabledDayIndex));
|
||||
const weekdayStr = weekDayDate.toLocaleDateString(locale, {
|
||||
weekday: 'short',
|
||||
timeZone: 'UTC',
|
||||
});
|
||||
return [
|
||||
timeStr
|
||||
|
|
@ -131,6 +132,51 @@ export const getTaskRepeatInfoText = (
|
|||
];
|
||||
|
||||
case 'MONTHLY':
|
||||
if (hasNthWeekdayAnchor(repeatCfg)) {
|
||||
const weekDayDate = new Date(Date.UTC(2026, 0, 4 + repeatCfg.monthlyWeekday));
|
||||
const weekdayStr = weekDayDate.toLocaleDateString(locale, {
|
||||
weekday: 'long',
|
||||
timeZone: 'UTC',
|
||||
});
|
||||
|
||||
let ordinalKey = '';
|
||||
if (repeatCfg.monthlyWeekOfMonth === -1) {
|
||||
ordinalKey = T.F.TASK_REPEAT.F.ORD_LAST_NTH;
|
||||
} else {
|
||||
const ordinalKeys = [
|
||||
T.F.TASK_REPEAT.F.ORD_FIRST_NTH,
|
||||
T.F.TASK_REPEAT.F.ORD_SECOND_NTH,
|
||||
T.F.TASK_REPEAT.F.ORD_THIRD_NTH,
|
||||
T.F.TASK_REPEAT.F.ORD_FOURTH_NTH,
|
||||
];
|
||||
ordinalKey = ordinalKeys[repeatCfg.monthlyWeekOfMonth - 1] || '';
|
||||
}
|
||||
|
||||
if (ordinalKey) {
|
||||
const ordinalStr = translateService.instant(ordinalKey);
|
||||
|
||||
return [
|
||||
timeStr
|
||||
? T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY_AND_TIME
|
||||
: T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY,
|
||||
{
|
||||
ordinalStr,
|
||||
weekdayStr,
|
||||
timeStr,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (repeatCfg.monthlyLastDay) {
|
||||
return [
|
||||
timeStr
|
||||
? T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_LAST_DAY_AND_TIME
|
||||
: T.F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_LAST_DAY,
|
||||
{ timeStr },
|
||||
];
|
||||
}
|
||||
|
||||
const dateDayStr = dateStrToUtcDate(
|
||||
getEffectiveRepeatStartDate(repeatCfg),
|
||||
).toLocaleDateString(locale, {
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ export class TaskDetailPanelComponent implements OnInit, AfterViewInit, OnDestro
|
|||
repeatCfg,
|
||||
this._dateTimeFormatService.currentLocale(),
|
||||
this._dateTimeFormatService,
|
||||
this._translateService,
|
||||
);
|
||||
return this._translateService.instant(key, params);
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ export class ScheduledListPageComponent {
|
|||
repeatCfg,
|
||||
this._dateTimeFormatService.currentLocale(),
|
||||
this._dateTimeFormatService,
|
||||
this._translateService,
|
||||
);
|
||||
return this._translateService.instant(key, params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1884,6 +1884,12 @@ const T = {
|
|||
MONTHLY_CURRENT_DATE: 'F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_CURRENT_DATE',
|
||||
MONTHLY_CURRENT_DATE_AND_TIME:
|
||||
'F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_CURRENT_DATE_AND_TIME',
|
||||
MONTHLY_LAST_DAY: 'F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_LAST_DAY',
|
||||
MONTHLY_LAST_DAY_AND_TIME:
|
||||
'F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_LAST_DAY_AND_TIME',
|
||||
MONTHLY_NTH_WEEKDAY: 'F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY',
|
||||
MONTHLY_NTH_WEEKDAY_AND_TIME:
|
||||
'F.TASK_REPEAT.ADD_INFO_PANEL.MONTHLY_NTH_WEEKDAY_AND_TIME',
|
||||
WEEKLY_CURRENT_WEEKDAY: 'F.TASK_REPEAT.ADD_INFO_PANEL.WEEKLY_CURRENT_WEEKDAY',
|
||||
WEEKLY_CURRENT_WEEKDAY_AND_TIME:
|
||||
'F.TASK_REPEAT.ADD_INFO_PANEL.WEEKLY_CURRENT_WEEKDAY_AND_TIME',
|
||||
|
|
@ -1934,10 +1940,15 @@ const T = {
|
|||
MONTHLY_MODE_DAY_OF_MONTH: 'F.TASK_REPEAT.F.MONTHLY_MODE_DAY_OF_MONTH',
|
||||
NOTES: 'F.TASK_REPEAT.F.NOTES',
|
||||
ORD_FIRST: 'F.TASK_REPEAT.F.ORD_FIRST',
|
||||
ORD_FIRST_NTH: 'F.TASK_REPEAT.F.ORD_FIRST_NTH',
|
||||
ORD_FOURTH: 'F.TASK_REPEAT.F.ORD_FOURTH',
|
||||
ORD_FOURTH_NTH: 'F.TASK_REPEAT.F.ORD_FOURTH_NTH',
|
||||
ORD_LAST: 'F.TASK_REPEAT.F.ORD_LAST',
|
||||
ORD_LAST_NTH: 'F.TASK_REPEAT.F.ORD_LAST_NTH',
|
||||
ORD_SECOND: 'F.TASK_REPEAT.F.ORD_SECOND',
|
||||
ORD_SECOND_NTH: 'F.TASK_REPEAT.F.ORD_SECOND_NTH',
|
||||
ORD_THIRD: 'F.TASK_REPEAT.F.ORD_THIRD',
|
||||
ORD_THIRD_NTH: 'F.TASK_REPEAT.F.ORD_THIRD_NTH',
|
||||
Q_CUSTOM: 'F.TASK_REPEAT.F.Q_CUSTOM',
|
||||
Q_DAILY: 'F.TASK_REPEAT.F.Q_DAILY',
|
||||
Q_MONDAY_TO_FRIDAY: 'F.TASK_REPEAT.F.Q_MONDAY_TO_FRIDAY',
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1837,6 +1837,10 @@
|
|||
"MONDAY_TO_FRIDAY_AND_TIME": "Mon-Fri, {{timeStr}}",
|
||||
"MONTHLY_CURRENT_DATE": "Monthly on day {{dateDayStr}}",
|
||||
"MONTHLY_CURRENT_DATE_AND_TIME": "Monthly on day {{dateDayStr}}, {{timeStr}}",
|
||||
"MONTHLY_LAST_DAY": "Monthly on the last day",
|
||||
"MONTHLY_LAST_DAY_AND_TIME": "Monthly on the last day, {{timeStr}}",
|
||||
"MONTHLY_NTH_WEEKDAY": "Monthly on the {{ordinalStr}} {{weekdayStr}}",
|
||||
"MONTHLY_NTH_WEEKDAY_AND_TIME": "Monthly on the {{ordinalStr}} {{weekdayStr}}, {{timeStr}}",
|
||||
"WEEKLY_CURRENT_WEEKDAY": "Weekly on {{weekdayStr}}",
|
||||
"WEEKLY_CURRENT_WEEKDAY_AND_TIME": "Weekly on {{weekdayStr}}, {{timeStr}}",
|
||||
"YEARLY_CURRENT_DATE": "Yearly on {{dayAndMonthStr}}",
|
||||
|
|
@ -1884,10 +1888,15 @@
|
|||
"MONTHLY_MODE_DAY_OF_MONTH": "Day of month",
|
||||
"NOTES": "Default notes",
|
||||
"ORD_FIRST": "first",
|
||||
"ORD_FIRST_NTH": "first",
|
||||
"ORD_FOURTH": "fourth",
|
||||
"ORD_FOURTH_NTH": "fourth",
|
||||
"ORD_LAST": "last",
|
||||
"ORD_LAST_NTH": "last",
|
||||
"ORD_SECOND": "second",
|
||||
"ORD_SECOND_NTH": "second",
|
||||
"ORD_THIRD": "third",
|
||||
"ORD_THIRD_NTH": "third",
|
||||
"Q_CUSTOM": "Custom recurring config",
|
||||
"Q_DAILY": "Every day",
|
||||
"Q_MONDAY_TO_FRIDAY": "Every Monday through Friday",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue