@nepdatejs/core 0.0.0-bootstrap → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +134 -1
- package/dist/data.cjs +3 -0
- package/dist/data.d.cts +16 -0
- package/dist/data.d.ts +16 -0
- package/dist/data.js +1 -0
- package/dist/index.cjs +1328 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +433 -0
- package/dist/index.d.ts +433 -0
- package/dist/index.js +1241 -0
- package/dist/index.js.map +1 -0
- package/dist/nepdate.global.js +2 -0
- package/dist/nepdate.global.js.map +1 -0
- package/dist/years-CabTFbN9.js +1832 -0
- package/dist/years-CabTFbN9.js.map +1 -0
- package/dist/years-J6lZENDt.cjs +1837 -0
- package/dist/years-J6lZENDt.cjs.map +1 -0
- package/package.json +77 -4
- package/src/ad/calendar.ts +71 -0
- package/src/arithmetic.ts +123 -0
- package/src/bs/data/years.ts +152 -0
- package/src/bs/data-api.ts +112 -0
- package/src/bs/table.ts +154 -0
- package/src/clock.ts +33 -0
- package/src/compare.ts +73 -0
- package/src/config.ts +85 -0
- package/src/convert.ts +188 -0
- package/src/create.ts +112 -0
- package/src/data.ts +2 -0
- package/src/epoch.ts +19 -0
- package/src/errors.ts +43 -0
- package/src/fiscal.ts +50 -0
- package/src/format.ts +88 -0
- package/src/index.ts +126 -0
- package/src/locale/en.ts +93 -0
- package/src/locale/index.ts +49 -0
- package/src/locale/ne.ts +93 -0
- package/src/locale/registry.ts +114 -0
- package/src/parse.ts +67 -0
- package/src/projected.ts +8 -0
- package/src/time.ts +186 -0
- package/src/types.ts +61 -0
package/src/errors.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type NepDateRangeErrorCode =
|
|
2
|
+
/** The year, month or day does not exist in that calendar. */
|
|
3
|
+
| 'INVALID_DATE'
|
|
4
|
+
/** The date is outside the BS table, so it cannot be a BS date or be converted. */
|
|
5
|
+
| 'OUT_OF_TABLE'
|
|
6
|
+
/** A value of the wrong kind: not an integer, unknown time zone, bad text, … */
|
|
7
|
+
| 'INVALID_ARGUMENT';
|
|
8
|
+
|
|
9
|
+
/** Thrown for any invalid date or argument. Check `code`, not the message. */
|
|
10
|
+
export class NepDateRangeError extends RangeError {
|
|
11
|
+
readonly code: NepDateRangeErrorCode;
|
|
12
|
+
|
|
13
|
+
constructor(code: NepDateRangeErrorCode, message: string) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'NepDateRangeError';
|
|
16
|
+
this.code = code;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type NepDateDataErrorCode =
|
|
21
|
+
'EMPTY' | 'BAD_MONTH_COUNT' | 'BAD_MONTH' | 'BAD_YEAR_TOTAL' | 'BAD_ANCHOR';
|
|
22
|
+
|
|
23
|
+
/** Thrown when a BS table passed to `setBsData` or `patchBsYear` is not valid. */
|
|
24
|
+
export class NepDateDataError extends Error {
|
|
25
|
+
readonly code: NepDateDataErrorCode;
|
|
26
|
+
/** The BS year at fault, when there is one. */
|
|
27
|
+
readonly year: number | undefined;
|
|
28
|
+
/** The month (1–12) at fault, when there is one. */
|
|
29
|
+
readonly month: number | undefined;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
code: NepDateDataErrorCode,
|
|
33
|
+
message: string,
|
|
34
|
+
year?: number,
|
|
35
|
+
month?: number,
|
|
36
|
+
) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'NepDateDataError';
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.year = year;
|
|
41
|
+
this.month = month;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/fiscal.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { BS_FISCAL_YEAR_START_MONTH } from './bs/data-api';
|
|
2
|
+
import { compareDates } from './compare';
|
|
3
|
+
import { toCalendar } from './convert';
|
|
4
|
+
import { bsDate, daysInMonth } from './create';
|
|
5
|
+
import { formatNumber } from './locale/index';
|
|
6
|
+
import type { CalendarDate, FiscalYear, LocaleCode } from './types';
|
|
7
|
+
|
|
8
|
+
/** The fiscal year a date falls in: 1 Shrawan to the end of Asar. */
|
|
9
|
+
export function fiscalYear(date: CalendarDate): FiscalYear {
|
|
10
|
+
const bs = toCalendar(date, 'BS');
|
|
11
|
+
|
|
12
|
+
return fiscalYearRange(bs.month >= BS_FISCAL_YEAR_START_MONTH ? bs.year : bs.year - 1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The fiscal year that starts in BS `startYear` (2083 → FY 2083/84). */
|
|
16
|
+
export function fiscalYearRange(startYear: number): FiscalYear {
|
|
17
|
+
const endYear = startYear + 1;
|
|
18
|
+
const lastMonth = BS_FISCAL_YEAR_START_MONTH - 1;
|
|
19
|
+
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
startYear,
|
|
22
|
+
label: `${startYear}/${String(endYear % 100).padStart(2, '0')}`,
|
|
23
|
+
firstDay: bsDate(startYear, BS_FISCAL_YEAR_START_MONTH, 1),
|
|
24
|
+
lastDay: bsDate(endYear, lastMonth, daysInMonth('BS', endYear, lastMonth)),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* label "2083/84" · full "2083/2084" · cbms "2083.084" (the form Nepal's tax
|
|
30
|
+
* system uses). Digits follow the locale.
|
|
31
|
+
*/
|
|
32
|
+
export function formatFiscalYear(
|
|
33
|
+
fy: FiscalYear,
|
|
34
|
+
style: 'label' | 'full' | 'cbms' = 'label',
|
|
35
|
+
locale?: LocaleCode,
|
|
36
|
+
): string {
|
|
37
|
+
const end = fy.startYear + 1;
|
|
38
|
+
const text =
|
|
39
|
+
style === 'full'
|
|
40
|
+
? `${fy.startYear}/${end}`
|
|
41
|
+
: style === 'cbms'
|
|
42
|
+
? `${fy.startYear}.${String(end % 1000).padStart(3, '0')}`
|
|
43
|
+
: fy.label;
|
|
44
|
+
|
|
45
|
+
return formatNumber(text, locale);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isInFiscalYear(date: CalendarDate, fy: FiscalYear): boolean {
|
|
49
|
+
return compareDates(date, fy.firstDay) >= 0 && compareDates(date, fy.lastDay) <= 0;
|
|
50
|
+
}
|
package/src/format.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { dayOfWeek } from './compare';
|
|
2
|
+
import { formatNumber, getLocale } from './locale/index';
|
|
3
|
+
import type { CalendarDate, LocaleCode } from './types';
|
|
4
|
+
|
|
5
|
+
/** Named formats. `cbms` is the dotted form Nepal's tax system uses. */
|
|
6
|
+
export const DATE_FORMATS: {
|
|
7
|
+
readonly iso: 'Y-m-d';
|
|
8
|
+
readonly cbms: 'Y.m.d';
|
|
9
|
+
readonly short: 'j M Y';
|
|
10
|
+
readonly long: 'j F Y';
|
|
11
|
+
readonly full: 'l, j F Y';
|
|
12
|
+
} = Object.freeze({
|
|
13
|
+
iso: 'Y-m-d',
|
|
14
|
+
cbms: 'Y.m.d',
|
|
15
|
+
short: 'j M Y',
|
|
16
|
+
long: 'j F Y',
|
|
17
|
+
full: 'l, j F Y',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type NamedFormat = keyof typeof DATE_FORMATS;
|
|
21
|
+
/** A named format, or a pattern of tokens (see formatDate). */
|
|
22
|
+
export type DateFormatPattern = NamedFormat | (string & Record<never, never>);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Format a date in its own calendar. Tokens: Y 2083, y 83, m 06, n 6, d 09,
|
|
26
|
+
* j 9, F month name, M short month name, l weekday, D short weekday. Put a
|
|
27
|
+
* backslash before a letter to print it as is. Digits follow the locale.
|
|
28
|
+
*
|
|
29
|
+
* formatDate(bsDate(2082, 4, 1), 'full') → "Thursday, 1 Shrawan 2082"
|
|
30
|
+
* formatDate(bsDate(2082, 4, 1), 'long', 'ne') → "१ साउन २०८२"
|
|
31
|
+
*/
|
|
32
|
+
export function formatDate(
|
|
33
|
+
date: CalendarDate,
|
|
34
|
+
pattern: DateFormatPattern = 'iso',
|
|
35
|
+
locale?: LocaleCode,
|
|
36
|
+
): string {
|
|
37
|
+
const tokens: string =
|
|
38
|
+
pattern in DATE_FORMATS ? DATE_FORMATS[pattern as NamedFormat] : pattern;
|
|
39
|
+
const data = getLocale(locale);
|
|
40
|
+
const months = data.months[date.calendar];
|
|
41
|
+
const digits = (value: string | number): string => formatNumber(value, locale);
|
|
42
|
+
const pad = (value: number): string => String(value).padStart(2, '0');
|
|
43
|
+
let output = '';
|
|
44
|
+
|
|
45
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
46
|
+
const token = tokens.charAt(index);
|
|
47
|
+
switch (token) {
|
|
48
|
+
case '\\':
|
|
49
|
+
index++;
|
|
50
|
+
output += tokens.charAt(index);
|
|
51
|
+
break;
|
|
52
|
+
case 'Y':
|
|
53
|
+
output += digits(date.year);
|
|
54
|
+
break;
|
|
55
|
+
case 'y':
|
|
56
|
+
output += digits(pad(((date.year % 100) + 100) % 100));
|
|
57
|
+
break;
|
|
58
|
+
case 'm':
|
|
59
|
+
output += digits(pad(date.month));
|
|
60
|
+
break;
|
|
61
|
+
case 'n':
|
|
62
|
+
output += digits(date.month);
|
|
63
|
+
break;
|
|
64
|
+
case 'd':
|
|
65
|
+
output += digits(pad(date.day));
|
|
66
|
+
break;
|
|
67
|
+
case 'j':
|
|
68
|
+
output += digits(date.day);
|
|
69
|
+
break;
|
|
70
|
+
case 'F':
|
|
71
|
+
output += months.long[date.month - 1] ?? '';
|
|
72
|
+
break;
|
|
73
|
+
case 'M':
|
|
74
|
+
output += months.short[date.month - 1] ?? '';
|
|
75
|
+
break;
|
|
76
|
+
case 'l':
|
|
77
|
+
output += data.weekdays.long[dayOfWeek(date)] ?? '';
|
|
78
|
+
break;
|
|
79
|
+
case 'D':
|
|
80
|
+
output += data.weekdays.short[dayOfWeek(date)] ?? '';
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
output += token;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return output;
|
|
88
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nepdatejs/core: the source of truth for Bikram Sambat (BS) and AD dates.
|
|
3
|
+
* No package dependencies; built-ins only (docs/plan D-004).
|
|
4
|
+
*/
|
|
5
|
+
export type {
|
|
6
|
+
AdDate,
|
|
7
|
+
BsData,
|
|
8
|
+
BsDate,
|
|
9
|
+
CalendarDate,
|
|
10
|
+
CalendarSystem,
|
|
11
|
+
DateOf,
|
|
12
|
+
EpochDay,
|
|
13
|
+
FiscalYear,
|
|
14
|
+
LocaleCode,
|
|
15
|
+
MonthNumber,
|
|
16
|
+
Weekday,
|
|
17
|
+
} from './types';
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
NepDateDataError,
|
|
21
|
+
NepDateRangeError,
|
|
22
|
+
type NepDateDataErrorCode,
|
|
23
|
+
type NepDateRangeErrorCode,
|
|
24
|
+
} from './errors';
|
|
25
|
+
|
|
26
|
+
export { toEpochDay, weekdayOf } from './epoch';
|
|
27
|
+
export { configure, getConfig, resetConfig, type NepDateConfig } from './config';
|
|
28
|
+
|
|
29
|
+
export { isAdLeapYear, MAX_AD_YEAR } from './ad/calendar';
|
|
30
|
+
export { bsFirstYear, bsLastYear } from './bs/table';
|
|
31
|
+
export {
|
|
32
|
+
BS_FISCAL_YEAR_START_MONTH,
|
|
33
|
+
getBsData,
|
|
34
|
+
patchBsYear,
|
|
35
|
+
resetBsData,
|
|
36
|
+
setBsData,
|
|
37
|
+
validateBsData,
|
|
38
|
+
} from './bs/data-api';
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
adDate,
|
|
42
|
+
bsDate,
|
|
43
|
+
createDate,
|
|
44
|
+
daysInMonth,
|
|
45
|
+
isAdDate,
|
|
46
|
+
isBsDate,
|
|
47
|
+
isCalendarDate,
|
|
48
|
+
isValidDate,
|
|
49
|
+
} from './create';
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
adToBs,
|
|
53
|
+
bsToAd,
|
|
54
|
+
fromEpoch,
|
|
55
|
+
fromIsoDate,
|
|
56
|
+
fromIsoTimestamp,
|
|
57
|
+
fromJsDate,
|
|
58
|
+
isConvertible,
|
|
59
|
+
maxDate,
|
|
60
|
+
minDate,
|
|
61
|
+
toCalendar,
|
|
62
|
+
toEpoch,
|
|
63
|
+
toIsoDate,
|
|
64
|
+
} from './convert';
|
|
65
|
+
export { isProjected } from './projected';
|
|
66
|
+
export { now, today } from './clock';
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
addDays,
|
|
70
|
+
addMonths,
|
|
71
|
+
addYears,
|
|
72
|
+
differenceInDays,
|
|
73
|
+
eachDayOfInterval,
|
|
74
|
+
eachDayOfMonth,
|
|
75
|
+
endOfMonth,
|
|
76
|
+
endOfWeek,
|
|
77
|
+
endOfYear,
|
|
78
|
+
startOfMonth,
|
|
79
|
+
startOfWeek,
|
|
80
|
+
startOfYear,
|
|
81
|
+
} from './arithmetic';
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
clampDate,
|
|
85
|
+
compareDates,
|
|
86
|
+
dayOfWeek,
|
|
87
|
+
isAfter,
|
|
88
|
+
isBefore,
|
|
89
|
+
isSameDay,
|
|
90
|
+
isSameMonth,
|
|
91
|
+
isSameYear,
|
|
92
|
+
isWeekend,
|
|
93
|
+
isWithin,
|
|
94
|
+
} from './compare';
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
formatMessage,
|
|
98
|
+
formatNumber,
|
|
99
|
+
getLocale,
|
|
100
|
+
hasLocale,
|
|
101
|
+
monthNames,
|
|
102
|
+
registerLocale,
|
|
103
|
+
toAsciiDigits,
|
|
104
|
+
toDevanagariDigits,
|
|
105
|
+
weekdayNames,
|
|
106
|
+
type LocaleData,
|
|
107
|
+
} from './locale/index';
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
DATE_FORMATS,
|
|
111
|
+
formatDate,
|
|
112
|
+
type DateFormatPattern,
|
|
113
|
+
type NamedFormat,
|
|
114
|
+
} from './format';
|
|
115
|
+
export { parseDate, parseDateStrict, parseFiscalYear } from './parse';
|
|
116
|
+
export { fiscalYear, fiscalYearRange, formatFiscalYear, isInFiscalYear } from './fiscal';
|
|
117
|
+
export {
|
|
118
|
+
compareTimes,
|
|
119
|
+
formatTime,
|
|
120
|
+
fromIsoDateTime,
|
|
121
|
+
isValidTime,
|
|
122
|
+
parseTime,
|
|
123
|
+
timeOfDay,
|
|
124
|
+
toIsoDateTime,
|
|
125
|
+
type TimeOfDay,
|
|
126
|
+
} from './time';
|
package/src/locale/en.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { LocaleData } from './registry';
|
|
2
|
+
|
|
3
|
+
const bsMonths = [
|
|
4
|
+
'Baisakh',
|
|
5
|
+
'Jestha',
|
|
6
|
+
'Asar',
|
|
7
|
+
'Shrawan',
|
|
8
|
+
'Bhadra',
|
|
9
|
+
'Ashwin',
|
|
10
|
+
'Kartik',
|
|
11
|
+
'Mangsir',
|
|
12
|
+
'Poush',
|
|
13
|
+
'Magh',
|
|
14
|
+
'Falgun',
|
|
15
|
+
'Chaitra',
|
|
16
|
+
];
|
|
17
|
+
const adMonths = [
|
|
18
|
+
'January',
|
|
19
|
+
'February',
|
|
20
|
+
'March',
|
|
21
|
+
'April',
|
|
22
|
+
'May',
|
|
23
|
+
'June',
|
|
24
|
+
'July',
|
|
25
|
+
'August',
|
|
26
|
+
'September',
|
|
27
|
+
'October',
|
|
28
|
+
'November',
|
|
29
|
+
'December',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export const en: LocaleData = {
|
|
33
|
+
digits: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
|
34
|
+
weekdays: {
|
|
35
|
+
long: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
|
36
|
+
short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
|
37
|
+
narrow: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
|
|
38
|
+
},
|
|
39
|
+
months: {
|
|
40
|
+
AD: { long: adMonths, short: adMonths.map((name) => name.slice(0, 3)) },
|
|
41
|
+
BS: { long: bsMonths, short: bsMonths.map((name) => name.slice(0, 3)) },
|
|
42
|
+
},
|
|
43
|
+
calendarNames: { AD: 'AD', BS: 'BS' },
|
|
44
|
+
labels: {
|
|
45
|
+
today: 'Today',
|
|
46
|
+
clear: 'Clear date',
|
|
47
|
+
previousMonth: 'Previous month',
|
|
48
|
+
nextMonth: 'Next month',
|
|
49
|
+
previousYear: 'Previous year',
|
|
50
|
+
nextYear: 'Next year',
|
|
51
|
+
month: 'Month',
|
|
52
|
+
year: 'Year',
|
|
53
|
+
openCalendar: 'Open calendar',
|
|
54
|
+
chooseDate: 'Choose a date',
|
|
55
|
+
calendar: 'Calendar',
|
|
56
|
+
presets: 'Shortcuts',
|
|
57
|
+
fiscalYear: 'Fiscal year',
|
|
58
|
+
time: 'Time',
|
|
59
|
+
hour: 'Hour',
|
|
60
|
+
minute: 'Minute',
|
|
61
|
+
now: 'Now',
|
|
62
|
+
done: 'Done',
|
|
63
|
+
},
|
|
64
|
+
time: { am: 'AM', pm: 'PM' },
|
|
65
|
+
presets: {
|
|
66
|
+
today: 'Today',
|
|
67
|
+
yesterday: 'Yesterday',
|
|
68
|
+
tomorrow: 'Tomorrow',
|
|
69
|
+
thisWeek: 'This week',
|
|
70
|
+
lastWeek: 'Last week',
|
|
71
|
+
last7Days: 'Last 7 days',
|
|
72
|
+
last30Days: 'Last 30 days',
|
|
73
|
+
thisMonth: 'This month',
|
|
74
|
+
lastMonth: 'Last month',
|
|
75
|
+
thisYear: 'This year',
|
|
76
|
+
thisFiscalYear: 'This fiscal year',
|
|
77
|
+
lastFiscalYear: 'Last fiscal year',
|
|
78
|
+
},
|
|
79
|
+
messages: {
|
|
80
|
+
invalidFormat: {
|
|
81
|
+
AD: 'Enter an AD date as YYYY-MM-DD.',
|
|
82
|
+
BS: 'Enter a BS date as YYYY-MM-DD.',
|
|
83
|
+
},
|
|
84
|
+
outOfRange: 'Choose a date from {min} to {max}.',
|
|
85
|
+
onOrAfter: 'Choose {min} or later.',
|
|
86
|
+
onOrBefore: 'Choose {max} or earlier.',
|
|
87
|
+
unavailable: 'This date is not available.',
|
|
88
|
+
rangeTooShort: 'Choose at least {min} days.',
|
|
89
|
+
rangeTooLong: 'Choose at most {max} days.',
|
|
90
|
+
tooManyDates: 'Choose at most {max} dates.',
|
|
91
|
+
invalidTime: 'Enter a time as HH:MM.',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getConfig } from '../config';
|
|
2
|
+
import type { CalendarSystem, LocaleCode } from '../types';
|
|
3
|
+
import { localeData, type LocaleData } from './registry';
|
|
4
|
+
|
|
5
|
+
export { hasLocale, registerLocale, type LocaleData } from './registry';
|
|
6
|
+
|
|
7
|
+
const DEVANAGARI = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९'];
|
|
8
|
+
|
|
9
|
+
/** The data for a locale (the configured default when omitted; English when unknown). */
|
|
10
|
+
export function getLocale(locale: LocaleCode = getConfig().defaultLocale): LocaleData {
|
|
11
|
+
return localeData(locale);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function monthNames(
|
|
15
|
+
calendar: CalendarSystem,
|
|
16
|
+
locale?: LocaleCode,
|
|
17
|
+
width: 'long' | 'short' = 'long',
|
|
18
|
+
): readonly string[] {
|
|
19
|
+
return getLocale(locale).months[calendar][width];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function weekdayNames(
|
|
23
|
+
locale?: LocaleCode,
|
|
24
|
+
width: 'long' | 'short' | 'narrow' = 'long',
|
|
25
|
+
): readonly string[] {
|
|
26
|
+
return getLocale(locale).weekdays[width];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Replace Devanagari digits with ASCII digits. */
|
|
30
|
+
export function toAsciiDigits(value: string): string {
|
|
31
|
+
return value.replace(/[०-९]/g, (digit) => String(DEVANAGARI.indexOf(digit)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Replace ASCII digits with Devanagari digits. */
|
|
35
|
+
export function toDevanagariDigits(value: string | number): string {
|
|
36
|
+
return String(value).replace(/[0-9]/g, (digit) => DEVANAGARI[Number(digit)] ?? digit);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Write a number (or digit string) with the locale's digits. */
|
|
40
|
+
export function formatNumber(value: string | number, locale?: LocaleCode): string {
|
|
41
|
+
const { digits } = getLocale(locale);
|
|
42
|
+
|
|
43
|
+
return String(value).replace(/[0-9]/g, (digit) => digits[Number(digit)] ?? digit);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Fill {name} placeholders in a locale message. */
|
|
47
|
+
export function formatMessage(template: string, values: Record<string, string>): string {
|
|
48
|
+
return template.replace(/\{(\w+)\}/g, (whole, name: string) => values[name] ?? whole);
|
|
49
|
+
}
|
package/src/locale/ne.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { LocaleData } from './registry';
|
|
2
|
+
|
|
3
|
+
const bsMonths = [
|
|
4
|
+
'बैशाख',
|
|
5
|
+
'जेठ',
|
|
6
|
+
'असार',
|
|
7
|
+
'साउन',
|
|
8
|
+
'भदौ',
|
|
9
|
+
'असोज',
|
|
10
|
+
'कार्तिक',
|
|
11
|
+
'मङ्सिर',
|
|
12
|
+
'पुस',
|
|
13
|
+
'माघ',
|
|
14
|
+
'फाल्गुण',
|
|
15
|
+
'चैत',
|
|
16
|
+
];
|
|
17
|
+
const adMonths = [
|
|
18
|
+
'जनवरी',
|
|
19
|
+
'फेब्रुअरी',
|
|
20
|
+
'मार्च',
|
|
21
|
+
'अप्रिल',
|
|
22
|
+
'मे',
|
|
23
|
+
'जुन',
|
|
24
|
+
'जुलाई',
|
|
25
|
+
'अगस्ट',
|
|
26
|
+
'सेप्टेम्बर',
|
|
27
|
+
'अक्टोबर',
|
|
28
|
+
'नोभेम्बर',
|
|
29
|
+
'डिसेम्बर',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export const ne: LocaleData = {
|
|
33
|
+
digits: ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९'],
|
|
34
|
+
weekdays: {
|
|
35
|
+
long: ['आइतबार', 'सोमबार', 'मङ्गलबार', 'बुधबार', 'बिहिबार', 'शुक्रबार', 'शनिबार'],
|
|
36
|
+
short: ['आइत', 'सोम', 'मङ्गल', 'बुध', 'बिही', 'शुक्र', 'शनि'],
|
|
37
|
+
narrow: ['आ', 'सो', 'मं', 'बु', 'बि', 'शु', 'श'],
|
|
38
|
+
},
|
|
39
|
+
months: {
|
|
40
|
+
AD: { long: adMonths, short: adMonths },
|
|
41
|
+
BS: { long: bsMonths, short: bsMonths },
|
|
42
|
+
},
|
|
43
|
+
calendarNames: { AD: 'ई.सं.', BS: 'वि.सं.' },
|
|
44
|
+
labels: {
|
|
45
|
+
today: 'आज',
|
|
46
|
+
clear: 'मिति हटाउनुहोस्',
|
|
47
|
+
previousMonth: 'अघिल्लो महिना',
|
|
48
|
+
nextMonth: 'अर्को महिना',
|
|
49
|
+
previousYear: 'अघिल्लो वर्ष',
|
|
50
|
+
nextYear: 'अर्को वर्ष',
|
|
51
|
+
month: 'महिना',
|
|
52
|
+
year: 'वर्ष',
|
|
53
|
+
openCalendar: 'पात्रो खोल्नुहोस्',
|
|
54
|
+
chooseDate: 'मिति छान्नुहोस्',
|
|
55
|
+
calendar: 'पात्रो',
|
|
56
|
+
presets: 'छिटो छनोट',
|
|
57
|
+
fiscalYear: 'आर्थिक वर्ष',
|
|
58
|
+
time: 'समय',
|
|
59
|
+
hour: 'घण्टा',
|
|
60
|
+
minute: 'मिनेट',
|
|
61
|
+
now: 'अहिले',
|
|
62
|
+
done: 'भयो',
|
|
63
|
+
},
|
|
64
|
+
time: { am: 'पूर्वाह्न', pm: 'अपराह्न' },
|
|
65
|
+
presets: {
|
|
66
|
+
today: 'आज',
|
|
67
|
+
yesterday: 'हिजो',
|
|
68
|
+
tomorrow: 'भोलि',
|
|
69
|
+
thisWeek: 'यो हप्ता',
|
|
70
|
+
lastWeek: 'गत हप्ता',
|
|
71
|
+
last7Days: 'पछिल्लो ७ दिन',
|
|
72
|
+
last30Days: 'पछिल्लो ३० दिन',
|
|
73
|
+
thisMonth: 'यो महिना',
|
|
74
|
+
lastMonth: 'गत महिना',
|
|
75
|
+
thisYear: 'यो वर्ष',
|
|
76
|
+
thisFiscalYear: 'यो आर्थिक वर्ष',
|
|
77
|
+
lastFiscalYear: 'गत आर्थिक वर्ष',
|
|
78
|
+
},
|
|
79
|
+
messages: {
|
|
80
|
+
invalidFormat: {
|
|
81
|
+
AD: 'ई.सं. मिति YYYY-MM-DD ढाँचामा लेख्नुहोस्।',
|
|
82
|
+
BS: 'वि.सं. मिति YYYY-MM-DD ढाँचामा लेख्नुहोस्।',
|
|
83
|
+
},
|
|
84
|
+
outOfRange: '{min} देखि {max} सम्मको मिति छान्नुहोस्।',
|
|
85
|
+
onOrAfter: '{min} वा त्यसपछिको मिति छान्नुहोस्।',
|
|
86
|
+
onOrBefore: '{max} वा त्यसअघिको मिति छान्नुहोस्।',
|
|
87
|
+
unavailable: 'यो मिति उपलब्ध छैन।',
|
|
88
|
+
rangeTooShort: 'कम्तीमा {min} दिन छान्नुहोस्।',
|
|
89
|
+
rangeTooLong: 'बढीमा {max} दिन छान्नुहोस्।',
|
|
90
|
+
tooManyDates: 'बढीमा {max} मिति छान्नुहोस्।',
|
|
91
|
+
invalidTime: 'समय HH:MM ढाँचामा लेख्नुहोस्।',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { NepDateRangeError } from '../errors';
|
|
2
|
+
import type { LocaleCode } from '../types';
|
|
3
|
+
import { en } from './en';
|
|
4
|
+
import { ne } from './ne';
|
|
5
|
+
|
|
6
|
+
type Names12 = readonly string[];
|
|
7
|
+
|
|
8
|
+
/** Everything a language needs: digits, names, UI labels and validation messages. */
|
|
9
|
+
export interface LocaleData {
|
|
10
|
+
/** Ten digit characters, 0–9. */
|
|
11
|
+
readonly digits: readonly string[];
|
|
12
|
+
/** Seven names each, Sunday first. */
|
|
13
|
+
readonly weekdays: {
|
|
14
|
+
readonly long: readonly string[];
|
|
15
|
+
readonly short: readonly string[];
|
|
16
|
+
readonly narrow: readonly string[];
|
|
17
|
+
};
|
|
18
|
+
/** Twelve names each, first month first. */
|
|
19
|
+
readonly months: {
|
|
20
|
+
readonly AD: { readonly long: Names12; readonly short: Names12 };
|
|
21
|
+
readonly BS: { readonly long: Names12; readonly short: Names12 };
|
|
22
|
+
};
|
|
23
|
+
/** How each calendar is named in this language: "BS" / "वि.सं.". */
|
|
24
|
+
readonly calendarNames: { readonly AD: string; readonly BS: string };
|
|
25
|
+
readonly labels: {
|
|
26
|
+
readonly today: string;
|
|
27
|
+
readonly clear: string;
|
|
28
|
+
readonly previousMonth: string;
|
|
29
|
+
readonly nextMonth: string;
|
|
30
|
+
readonly previousYear: string;
|
|
31
|
+
readonly nextYear: string;
|
|
32
|
+
readonly month: string;
|
|
33
|
+
readonly year: string;
|
|
34
|
+
readonly openCalendar: string;
|
|
35
|
+
readonly chooseDate: string;
|
|
36
|
+
readonly calendar: string;
|
|
37
|
+
/** Picker shortcuts (presets) and the name of their list. */
|
|
38
|
+
readonly presets: string;
|
|
39
|
+
readonly fiscalYear: string;
|
|
40
|
+
readonly time: string;
|
|
41
|
+
readonly hour: string;
|
|
42
|
+
readonly minute: string;
|
|
43
|
+
readonly now: string;
|
|
44
|
+
readonly done: string;
|
|
45
|
+
};
|
|
46
|
+
/** Words for 12-hour times. */
|
|
47
|
+
readonly time: { readonly am: string; readonly pm: string };
|
|
48
|
+
/** Names of the built-in picker presets. */
|
|
49
|
+
readonly presets: {
|
|
50
|
+
readonly today: string;
|
|
51
|
+
readonly yesterday: string;
|
|
52
|
+
readonly tomorrow: string;
|
|
53
|
+
readonly thisWeek: string;
|
|
54
|
+
readonly lastWeek: string;
|
|
55
|
+
readonly last7Days: string;
|
|
56
|
+
readonly last30Days: string;
|
|
57
|
+
readonly thisMonth: string;
|
|
58
|
+
readonly lastMonth: string;
|
|
59
|
+
readonly thisYear: string;
|
|
60
|
+
readonly thisFiscalYear: string;
|
|
61
|
+
readonly lastFiscalYear: string;
|
|
62
|
+
};
|
|
63
|
+
/** Messages may use {min} and {max} placeholders. */
|
|
64
|
+
readonly messages: {
|
|
65
|
+
readonly invalidFormat: { readonly AD: string; readonly BS: string };
|
|
66
|
+
readonly outOfRange: string;
|
|
67
|
+
readonly onOrAfter: string;
|
|
68
|
+
readonly onOrBefore: string;
|
|
69
|
+
readonly unavailable: string;
|
|
70
|
+
/** Range pickers: {min} / {max} are day counts. */
|
|
71
|
+
readonly rangeTooShort: string;
|
|
72
|
+
readonly rangeTooLong: string;
|
|
73
|
+
/** Multiple-date pickers: {max} is a count of dates. */
|
|
74
|
+
readonly tooManyDates: string;
|
|
75
|
+
readonly invalidTime: string;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const locales = new Map<string, LocaleData>([
|
|
80
|
+
['en', en],
|
|
81
|
+
['ne', ne],
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
/** Add or replace a language. Lists must have the right lengths (10 digits, 7 weekdays, 12 months). */
|
|
85
|
+
export function registerLocale(code: LocaleCode, data: LocaleData): void {
|
|
86
|
+
const checks: [string, readonly string[], number][] = [
|
|
87
|
+
['digits', data.digits, 10],
|
|
88
|
+
['weekdays.long', data.weekdays.long, 7],
|
|
89
|
+
['weekdays.short', data.weekdays.short, 7],
|
|
90
|
+
['weekdays.narrow', data.weekdays.narrow, 7],
|
|
91
|
+
['months.AD.long', data.months.AD.long, 12],
|
|
92
|
+
['months.AD.short', data.months.AD.short, 12],
|
|
93
|
+
['months.BS.long', data.months.BS.long, 12],
|
|
94
|
+
['months.BS.short', data.months.BS.short, 12],
|
|
95
|
+
];
|
|
96
|
+
for (const [name, list, length] of checks) {
|
|
97
|
+
if (list.length !== length) {
|
|
98
|
+
throw new NepDateRangeError(
|
|
99
|
+
'INVALID_ARGUMENT',
|
|
100
|
+
`Locale "${code}": ${name} must have ${length} entries, not ${list.length}.`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
locales.set(code, data);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function hasLocale(code: LocaleCode): boolean {
|
|
108
|
+
return locales.has(code);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The data for a locale, or English when the locale is not registered. */
|
|
112
|
+
export function localeData(code: LocaleCode): LocaleData {
|
|
113
|
+
return locales.get(code) ?? en;
|
|
114
|
+
}
|