Skip to content

Commit

Permalink
feat(locale): export Selectlang component (#200)
Browse files Browse the repository at this point in the history
* feat(locale): export Selectlang component

* feat(plugin-locake): support getAllLocales

Co-authored-by: 依鹭 <jingcao.cjc@antfin.com>
Co-authored-by: chenshuai2144 <qixian.cs@outlook.com>
  • Loading branch information
3 people authored May 15, 2020
1 parent 9f0df80 commit 16278a4
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 9 deletions.
30 changes: 22 additions & 8 deletions example/app.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { InitialState, request as umiRequest } from 'umi';
import { InitialState, request as umiRequest, SelectLang } from 'umi';
import { MenuItem } from '@umijs/plugin-layout';

export function render(oldRender: Function) {
Expand All @@ -19,13 +19,27 @@ export const layout = {
rightRender: (initialState: any, setInitialState: any) => {
console.log('initialState', initialState);
return (
<button
onClick={() => {
setInitialState({ name: 'SS' });
}}
>
{initialState.name}
</button>
<>
<SelectLang
postLocalesData={locales => [
...locales,
{
lang: 'nl-NL', // 荷兰语的 key 与 antd 保持一致
label: 'Nederlands', // 荷兰语的“荷兰语”
icon: '🇳🇱', // 荷兰国旗
title: 'Taal', // 荷兰语的“语言”
},
]}
onItemClick={({ key }) => alert(key)}
/>
<button
onClick={() => {
setInitialState({ name: 'SS' });
}}
>
{initialState.name}
</button>
</>
);
},
patchMenus: (menus: MenuItem[], initialInfo: InitialState) => {
Expand Down
3 changes: 3 additions & 0 deletions example/locales/nl-NL.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
name: '你好,{name}',
};
26 changes: 26 additions & 0 deletions packages/plugin-locale/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,32 @@ setLocale('zh-TW', true);
setLocale('zh-TW', false);
```

### <SelectLang />

选择语言的展示组件。可以通过开启 locale 插件,从 umi 中获取该组件。只有当项目依赖 antd, 同时项目 /locales 文件夹下有超过两个语言文件时才会显示。可配置的属性有:

- postLocaleData, 默认包含 "简体中文" 、"繁体中文"、 "英文" 、"葡萄牙语"四种语言配置,当需要展示其他语言时,可以通过配置 postLocaleData 来扩展,格式如下所示。
- globalIconClassName, 全球图标的样式。
- onItemClick, 切换语言时的回掉函数,默认会 setLocale。
- <[Dropdown/](https://ant.design/components/dropdown-cn/#API)> 的所有 API。

```tsx
import { SelectLang } from 'umi';

<SelectLang
postLocaleData={locales=> ([
...locales,
{
lang: 'nl-NL', // 语言的 key 与 antd & locales 下的文件名保持一致
label: 'Nederlands', // 下拉菜单中展示的语言名
icon: '🇳🇱', // 下拉菜单中展示的 icon (一般为国旗
title: 'Taal', // 鼠标浮上全球图标时展示的文案(一般为“语言”这个词的各种翻译
}
])}
onItemClick={({ key }) => alert(key)}
>
```

### 运行时配置

支持运行时对国际化做一些扩展与定制,例如自定义语言识别等。
Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-locale/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"intl": "1.2.5",
"moment": "2.x",
"react-intl": "3.12.0",
"warning": "^4.0.3"
"warning": "^4.0.3",
"@ant-design/icons": "^4.1.0"
},
"devDependencies": {
"@types/warning": "^3.0.0"
Expand Down
34 changes: 34 additions & 0 deletions packages/plugin-locale/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ interface ILocaleConfig {
baseSeparator?: string;
}

let hasAntd = false;
try {
hasAntd = !!require.resolve('antd');
} catch (_) {
console.log(
'@umijs/plugin-locale WARNING: antd is not installed, <SelectLang /> unavailable.',
);
}

export default (api: IApi) => {
const {
paths,
Expand All @@ -34,6 +43,7 @@ export default (api: IApi) => {
baseNavigator: true,
useLocalStorage: true,
baseSeparator: '-',
antd: !!hasAntd,
},
schema(joi) {
return joi.object({
Expand Down Expand Up @@ -171,6 +181,23 @@ export default (api: IApi) => {
Title: !!title,
}),
});

// SelectLang.tsx
const selectLang = readFileSync(
join(__dirname, 'templates', 'SelectLang.tpl'),
'utf-8',
);

api.writeTmpFile({
path: 'plugin-locale/SelectLang.tsx',
content: Mustache.render(selectLang, {
Antd: !!antd,
LocaleList: localeList,
ShowSelectLang: localeList.length > 1 && !!antd,
iconsPkgPath: winPath(require.resolve('@ant-design/icons')),
antdFiles: api.config?.ssr ? 'lib' : 'es',
}),
});
});

api.addRuntimePluginKey(() => 'locale');
Expand All @@ -196,4 +223,11 @@ export default (api: IApi) => {
source: `../plugin-locale/localeExports`,
};
});

api.addUmiExports(() => {
return {
exportAll: true,
source: `../plugin-locale/SelectLang`,
};
});
};
142 changes: 142 additions & 0 deletions packages/plugin-locale/src/templates/SelectLang.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React from 'react';
{{#Antd}}
import { GlobalOutlined } from '{{{ iconsPkgPath }}}';
import { Menu, Dropdown } from 'antd';
import { ClickParam } from 'antd/{{{antdFiles}}}/menu';
import { DropDownProps } from 'antd/{{{antdFiles}}}/dropdown';
{{/Antd}}
import { getLocale, setLocale } from './localeExports';

{{#Antd}}
export interface HeaderDropdownProps extends DropDownProps {
overlayClassName?: string;
placement?:
| 'bottomLeft'
| 'bottomRight'
| 'topLeft'
| 'topCenter'
| 'topRight'
| 'bottomCenter';
}

const HeaderDropdown: React.FC<HeaderDropdownProps> = ({
overlayClassName: cls,
...restProps
}) => (
<Dropdown
getPopupContainer={(trigger) =>( trigger.parentNode as HTMLElement)}
overlayClassName={cls}
{...restProps}
/>
);
{{/Antd}}

interface LocalData {
lang: string,
label?: string,
icon?: string,
title?: string,
}

interface SelectLangProps {
globalIconClassName?: string,
postLocalesData?: (locales: LocalData[]) => LocalData[],
onItemClick?: (params:ClickParam) => void,
}

const transformArrayToObject = (allLangUIConfig:LocalData[])=>{
return allLangUIConfig.reduce((obj, item) => {
if(!item.lang){
return obj;
}

return {
...obj,
[item.lang]: item,
};
}, {});
}

const defaultLangUConfigMap = {
'zh-CN': {
lang: 'zh-CN',
label: '简体中文',
icon: '🇨🇳',
title: '语言'
},
'zh-TW': {
lang: 'zh-TW',
label: '繁体中文',
icon: '🇭🇰',
title: '語言'
},
'en-US': {
lang: 'en-US',
label: 'English',
icon: '🇺🇸',
title: 'Language'
},
'pt-BR': {
lang: 'pt-BR',
label: 'Português',
icon: '🇧🇷',
title: 'Idiomas'
}
};

export const SelectLang: React.FC<SelectLangProps> = (props) => {
{{#ShowSelectLang}}
const { globalIconClassName, postLocalesData, onItemClick, ...restProps } = props;
const selectedLang = getLocale();

const changeLang = ({ key }: ClickParam): void => setLocale(key);

const defaultLangUConfig = getAllLocales().map(
key =>
defaultLangUConfigMap[key] || {
lang: key,
label: key,
icon: '🌐',
title: key
}
);


const allLangUIConfig = transformArrayToObject(postLocalesData ? postLocalesData(defaultLangUConfig): defaultLangUConfig);

const handleClick = onItemClick ? (params)=> onItemClick(params): changeLang;

const menuItemStyle = { minWidth: '160px' }
const langMenu = (
<Menu
selectedKeys={[selectedLang]} onClick={handleClick}
>
{{#LocaleList}}
<Menu.Item key={'{{locale}}'} style={menuItemStyle}>
<span role='img' aria-label={allLangUIConfig['{{locale}}']?.label || '{{locale}}'}>
{allLangUIConfig['{{locale}}']?.icon || "🌐"}
</span>{' '}
{allLangUIConfig['{{locale}}']?.label || '{{locale}}'}
</Menu.Item>
{{/LocaleList}}
</Menu>
);

const style = {
verticalAlign: 'top',
cursor: 'pointer',
padding: '0 12px',
}

return (
<HeaderDropdown overlay={langMenu} placement='bottomRight' {...restProps}>
<span
className={globalIconClassName}
style={style}>
<GlobalOutlined title={allLangUIConfig[selectedLang]?.title} />
</span>
</HeaderDropdown>
);
{{/ShowSelectLang}}
return <></>
};

0 comments on commit 16278a4

Please sign in to comment.