Skip to content

Commit

Permalink
feat: more strictly return type locale detector (#1640)
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Nov 27, 2023
1 parent 917e00e commit 1345c99
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 26 deletions.
12 changes: 10 additions & 2 deletions packages/core-base/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export const CoreErrorCodes = {
INVALID_DATE_ARGUMENT: inc(), // 19
INVALID_ISO_DATE_ARGUMENT: inc(), // 20
NOT_SUPPORT_NON_STRING_MESSAGE: inc(), // 21
__EXTEND_POINT__: inc() // 22
NOT_SUPPORT_LOCALE_PROMISE_VALUE: inc(), // 22
NOT_SUPPORT_LOCALE_ASYNC_FUNCTION: inc(), // 23
NOT_SUPPORT_LOCALE_TYPE: inc(), // 24
__EXTEND_POINT__: inc() // 25
} as const

export type CoreErrorCodes =
Expand All @@ -39,5 +42,10 @@ export const errorMessages: { [code: number]: string } = {
[CoreErrorCodes.INVALID_ISO_DATE_ARGUMENT]:
'The argument provided is not a valid ISO date string',
[CoreErrorCodes.NOT_SUPPORT_NON_STRING_MESSAGE]:
'Not support non-string message'
'Not support non-string message',
[CoreErrorCodes.NOT_SUPPORT_LOCALE_PROMISE_VALUE]:
'cannot support promise value',
[CoreErrorCodes.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION]:
'cannot support async function',
[CoreErrorCodes.NOT_SUPPORT_LOCALE_TYPE]: 'cannot support locale type'
}
30 changes: 23 additions & 7 deletions packages/core-base/src/fallbacker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import {
isArray,
isBoolean,
isPlainObject,
isObject
isObject,
isPromise,
isFunction
} from '@intlify/shared'
import { DEFAULT_LOCALE } from './context'
import { CoreErrorCodes, createCoreError } from './errors'

import type { Locale, LocaleDetector, FallbackLocale } from './runtime'
import type { CoreContext, CoreInternalContext } from './context'
Expand Down Expand Up @@ -33,12 +36,25 @@ let _resolveLocale: string

/** @internal */
export function resolveLocale(locale: Locale | LocaleDetector) {
// prettier-ignore
return isString(locale)
? locale
: _resolveLocale != null && locale.resolvedOnce
? _resolveLocale
: (_resolveLocale = locale())
if (isString(locale)) {
return locale
} else {
if (isFunction(locale)) {
if (locale.resolvedOnce && _resolveLocale != null) {
return _resolveLocale
} else if (locale.constructor.name === 'Function') {
const resolve = locale()
if (isPromise(resolve)) {
throw createCoreError(CoreErrorCodes.NOT_SUPPORT_LOCALE_PROMISE_VALUE)
}
return (_resolveLocale = resolve)
} else {
throw createCoreError(CoreErrorCodes.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION)
}
} else {
throw createCoreError(CoreErrorCodes.NOT_SUPPORT_LOCALE_TYPE)
}
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/core-base/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type Locale = string
/** @VueI18nGeneral */
// prettier-ignore
export interface LocaleDetector<Args extends any[] = any[]> { // eslint-disable-line @typescript-eslint/no-explicit-any
(...args: Args): Locale
(...args: Args): Locale | Promise<Locale>
resolvedOnce?: boolean
}

Expand Down
39 changes: 38 additions & 1 deletion packages/core-base/test/fallbacker.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { createCoreContext as context, CoreContext } from '../src/context'
import { fallbackWithLocaleChain, fallbackWithSimple } from '../src/fallbacker'
import {
fallbackWithLocaleChain,
fallbackWithSimple,
resolveLocale
} from '../src/fallbacker'
import { errorMessages, CoreErrorCodes } from '../src/errors'
import { LocaleDetector } from '@intlify/core-base'

describe('fallbackWithSimple', () => {
let ctx: CoreContext<string>
Expand Down Expand Up @@ -283,3 +289,34 @@ describe('fallbackWithLocaleChain', () => {
})
})
})

describe('resolveLocale', () => {
test('string', () => {
expect(resolveLocale('en')).toEqual('en')
})

test('function return promise value', () => {
expect(() => resolveLocale(() => Promise.resolve('en'))).toThrowError(
errorMessages[CoreErrorCodes.NOT_SUPPORT_LOCALE_PROMISE_VALUE]
)
})

test('function return string', () => {
const fn = vi.fn(() => 'en')
expect(resolveLocale(fn)).toEqual('en')
expect(resolveLocale(fn)).toEqual('en')
})

test('async function', () => {
expect(() => resolveLocale(async () => Promise.resolve('en'))).toThrowError(
errorMessages[CoreErrorCodes.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION]
)
})

test('other type value', () => {
const fn = 1 as unknown as LocaleDetector
expect(() => resolveLocale(fn)).toThrowError(
errorMessages[CoreErrorCodes.NOT_SUPPORT_LOCALE_TYPE]
)
})
})
30 changes: 15 additions & 15 deletions packages/vue-i18n-core/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ const inc = incrementer(code)

export const I18nErrorCodes = {
// composer module errors
UNEXPECTED_RETURN_TYPE: code, // 23
UNEXPECTED_RETURN_TYPE: code, // 26
// legacy module errors
INVALID_ARGUMENT: inc(), // 24
INVALID_ARGUMENT: inc(), // 27
// i18n module errors
MUST_BE_CALL_SETUP_TOP: inc(), // 25
NOT_INSTALLED: inc(), // 26
NOT_AVAILABLE_IN_LEGACY_MODE: inc(), // 27
MUST_BE_CALL_SETUP_TOP: inc(), // 28
NOT_INSTALLED: inc(), // 29
NOT_AVAILABLE_IN_LEGACY_MODE: inc(), // 30
// directive module errors
REQUIRED_VALUE: inc(), // 28
INVALID_VALUE: inc(), // 29
REQUIRED_VALUE: inc(), // 31
INVALID_VALUE: inc(), // 32
// vue-devtools errors
CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN: inc(), // 30
NOT_INSTALLED_WITH_PROVIDE: inc(), // 31
CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN: inc(), // 33
NOT_INSTALLED_WITH_PROVIDE: inc(), // 34
// unexpected error
UNEXPECTED_ERROR: inc(), // 32
UNEXPECTED_ERROR: inc(), // 35
// not compatible legacy vue-i18n constructor
NOT_COMPATIBLE_LEGACY_VUE_I18N: inc(), // 33
NOT_COMPATIBLE_LEGACY_VUE_I18N: inc(), // 36
// bridge support vue 2.x only
BRIDGE_SUPPORT_VUE_2_ONLY: inc(), // 34
BRIDGE_SUPPORT_VUE_2_ONLY: inc(), // 37
// need to define `i18n` option in `allowComposition: true` and `useScope: 'local' at `useI18n``
MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION: inc(), // 35
MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION: inc(), // 38
// Not available Compostion API in Legacy API mode. Please make sure that the legacy API mode is working properly
NOT_AVAILABLE_COMPOSITION_IN_LEGACY: inc(), // 36
NOT_AVAILABLE_COMPOSITION_IN_LEGACY: inc(), // 39
// for enhancement
__EXTEND_POINT__: inc() // 37
__EXTEND_POINT__: inc() // 40
} as const

type I18nErrorCodes = (typeof I18nErrorCodes)[keyof typeof I18nErrorCodes]
Expand Down

0 comments on commit 1345c99

Please sign in to comment.