diff --git a/.ls-lint.yml b/.ls-lint.yml index 260343b46..4612a8008 100644 --- a/.ls-lint.yml +++ b/.ls-lint.yml @@ -4,3 +4,6 @@ ls: .d.ts: camelCase .spec.ts: camelCase .vue: PascalCase + +ignore: + - packages/components/i18n/languages diff --git a/docs/i18n.zh.md b/docs/i18n.zh.md new file mode 100755 index 000000000..6f7fd687c --- /dev/null +++ b/docs/i18n.zh.md @@ -0,0 +1,44 @@ +--- +order: 4 +title: 国际化 +--- + +目前的默认文案是中文(`zh-CN`),如果需要使用其他语言,可以在初始化时进行配置,也可以在运行时切换,可以参考下面的方案。 + +## 初始化 + +```typescript +import { useI18n } from '@idux/components' +import { en_US } from '@idux/components/i18n/languages' + + +useI18n(en_US) + +``` + +## 运行时切换 + +```typescript +import { addI18n, useI18n } from '@idux/components' +import { es_ES, en_US } from '@idux/components/i18n/languages' + +// 首先需要先添加多语言包 +addI18n([es_ES, en_US]) + +useI18n('es_ES') + +// 运行时切换 +setTimeout(()=> useI18n('en_US'), 3000) + +``` + +注意:`es_ES`, `en_US` 是语言包名称,以下表格也遵循同样的规则。 + +## 支持语言 + +| 语言 | 语言包名 | +| ---------------- | ------ | +| 英语(美式) | en_US | +| 西班牙语 | es_ES | +| 中文简体 | zh_CN | +| 中文繁体 | zh_TW | diff --git a/packages/components/core/logger/index.ts b/packages/components/core/logger/index.ts index d095dee08..72f519dfe 100644 --- a/packages/components/core/logger/index.ts +++ b/packages/components/core/logger/index.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +/* istanbul ignore file */ import { IDUX_COMPONENTS_PREFIX } from '../constant' import { isDevMode } from '../utils' diff --git a/packages/components/core/utils/installComponent.ts b/packages/components/core/utils/installComponent.ts index 8fcc9b919..de1e2a473 100644 --- a/packages/components/core/utils/installComponent.ts +++ b/packages/components/core/utils/installComponent.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import type { App, DefineComponent } from 'vue' export const installComponent = (component: DefineComponent) => { diff --git a/packages/components/i18n/__tests__/useI18n.spec.ts b/packages/components/i18n/__tests__/useI18n.spec.ts new file mode 100644 index 000000000..815fc7413 --- /dev/null +++ b/packages/components/i18n/__tests__/useI18n.spec.ts @@ -0,0 +1,55 @@ +/* eslint-disable camelcase */ +import { flushPromises, mount } from '@vue/test-utils' +import { es_ES, en_US, zh_CN } from '../languages' +import { addI18n, getI18n, useI18n } from '../useI18n' + +const Comp = { + template: `
{{globalI18n.placeholder}}
`, + setup() { + const globalI18n = getI18n('global') + return { globalI18n } + }, +} + +describe('useI18n.ts', () => { + test('default zh_CN work', async () => { + const wrapper = mount(Comp) + expect(wrapper.text()).toEqual(zh_CN.global.placeholder) + }) + + test('addI18n work', async () => { + const wrapper = mount(Comp) + addI18n(es_ES) + useI18n('es_ES') + await flushPromises() + + expect(wrapper.text()).toEqual(es_ES.global.placeholder) + + addI18n([en_US, zh_CN]) + useI18n('en_US') + await flushPromises() + expect(wrapper.text()).toEqual(en_US.global.placeholder) + }) + + test('useI18n work', async () => { + const wrapper = mount(Comp) + useI18n(es_ES) + await flushPromises() + + expect(wrapper.text()).toEqual(es_ES.global.placeholder) + + useI18n('zh-CN') + await flushPromises() + expect(wrapper.text()).toEqual(zh_CN.global.placeholder) + + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}) + useI18n('zh-TW') + expect(warnSpy).toBeCalledTimes(1) + }) + + test('getI18n work', async () => { + const i18n = getI18n() + + expect(i18n.value).toEqual(zh_CN) + }) +}) diff --git a/packages/components/i18n/index.ts b/packages/components/i18n/index.ts new file mode 100644 index 000000000..5f99fd487 --- /dev/null +++ b/packages/components/i18n/index.ts @@ -0,0 +1,2 @@ +export * from './types' +export * from './useI18n' diff --git a/packages/components/i18n/languages/en_US.ts b/packages/components/i18n/languages/en_US.ts new file mode 100644 index 000000000..26fcf8437 --- /dev/null +++ b/packages/components/i18n/languages/en_US.ts @@ -0,0 +1,9 @@ +import { Locale } from '../types' + +// eslint-disable-next-line camelcase +export const en_US: Locale = { + type: 'en_US', + global: { + placeholder: 'Please select', + }, +} diff --git a/packages/components/i18n/languages/es_ES.ts b/packages/components/i18n/languages/es_ES.ts new file mode 100755 index 000000000..4d173bfe0 --- /dev/null +++ b/packages/components/i18n/languages/es_ES.ts @@ -0,0 +1,9 @@ +import { Locale } from '../types' + +// eslint-disable-next-line camelcase +export const es_ES: Locale = { + type: 'es_ES', + global: { + placeholder: 'Seleccione', + }, +} diff --git a/packages/components/i18n/languages/index.ts b/packages/components/i18n/languages/index.ts new file mode 100644 index 000000000..1f55b25e6 --- /dev/null +++ b/packages/components/i18n/languages/index.ts @@ -0,0 +1,4 @@ +export * from './en_US' +export * from './es_ES' +export * from './zh_CN' +export * from './zh_TW' diff --git a/packages/components/i18n/languages/zh_CN.ts b/packages/components/i18n/languages/zh_CN.ts new file mode 100755 index 000000000..1185dfde9 --- /dev/null +++ b/packages/components/i18n/languages/zh_CN.ts @@ -0,0 +1,9 @@ +import { Locale } from '../types' + +// eslint-disable-next-line camelcase +export const zh_CN: Locale = { + type: 'zh-CN', + global: { + placeholder: '请选择', + }, +} diff --git a/packages/components/i18n/languages/zh_TW.ts b/packages/components/i18n/languages/zh_TW.ts new file mode 100755 index 000000000..b5ae50bf2 --- /dev/null +++ b/packages/components/i18n/languages/zh_TW.ts @@ -0,0 +1,9 @@ +import { Locale } from '../types' + +// eslint-disable-next-line camelcase +export const zh_TW: Locale = { + type: 'zh-TW', + global: { + placeholder: '請選擇', + }, +} diff --git a/packages/components/i18n/types.ts b/packages/components/i18n/types.ts new file mode 100644 index 000000000..2b4a7c404 --- /dev/null +++ b/packages/components/i18n/types.ts @@ -0,0 +1,12 @@ +export interface GlobalLocale { + placeholder: string +} + +export interface Locale { + type: LocaleType + global: GlobalLocale +} + +export type LocaleKey = keyof Locale + +export type LocaleType = 'en_US' | 'es_ES' | 'zh-CN' | 'zh-TW' diff --git a/packages/components/i18n/useI18n.ts b/packages/components/i18n/useI18n.ts new file mode 100644 index 000000000..6ffb80692 --- /dev/null +++ b/packages/components/i18n/useI18n.ts @@ -0,0 +1,48 @@ +import { computed, ref } from 'vue' +import type { ComputedRef, Ref } from 'vue' +import { IDUX_COMPONENTS_PREFIX } from '../core/constant' +import { Logger } from '../core/logger' +import { zh_CN as defaultLocale } from './languages/zh_CN' +import type { Locale, LocaleKey, LocaleType } from './types' + +const currentType: Ref = ref('zh-CN') +const localeMap: Partial> = { 'zh-CN': defaultLocale } + +export function useI18n(locale: LocaleType | Locale): void { + if (typeof locale === 'string') { + if (localeMap[locale]) { + currentType.value = locale + } else { + Logger.warn(`${IDUX_COMPONENTS_PREFIX} The local [${locale}] was not added, please via 'addI18n()' add it.`) + } + } else { + const type = locale.type + localeMap[type] = locale + currentType.value = type + } +} + +export function addI18n(locale: Locale | Locale[]): void { + const locales = Array.isArray(locale) ? locale : [locale] + locales.forEach(item => { + localeMap[item.type] = item + }) +} + +/** + * + * @param key optional, gets the value of current locale by key + */ +export function getI18n(key: T): ComputedRef +export function getI18n(): ComputedRef +export function getI18n(key?: T): ComputedRef { + return computed(() => { + let currLocale = localeMap[currentType.value] + /* istanbul ignore next */ + if (!currLocale) { + /* istanbul ignore next */ + currLocale = defaultLocale + } + return key ? currLocale[key] : currLocale + }) +} diff --git a/packages/components/icon/__tests__/__snapshots__/icon.spec.ts.snap b/packages/components/icon/__tests__/__snapshots__/icon.spec.ts.snap index 2a24fc2b1..2bf94e8f2 100644 --- a/packages/components/icon/__tests__/__snapshots__/icon.spec.ts.snap +++ b/packages/components/icon/__tests__/__snapshots__/icon.spec.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Icon.vue render work 1`] = `""`; +exports[`Icon.vue render work 1`] = `""`; diff --git a/packages/components/icon/src/utils.ts b/packages/components/icon/src/utils.ts index 3c5409169..0e6dca5cd 100644 --- a/packages/components/icon/src/utils.ts +++ b/packages/components/icon/src/utils.ts @@ -104,7 +104,7 @@ export function createScriptElements(urls: string[], index = 0): void { if (urls.length > index + 1) { scriptElement.onload = () => createScriptElements(urls, index + 1) scriptElement.onerror = () => { - Logger.warn(`The url ${currentUrl} failed to load`) + Logger.error(`The url ${currentUrl} failed to load`) createScriptElements(urls, index + 1) } } diff --git a/packages/components/index.ts b/packages/components/index.ts index 366482b77..2540fe0e3 100644 --- a/packages/components/index.ts +++ b/packages/components/index.ts @@ -21,4 +21,5 @@ export * from './core/config' export * from './core/types' export * from './button' +export * from './i18n' export * from './icon'