Skip to content

Commit

Permalink
feat: add locale option for nepali language support (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
aj3sh authored Dec 25, 2024
1 parent 8011f94 commit 982d80d
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 15 deletions.
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ function App() {
<div>Basic Nepali Date picker</div>
<NepaliDatePicker name="date1" />
<hr />
<div>Nepali Date picker (locale=ne)</div>
<NepaliDatePicker name="dateNe" locale="ne" />
<hr />
<div>Input onDateSelect, value: {inputValue}</div>
<NepaliDatePicker
value={inputValue}
Expand Down
2 changes: 1 addition & 1 deletion src/components/NepaliCalendar/NepaliCalendar.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
}

.calendarCellCurrent {
color: #9325b1;
color: #2096f5;
font-weight: bold;
}

Expand Down
26 changes: 21 additions & 5 deletions src/components/NepaliCalendar/NepaliCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,35 @@ import NepaliDate from 'nepali-datetime'

import YearSelector from '../core/YearSelector'
import MonthSelector from '../core/MonthSelector'
import { MAX_NEPALI_YEAR, MIN_NEPALI_YEAR, WEEK_DAYS_SHORT_EN } from '../../constants'
import {
DEFAULT_LOCALE,
LOCALE_NE,
MAX_NEPALI_YEAR,
MIN_NEPALI_YEAR,
WEEKDAYS_SHORT_EN,
WEEKDAYS_SHORT_NE,
} from '../../constants'
import { getCalendarTableArray, ICalendarDate } from '../../utils/calendar'
import classNames from '../../utils/classNames'
import type { Locale } from '../../types'

import styles from './NepaliCalendar.module.scss'
import { nepaliNumber } from '../../utils/nepaliNumber'

export interface INepaliCalendarProps
extends React.InputHTMLAttributes<HTMLInputElement> {
selectedNepaliDate?: NepaliDate | null
onDateSelect?: ((nepaliDate: NepaliDate) => void) | null
locale?: Locale
}

const NepaliCalendar: React.FC<INepaliCalendarProps> = ({
selectedNepaliDate,
onDateSelect = null,
locale = DEFAULT_LOCALE,
}) => {
const now = new NepaliDate()
const weekDays = locale === LOCALE_NE ? WEEKDAYS_SHORT_NE : WEEKDAYS_SHORT_EN

const [selectedYear, setSelectedYear] = useState(
selectedNepaliDate?.getYear() ?? now.getYear()
Expand Down Expand Up @@ -97,10 +109,12 @@ const NepaliCalendar: React.FC<INepaliCalendarProps> = ({
<YearSelector
selectedYear={selectedYear}
onChange={newYear => setSelectedYear(newYear)}
locale={locale}
/>
<MonthSelector
selectedMonth={selectedMonth}
onChange={newMonth => setSelectedMonth(newMonth)}
locale={locale}
/>
</div>
<div className={`ndt-next ${styles.prevNext}`}>
Expand All @@ -126,12 +140,12 @@ const NepaliCalendar: React.FC<INepaliCalendarProps> = ({
</div>
<div className={`ndt-calendar-container ${styles.calendarContainer}`}>
<div className={`ndt-calendar-grid ${styles.calendarGrid}`}>
{WEEK_DAYS_SHORT_EN.map(weekShortEn => (
{weekDays.map(weekShort => (
<div
key={weekShortEn}
key={weekShort}
className={classNames('ndt-calendar-header', styles.calendarCell)}
>
{weekShortEn}
{weekShort}
</div>
))}
</div>
Expand All @@ -150,7 +164,9 @@ const NepaliCalendar: React.FC<INepaliCalendarProps> = ({
!calendarDate.active && styles.calendarCellInactive
)}
>
{calendarDate.date}
{calendarDate.date && locale === LOCALE_NE
? nepaliNumber(calendarDate.date)
: calendarDate.date}
</div>
))}
</div>
Expand Down
19 changes: 15 additions & 4 deletions src/components/NepaliDatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@ import NepaliCalendar from './NepaliCalendar'
import Popover from './core/Popover'
import NepaliDate from 'nepali-datetime'
import PickerInput from './core/PickerInput'
import { DEFAULT_LOCALE, LOCALE_NE } from '../constants'
import type { Locale } from '../types'
import { englishNumber } from '../utils/nepaliNumber'

interface INepaliDatePickerProps extends React.InputHTMLAttributes<HTMLInputElement> {
value?: string
format?: string
inputElement?: ReactElement | null
locale?: Locale
onDateSelect?: ((value: string) => void) | null
}

const getNepaliDateOrNull = (
nepaliDateStr: string,
format: string
format: string,
locale: Locale
): NepaliDate | null => {
const value = locale === LOCALE_NE ? englishNumber(nepaliDateStr) : nepaliDateStr

try {
return new NepaliDate(nepaliDateStr, format)
return new NepaliDate(value, format)
} catch {
return null
}
Expand All @@ -27,6 +34,7 @@ const NepaliDatePicker: React.FC<INepaliDatePickerProps> = ({
format = 'YYYY-MM-DD',
inputElement = null,
onDateSelect = null,
locale = DEFAULT_LOCALE,
...rest
}) => {
const [isPopoverVisible, setIsPopoverVisible] = useState(false)
Expand All @@ -46,7 +54,9 @@ const NepaliDatePicker: React.FC<INepaliDatePickerProps> = ({
}

const handleSetDate = (nepaliDate: NepaliDate) => {
setDateValue(nepaliDate.format(format))
const inputValue =
locale === LOCALE_NE ? nepaliDate.formatNepali(format) : nepaliDate.format(format)
setDateValue(inputValue)
setIsPopoverVisible(false)
}

Expand All @@ -61,7 +71,8 @@ const NepaliDatePicker: React.FC<INepaliDatePickerProps> = ({
onOpenChange={newOpen => setIsPopoverVisible(newOpen)}
content={
<NepaliCalendar
selectedNepaliDate={getNepaliDateOrNull(inputValue, format)}
locale={locale}
selectedNepaliDate={getNepaliDateOrNull(inputValue, format, locale)}
onDateSelect={handleSetDate}
/>
}
Expand Down
15 changes: 13 additions & 2 deletions src/components/core/MonthSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import { NEPALI_MONTHS_EN } from '../../constants'
import {
DEFAULT_LOCALE,
LOCALE_NE,
NEPALI_MONTHS_EN,
NEPALI_MONTHS_NE,
} from '../../constants'
import { Locale } from '../../types'

interface IMonthSelectionProps {
selectedMonth: number
onChange?: (newMonth: number) => void
locale?: Locale
}

const getNepaliMonths = (locale: Locale) =>
locale === LOCALE_NE ? NEPALI_MONTHS_NE : NEPALI_MONTHS_EN

const MonthSelector: React.FC<IMonthSelectionProps> = ({
selectedMonth,
onChange: onMonthChange,
locale = DEFAULT_LOCALE,
}) => (
<select
value={selectedMonth}
onChange={e => onMonthChange && onMonthChange(Number(e.target.value))}
>
{NEPALI_MONTHS_EN.map((month, month0) => (
{getNepaliMonths(locale).map((month, month0) => (
<option key={month} value={month0}>
{month}
</option>
Expand Down
13 changes: 11 additions & 2 deletions src/components/core/YearSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { MAX_NEPALI_YEAR, MIN_NEPALI_YEAR } from '../../constants'
import {
DEFAULT_LOCALE,
LOCALE_NE,
MAX_NEPALI_YEAR,
MIN_NEPALI_YEAR,
} from '../../constants'
import { Locale } from '../../types'
import { nepaliNumber } from '../../utils/nepaliNumber'

const YEARS = Array.from(
{ length: MAX_NEPALI_YEAR - MIN_NEPALI_YEAR + 1 },
Expand All @@ -8,19 +15,21 @@ const YEARS = Array.from(
interface IYearSelectionProps {
selectedYear: number
onChange?: (newYear: number) => void
locale?: Locale
}

const YearSelector: React.FC<IYearSelectionProps> = ({
selectedYear,
onChange: onYearChange,
locale = DEFAULT_LOCALE,
}) => (
<select
value={selectedYear}
onChange={e => onYearChange && onYearChange(Number(e.target.value))}
>
{YEARS.map(year => (
<option key={year} value={year}>
{year}
{locale === LOCALE_NE ? nepaliNumber(year) : year}
</option>
))}
</select>
Expand Down
24 changes: 23 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import dateConverter from 'nepali-datetime/dateConverter'

export const NEPALI_NUMBERS = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९']

export const LOCALE_EN = 'en'
export const LOCALE_NE = 'ne'
export const DEFAULT_LOCALE = LOCALE_EN

export const MIN_NEPALI_YEAR = dateConverter.npMinYear()
export const MAX_NEPALI_YEAR = dateConverter.npMaxYear()

Expand Down Expand Up @@ -123,4 +129,20 @@ export const NEPALI_MONTHS_EN = [
'Chaitra',
]

export const WEEK_DAYS_SHORT_EN = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
export const NEPALI_MONTHS_NE = [
'बैशाख',
'जेठ',
'असार',
'श्रावण',
'भाद्र',
'आश्विन',
'कार्तिक',
'मंसिर',
'पौष',
'माघ',
'फाल्गुण',
'चैत्र',
]

export const WEEKDAYS_SHORT_EN = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
export const WEEKDAYS_SHORT_NE = ['आइत', 'सोम', 'मंगल', 'बुध', 'बिहि', 'शुक्र', 'शनि']
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { LOCALE_EN, LOCALE_NE } from './constants'

export type Locale = typeof LOCALE_EN | typeof LOCALE_NE
17 changes: 17 additions & 0 deletions src/utils/nepaliNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NEPALI_NUMBERS } from '../constants'

export const nepaliNumber = (num: number | string) => {
return num
.toString()
.split('')
.map(digit => (!isNaN(Number(digit)) ? NEPALI_NUMBERS[Number(digit)] : digit))
.join('')
}

export const englishNumber = (num: number | string) => {
return num
.toString()
.split('')
.map(char => (NEPALI_NUMBERS.includes(char) ? NEPALI_NUMBERS.indexOf(char) : char))
.join('')
}

0 comments on commit 982d80d

Please sign in to comment.