fix(locale): dayjs related i18n issue (#2678)

- Fix dayjs related i18n issue.
This commit is contained in:
jeremywu
2021-07-26 10:51:32 +08:00
committed by GitHub
parent 8f20b35785
commit 07106da33a
14 changed files with 134 additions and 93 deletions

View File

@@ -48,6 +48,7 @@ import {
} from 'vue'
import dayjs, { Dayjs } from 'dayjs'
import localeData from 'dayjs/plugin/localeData'
import { useLocaleInject } from '@element-plus/hooks'
import { rangeArr } from '@element-plus/time-picker'
dayjs.extend(localeData)
@@ -78,9 +79,10 @@ export default defineComponent({
},
emits: ['pick'],
setup(props, ctx) {
const WEEK_DAYS = ref(dayjs().localeData().weekdaysShort())
const { lang } = useLocaleInject()
const WEEK_DAYS = ref(dayjs().locale(lang.value).localeData().weekdaysShort())
const now = dayjs()
const now = dayjs().locale(lang.value)
// todo better way to get Day.js locale object
const firstDayOfWeek = (now as any).$locale().weekStart || 0

View File

@@ -100,15 +100,15 @@ export default defineComponent({
emits: ['input', 'update:modelValue'],
setup(props, ctx) {
const { t } = useLocaleInject()
const { t, lang } = useLocaleInject()
const selectedDay = ref(null)
const now = dayjs()
const now = dayjs().locale(lang.value)
const prevMonthDayjs = computed(() => {
return date.value.subtract(1, 'month')
})
const curMonthDatePrefix = computed(() => {
return dayjs(date.value).format('YYYY-MM')
return dayjs(date.value).locale(lang.value).format('YYYY-MM')
})
const nextMonthDayjs = computed(() => {
@@ -143,14 +143,14 @@ export default defineComponent({
}
return now
} else {
return dayjs(props.modelValue)
return dayjs(props.modelValue).locale(lang.value)
}
})
// if range is valid, we get a two-digit array
const validatedRange = computed(() => {
if (!props.range) return []
const rangeArrDayjs = props.range.map(_ => dayjs(_))
const rangeArrDayjs = props.range.map(_ => dayjs(_).locale(lang.value))
const [startDayjs, endDayjs] = rangeArrDayjs
if (startDayjs.isAfter(endDayjs)) {
console.warn(

View File

@@ -1,5 +1,8 @@
import ConfigProvider from '@element-plus/config-provider'
import { CommonPicker } from '@element-plus/time-picker'
import { mount } from '@vue/test-utils'
import zhCn from '@element-plus/locale/lang/zh-cn'
import enUs from '@element-plus/locale/lang/en'
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
import { nextTick } from 'vue'
@@ -461,15 +464,32 @@ describe('WeekPicker', () => {
})
;[
{ locale: 'zh-cn', name: 'Monday', value: 1 },
{ locale: 'en', name: 'Sunday', value: 0 },
{ locale: zhCn, name: 'Monday', value: 1 },
{ locale: enUs, name: 'Sunday', value: 0 },
].forEach(loObj => {
it(`emit first day of the week, ${loObj.locale} locale, ${loObj.name}`, async () => {
dayjs.locale(loObj.locale)
const wrapper = _mount(`<el-date-picker
type='week'
v-model="value"
/>`, () => ({ value: '' }))
it(`emit first day of the week, ${loObj.locale.name} locale, ${loObj.name}`, async () => {
const wrapper = mount({
components: {
'el-date-picker': DatePicker,
'el-config-provider': ConfigProvider,
},
template: `
<el-config-provider :locale="locale">
<el-date-picker
type='week'
v-model="value"
/>
</el-config-provider>
`,
data() {
return {
locale: loObj.locale,
value: '',
}
},
}, {
attachTo: 'body',
})
const input = wrapper.find('input')
input.trigger('blur')
input.trigger('focus')
@@ -479,8 +499,8 @@ describe('WeekPicker', () => {
await nextTick()
const vm = wrapper.vm as any
expect(vm.value).not.toBeNull()
expect(+dayjs(vm.value)).toBe(+dayjs(vm.value).startOf('week'))
expect(dayjs(vm.value).day()).toBe(loObj.value) // Sunday or Monday
expect(+dayjs(vm.value).locale(loObj.locale.name)).toBe(+dayjs(vm.value).locale(loObj.locale.name).startOf('week'))
expect(dayjs(vm.value).locale(loObj.locale.name).day()).toBe(loObj.value) // Sunday or Monday
})
})
})

View File

@@ -89,7 +89,7 @@ export default defineComponent({
emits: ['changerange', 'pick', 'select'],
setup(props, ctx) {
const { t } = useLocaleInject()
const { t, lang } = useLocaleInject()
// data
const lastRow = ref(null)
const lastColumn = ref(null)
@@ -126,7 +126,7 @@ export default defineComponent({
const selectedDate: Dayjs[] = props.selectionMode === 'dates' ? coerceTruthyValueToArray(props.parsedValue) : []
const calNow = dayjs().startOf('day')
const calNow = dayjs().locale(lang.value).startOf('day')
for (let i = 0; i < 6; i++) {
const row = rows_[i]
@@ -225,7 +225,7 @@ export default defineComponent({
const cellMatchesDate = (cell, date) => {
if (!date) return false
return dayjs(date)
return dayjs(date).locale(lang.value)
.isSame(
props.date.date(Number(cell.text))
, 'day',

View File

@@ -26,8 +26,8 @@ import {
PropType,
} from 'vue'
const datesInMonth = (year, month) => {
const firstDay = dayjs().startOf('month').month(month).year(year)
const datesInMonth = (year, month, lang: string) => {
const firstDay = dayjs().locale(lang).startOf('month').month(month).year(year)
const numOfDays = firstDay.daysInMonth()
return rangeArr(numOfDays).map(n => firstDay.add(n, 'day').toDate())
}
@@ -66,7 +66,7 @@ export default defineComponent({
emits: ['changerange', 'pick', 'select'],
setup(props, ctx) {
const { t } = useLocaleInject()
const { t, lang } = useLocaleInject()
const months = ref(props.date.locale('en').localeData().monthsShort().map(_=>_.toLowerCase()))
const tableRows = ref([ [], [], [] ])
const lastRow = ref(null)
@@ -74,7 +74,7 @@ export default defineComponent({
const rows = computed(() => {
// TODO: refactory rows / getCellClasses
const rows = tableRows.value
const now = dayjs().startOf('month')
const now = dayjs().locale(lang.value).startOf('month')
for (let i = 0; i < 3; i++) {
const row = rows[i]
@@ -141,7 +141,7 @@ export default defineComponent({
const month = cell.text
style.disabled = props.disabledDate
? datesInMonth(year, month).every(props.disabledDate)
? datesInMonth(year, month, lang.value).every(props.disabledDate)
: false
style.current = coerceTruthyValueToArray(props.parsedValue).findIndex(date => date.year() === year && date.month() === month) >= 0
style.today = today.getFullYear() === year && today.getMonth() === month

View File

@@ -44,6 +44,7 @@
</template>
<script lang="ts">
import { useLocaleInject } from '@element-plus/hooks'
import { hasClass } from '@element-plus/utils/dom'
import { rangeArr } from '@element-plus/time-picker'
import { coerceTruthyValueToArray } from '@element-plus/utils/util'
@@ -54,8 +55,8 @@ import {
} from 'vue'
import dayjs, { Dayjs } from 'dayjs'
const datesInYear = year => {
const firstDay = dayjs(String(year)).startOf('year')
const datesInYear = (year, lang: string) => {
const firstDay = dayjs(String(year)).locale(lang).startOf('year')
const lastDay = firstDay.endOf('year')
const numOfDays = lastDay.dayOfYear()
return rangeArr(numOfDays).map(n => firstDay.add(n, 'day').toDate())
@@ -77,15 +78,16 @@ export default defineComponent({
emits: ['pick'],
setup(props, ctx) {
const { lang } = useLocaleInject()
const startYear = computed(() => {
return Math.floor(props.date.year() / 10) * 10
})
const getCellStyle = year => {
const style = {} as any
const today = dayjs()
const today = dayjs().locale(lang.value)
style.disabled = props.disabledDate
? datesInYear(year).every(props.disabledDate)
? datesInYear(year, lang.value).every(props.disabledDate)
: false
style.current = coerceTruthyValueToArray(props.parsedValue).findIndex(_ => _.year() === year) >= 0
@@ -95,10 +97,10 @@ export default defineComponent({
return style
}
const handleYearTableClick = event => {
const target = event.target
const handleYearTableClick = (event: MouseEvent) => {
const target = event.target as HTMLDivElement
if (target.tagName === 'A') {
if (hasClass(target.parentNode, 'disabled')) return
if (hasClass((target as any).parentNode, 'disabled')) return
const year = target.textContent || target.innerText
ctx.emit('pick', Number(year))
}

View File

@@ -195,9 +195,9 @@ export default defineComponent({
},
emits: ['pick', 'set-picker-option'],
setup(props, ctx) {
const { t } = useLocaleInject()
const { t, lang } = useLocaleInject()
const innerDate = ref(dayjs())
const innerDate = ref(dayjs().locale(lang.value))
const month = computed(() => {
return innerDate.value.month()
@@ -218,7 +218,7 @@ export default defineComponent({
}
const formatEmit = (emitDayjs: Dayjs) => {
if (defaultTime) {
const defaultTimeD = dayjs(defaultTime)
const defaultTimeD = dayjs(defaultTime).locale(lang.value)
return defaultTimeD.year(emitDayjs.year()).month(emitDayjs.month()).date(emitDayjs.date())
}
if (showTime.value) return emitDayjs.millisecond(0)
@@ -292,7 +292,7 @@ export default defineComponent({
const handleShortcutClick = shortcut => {
const shortcutValue = typeof shortcut.value === 'function' ? shortcut.value() : shortcut.value
if (shortcutValue) {
emit(dayjs(shortcutValue))
emit(dayjs(shortcutValue).locale(lang.value))
return
}
if (shortcut.onClick) {
@@ -358,7 +358,7 @@ export default defineComponent({
// deal with the scenario where: user opens the date time picker, then confirm without doing anything
let result = props.parsedValue as Dayjs
if (!result) {
const defaultTimeD = dayjs(defaultTime)
const defaultTimeD = dayjs(defaultTime).locale(lang.value)
const defaultValueD = getDefaultValue()
result = defaultTimeD.year(defaultValueD.year()).month(defaultValueD.month()).date(defaultValueD.date())
}
@@ -371,10 +371,10 @@ export default defineComponent({
const changeToNow = () => {
// NOTE: not a permanent solution
// consider disable "now" button in the future
const now = dayjs()
const now = dayjs().locale(lang.value)
const nowDate = now.toDate()
if ((!disabledDate || !disabledDate(nowDate)) && checkDateWithinRange(nowDate)) {
innerDate.value = dayjs()
innerDate.value = dayjs().locale(lang.value)
emit(innerDate.value)
}
}
@@ -417,7 +417,7 @@ export default defineComponent({
}
const handleVisibleTimeChange = value => {
const newDate = dayjs(value, timeFormat.value)
const newDate = dayjs(value, timeFormat.value).locale(lang.value)
if (newDate.isValid() && checkDateWithinRange(newDate)) {
innerDate.value = newDate.year(innerDate.value.year()).month(innerDate.value.month()).date(innerDate.value.date())
userInputTime.value = null
@@ -427,7 +427,7 @@ export default defineComponent({
}
const handleVisibleDateChange = value => {
const newDate = dayjs(value, dateFormat.value)
const newDate = dayjs(value, dateFormat.value).locale(lang.value)
if (newDate.isValid()) {
if (disabledDate && disabledDate(newDate.toDate())) {
return
@@ -454,11 +454,11 @@ export default defineComponent({
}
const parseUserInput = value => {
return dayjs(value, props.format)
return dayjs(value, props.format).locale(lang.value)
}
const getDefaultValue = () => {
return dayjs(defaultValue)
return dayjs(defaultValue).locale(lang.value)
}
const handleKeydown = event => {
@@ -502,7 +502,7 @@ export default defineComponent({
if (disabledDate && disabledDate(newDate)) {
continue
}
const result = dayjs(newDate)
const result = dayjs(newDate).locale(lang.value)
innerDate.value = result
ctx.emit('pick', result, true)
break

View File

@@ -236,9 +236,9 @@ export default defineComponent({
emits: ['pick', 'set-picker-option'],
setup(props, ctx) {
const { t } = useLocaleInject()
const leftDate = ref(dayjs())
const rightDate = ref(dayjs().add(1, 'month'))
const { t, lang } = useLocaleInject()
const leftDate = ref(dayjs().locale(lang.value))
const rightDate = ref(dayjs().locale(lang.value).add(1, 'month'))
const minDate = ref(null)
const maxDate = ref(null)
const dateUserInput = ref({
@@ -404,7 +404,7 @@ export default defineComponent({
const formatEmit = (emitDayjs: Dayjs, index?) => {
if (!emitDayjs) return
if (defaultTime) {
const defaultTimeD = dayjs(defaultTime[index] || defaultTime)
const defaultTimeD = dayjs(defaultTime[index] || defaultTime).locale(lang.value)
return defaultTimeD.year(emitDayjs.year()).month(emitDayjs.month()).date(emitDayjs.date())
}
return emitDayjs
@@ -427,7 +427,10 @@ export default defineComponent({
const handleShortcutClick = shortcut => {
const shortcutValues = typeof shortcut.value === 'function' ? shortcut.value() : shortcut.value
if (shortcutValues) {
ctx.emit('pick', [dayjs(shortcutValues[0]), dayjs(shortcutValues[1])])
ctx.emit('pick', [
dayjs(shortcutValues[0]).locale(lang.value),
dayjs(shortcutValues[1]).locale(lang.value),
])
return
}
if (shortcut.onClick) {
@@ -448,7 +451,7 @@ export default defineComponent({
const handleDateInput = (value, type) => {
dateUserInput.value[type] = value
const parsedValueD = dayjs(value, dateFormat.value)
const parsedValueD = dayjs(value, dateFormat.value).locale(lang.value)
if (parsedValueD.isValid()) {
if (disabledDate &&
@@ -473,13 +476,13 @@ export default defineComponent({
}
}
const handleDateChange = (value, type) => {
const handleDateChange = (_, type) => {
dateUserInput.value[type] = null
}
const handleTimeInput = (value, type) => {
timeUserInput.value[type] = value
const parsedValueD = dayjs(value, timeFormat.value)
const parsedValueD = dayjs(value, timeFormat.value).locale(lang.value)
if (parsedValueD.isValid()) {
if (type === 'min') {
@@ -549,16 +552,18 @@ export default defineComponent({
ctx.emit('pick', null)
}
const formatToString = value => {
const formatToString = (value: Dayjs | Dayjs[]) => {
return Array.isArray(value) ? value.map(_ => _.format(format)) : value.format(format)
}
const parseUserInput = value => {
return Array.isArray(value) ? value.map(_ => dayjs(_, format)) : dayjs(value, format)
const parseUserInput = (value: Dayjs | Dayjs[]) => {
return Array.isArray(value)
? value.map(_ => dayjs(_, format).locale(lang.value))
: dayjs(value, format).locale(lang.value)
}
const getDefaultValue = () => {
let start
let start: Dayjs
if (Array.isArray(defaultValue)) {
const left = dayjs(defaultValue[0])
let right = dayjs(defaultValue[1])
@@ -571,6 +576,8 @@ export default defineComponent({
} else {
start = dayjs()
}
start = start.locale(lang.value)
return [start, start.add(1, 'month')]
}

View File

@@ -109,16 +109,19 @@ export default defineComponent({
emits: ['pick', 'set-picker-option'],
setup(props, ctx) {
const { t } = useLocaleInject()
const leftDate = ref(dayjs())
const rightDate = ref(dayjs().add(1, 'year'))
const { t, lang } = useLocaleInject()
const leftDate = ref(dayjs().locale(lang.value))
const rightDate = ref(dayjs().locale(lang.value).add(1, 'year'))
const hasShortcuts = computed(() => !!shortcuts.length)
const handleShortcutClick = shortcut => {
const shortcutValues = typeof shortcut.value === 'function' ? shortcut.value() : shortcut.value
if (shortcutValues) {
ctx.emit('pick', [dayjs(shortcutValues[0]), dayjs(shortcutValues[1])])
ctx.emit('pick', [
dayjs(shortcutValues[0]).locale(lang.value),
dayjs(shortcutValues[1]).locale(lang.value),
])
return
}
if (shortcut.onClick) {
@@ -220,7 +223,7 @@ export default defineComponent({
}
const getDefaultValue = () => {
let start
let start: Dayjs
if (Array.isArray(defaultValue)) {
const left = dayjs(defaultValue[0])
let right = dayjs(defaultValue[1])
@@ -233,6 +236,7 @@ export default defineComponent({
} else {
start = dayjs()
}
start = start.locale(lang.value)
return [start, start.add(1, 'year')]
}

View File

@@ -1,5 +1,5 @@
import { setLocale, i18n } from '@element-plus/locale'
import { setLocale } from '@element-plus/locale'
import { setConfig } from '@element-plus/utils/config'
import isServer from '@element-plus/utils/isServer'
// if you encountered problems alike "Can't resolve './version'"

View File

@@ -133,6 +133,7 @@ import {
} from 'vue'
import dayjs, { Dayjs } from 'dayjs'
import isEqual from 'lodash/isEqual'
import { useLocaleInject } from '@element-plus/hooks'
import { ClickOutside } from '@element-plus/directives'
import ElInput from '@element-plus/input'
import ElPopper from '@element-plus/popper'
@@ -182,13 +183,15 @@ const valueEquals = function(a, b) {
return false
}
const parser = function(date: Date | string, format: string): Dayjs {
const day = isEmpty(format) ? dayjs(date) : dayjs(date, format)
const parser = function(date: Date | string, format: string, lang: string): Dayjs {
const day = isEmpty(format)
? dayjs(date).locale(lang)
: dayjs(date, format).locale(lang)
return day.isValid() ? day : undefined
}
const formatter = function(date: Date, format: string) {
return isEmpty(format) ? date : dayjs(date).format(format)
const formatter = function(date: Date, format: string, lang: string) {
return isEmpty(format) ? date : dayjs(date).locale(lang).format(format)
}
export default defineComponent({
@@ -202,6 +205,7 @@ export default defineComponent({
emits: ['update:modelValue', 'change', 'focus', 'blur'],
setup(props, ctx) {
const ELEMENT = useGlobalConfig()
const { lang } = useLocaleInject()
const elForm = inject(elFormKey, {} as ElFormContext)
const elFormItem = inject(elFormItemKey, {} as ElFormItemContext)
@@ -236,11 +240,11 @@ export default defineComponent({
if (!valueEquals(props.modelValue, val)) {
let formatValue
if (Array.isArray(val)) {
formatValue = val.map(_ => formatter(_, props.valueFormat))
formatValue = val.map(_ => formatter(_, props.valueFormat, lang.value))
} else if(val) {
formatValue = formatter(val, props.valueFormat)
formatValue = formatter(val, props.valueFormat, lang.value)
}
ctx.emit('update:modelValue', val ? formatValue : val)
ctx.emit('update:modelValue', val ? formatValue : val, lang.value)
}
}
const refInput = computed(() => {
@@ -296,9 +300,9 @@ export default defineComponent({
}
} else {
if (Array.isArray(props.modelValue)) {
result = props.modelValue.map(_=> parser(_, props.valueFormat))
result = props.modelValue.map(_=> parser(_, props.valueFormat, lang.value))
} else {
result = parser(props.modelValue, props.valueFormat)
result = parser(props.modelValue, props.valueFormat, lang.value)
}
}

View File

@@ -77,7 +77,7 @@ export default defineComponent({
emits: ['pick', 'select-range', 'set-picker-option'],
setup(props, ctx) {
const { t } = useLocaleInject()
const { t, lang } = useLocaleInject()
// data
const selectionRange = ref([0, 2])
const oldValue = useOldValue(props)
@@ -94,8 +94,8 @@ export default defineComponent({
return ''
})
// method
const isValidValue = _date => {
const parsedDate = dayjs(_date)
const isValidValue = (_date: Dayjs) => {
const parsedDate = dayjs(_date).locale(lang.value)
const result = getRangeAvailableTime(parsedDate)
return parsedDate.isSame(result)
}
@@ -118,7 +118,7 @@ export default defineComponent({
selectionRange.value = [start, end]
}
const changeSelectionRange = step => {
const changeSelectionRange = (step: number) => {
const list = [0, 3].concat(showSeconds.value ? [6] : [])
const mapping = ['hours', 'minutes'].concat(showSeconds.value ? ['seconds'] : [])
const index = list.indexOf(selectionRange.value[0])
@@ -126,7 +126,7 @@ export default defineComponent({
timePickerOptions['start_emitSelectRange'](mapping[next])
}
const handleKeydown = event => {
const handleKeydown = (event: KeyboardEvent) => {
const code = event.code
if (code === EVENT_CODE.left || code === EVENT_CODE.right) {
@@ -170,18 +170,18 @@ export default defineComponent({
return result
}
const parseUserInput = value => {
const parseUserInput = (value: Dayjs) => {
if (!value) return null
return dayjs(value, props.format)
return dayjs(value, props.format).locale(lang.value)
}
const formatToString = value => {
const formatToString = (value: Dayjs) => {
if (!value) return null
return value.format(props.format)
}
const getDefaultValue = () => {
return dayjs(defaultValue)
return dayjs(defaultValue).locale(lang.value)
}
ctx.emit('set-picker-option', ['isValidValue', isValidValue])

View File

@@ -107,7 +107,7 @@ export default defineComponent({
emits: ['pick', 'select-range', 'set-picker-option'],
setup(props, ctx) {
const { t } = useLocaleInject()
const { t, lang } = useLocaleInject()
const minDate = computed(() => props.parsedValue[0])
const maxDate = computed(() => props.parsedValue[1])
const oldValue = useOldValue(props)
@@ -137,8 +137,8 @@ export default defineComponent({
handleChange(minDate.value, date.millisecond(0))
}
const isValidValue = _date => {
const parsedDate = _date.map(_=> dayjs(_))
const isValidValue = (_date: Dayjs[]) => {
const parsedDate = _date.map(_=> dayjs(_).locale(lang.value))
const result = getRangeAvailableTime(parsedDate)
return parsedDate[0].isSame(result[0]) && parsedDate[1].isSame(result[1])
}
@@ -268,15 +268,15 @@ export default defineComponent({
return result
}
const parseUserInput = value => {
const parseUserInput = (value: Dayjs[] | Dayjs) => {
if (!value) return null
if (Array.isArray(value)) {
return value.map(_=> dayjs(_, props.format))
return value.map(_=> dayjs(_, props.format).locale(lang.value))
}
return dayjs(value, props.format)
return dayjs(value, props.format).locale(lang.value)
}
const formatToString = value => {
const formatToString = (value: Dayjs[] | Dayjs) => {
if (!value) return null
if (Array.isArray(value)) {
return value.map(_=> _.format(props.format))
@@ -286,11 +286,12 @@ export default defineComponent({
const getDefaultValue = () => {
if (Array.isArray(defaultValue)) {
return defaultValue.map(_=> dayjs(_))
return defaultValue.map(_=> dayjs(_).locale(lang.value))
}
const defaultDay = dayjs(defaultValue).locale(lang.value)
return [
dayjs(defaultValue),
dayjs(defaultValue).add(60,'m'),
defaultDay,
defaultDay.add(60,'m'),
]
}

View File

@@ -28,9 +28,10 @@ yarn build:theme
yarn build:helper
echo "Copying types"
# Post build cp type definitions
touch temp
find dist -type d ! -name 'element-plus' -depth 1 -print0 | xargs -0 -I {} sh -c "basename {}" > temp
find ./dist -type d ! -name 'element-plus' -depth 1 -print0 | xargs -0 -I {} sh -c "basename {}" > temp
input="./temp"
@@ -54,7 +55,7 @@ cp dist/element-plus/* lib
cp packages/utils/types.ts lib/utils/
cp dist/element-plus/* es
# Post build cleanup
echo "Remove temp files"
# Post build cleanup
rm -rf temp
rm -rf tempDir