目前支持语言
| 最低支持版本 | 语言 |
|---|---|
| v0.0.1 | 简体中文: zh_CN |
| v0.7.0 | 英语: en_GB、日语: ja_JP、韩语: ko_KR |
| v1.8.0 | 阿拉伯语: ar |
| v1.11.0 | 越南语: vi_VN、俄罗斯语: ru_RU、印尼语: id_ID、马来语: ms_MY、泰语: th_TH、土耳其语: tr_TR |
| v1.17.0 | 葡萄牙语(巴西): pt_BR |
| v1.28.0 | 繁体中文: zh_TW |
| v2.2.0 | 西班牙语: es |
| v2.15.0 | 意大利语: it、法语:fr、德语:de |
| v2.21.0 | 罗马尼亚语: ro |
| v2.29.0 | 瑞典语: sv_SE、波兰语: pl_PL、荷兰语: nl_NL |
| v2.88.0 | 阿塞拜疆语:az、保加利亚语:bg、加泰罗尼亚语:ca、捷克语:cs_CZ、宿务语:ceb_PH、丹麦语: da、希腊语:el_GR、西班牙语(拉美):es_419、爱沙尼亚语:et、波斯语:fa_IR、菲律宾语:fil_PH、芬兰语: fi_FI、法语(加):fr_CA、爱尔兰语:ga、希伯来语:he_IL、印地语:hi_IN、克罗地亚语:hr、匈牙利语:hu_HU、冰岛语:is、爪哇语:jv_ID、哈萨克语:kk、高棉语:km_KH、立陶宛语:lt、拉脱维亚语:lv、缅甸语:my_MM、挪威语: nb、葡萄牙语:pt、斯洛伐克语:sk、斯洛文尼亚语:sl、阿尔巴尼亚语:sq、斯瓦希里语:sw、乌克兰语:uk_UA、乌尔都语:ur、乌兹别克语:uz |
已支持组件
目前有以下组件存在内置默认文本,均已实现国际化多语言适配
Calendar、Cascader、Chat、DatePicker、Form、Image、List、Modal、Navigation、Nav、Pagination、Popconfirm、Select、Table、TimePicker、Transfer、Tree、TreeSelect、Typography、Upload
使用
LocaleProvider 使用了 React 的 context 上下文特性,你只需要在应用外围包裹一次即可全局生效
当需要切换语言时,直接切换 props 传入的 locale 即可
import React, { useCallback, useMemo, useState } from 'react';
import zh_CN from '@fibrous/semi-foundation/locale/source/zh_CN';
import en_GB from '@fibrous/semi-foundation/locale/source/en_GB';
import en_US from '@fibrous/semi-foundation/locale/source/en_US';
import ko_KR from '@fibrous/semi-foundation/locale/source/ko_KR';
import ja_JP from '@fibrous/semi-foundation/locale/source/ja_JP';
import vi_VN from '@fibrous/semi-foundation/locale/source/vi_VN';
import ru_RU from '@fibrous/semi-foundation/locale/source/ru_RU';
import id_ID from '@fibrous/semi-foundation/locale/source/id_ID';
import ms_MY from '@fibrous/semi-foundation/locale/source/ms_MY';
import th_TH from '@fibrous/semi-foundation/locale/source/th_TH';
import tr_TR from '@fibrous/semi-foundation/locale/source/tr_TR';
import pt_BR from '@fibrous/semi-foundation/locale/source/pt_BR';
import zh_TW from '@fibrous/semi-foundation/locale/source/zh_TW';
import sv_SE from '@fibrous/semi-foundation/locale/source/sv_SE';
import pl_PL from '@fibrous/semi-foundation/locale/source/pl_PL';
import nl_NL from '@fibrous/semi-foundation/locale/source/nl_NL';
import ar from '@fibrous/semi-foundation/locale/source/ar';
import es from '@fibrous/semi-foundation/locale/source/es';
import it from '@fibrous/semi-foundation/locale/source/it';
import de from '@fibrous/semi-foundation/locale/source/de';
import fr from '@fibrous/semi-foundation/locale/source/fr';
import ro from '@fibrous/semi-foundation/locale/source/ro';
import { LocaleProvider } from '@fibrous/semi-ui-react';
// 在locale中传入相应的语言包即可
() => {
return (
<LocaleProvider locale={en_GB}>
{/* eslint-disable-next-line react/jsx-no-undef */}
<App />
</LocaleProvider>
);
};
不传 locale 时 LocaleProvider 继承外层 LocaleProvider 的语言,最外层都没有时回退 zh_CN;ConfigProvider 的 locale 优先于 LocaleProvider。
代码示例
国际化
- Total pages: 10
- Items per page: 10
- 合計ページ数:10
- 1ページあたりのアイテム数:10
代码
import en_GB from '@fibrous/semi-foundation/locale/source/en_GB';
import ja_JP from '@fibrous/semi-foundation/locale/source/ja_JP';
import { LocaleProvider, Pagination } from '@fibrous/semi-ui-react';
export default function LocaleDemo() {
return (
<>
<LocaleProvider locale={en_GB}>
<Pagination total={100} showTotal showSizeChanger style={{ margin: 20 }} />
</LocaleProvider>
<LocaleProvider locale={ja_JP}>
<Pagination total={100} showTotal showSizeChanger style={{ margin: 20 }} />
</LocaleProvider>
</>
);
}自定义国际化组件
当你的自定义组件,也希望消费 Semi LocaleProvider Context 中的 localeCode 或者读取具体某个组件的 i18n 文本 localeData时,你可以使用 LocaleConsumer 进行获取;
代码
import en_GB from '@fibrous/semi-foundation/locale/source/en_GB';
import ko_KR from '@fibrous/semi-foundation/locale/source/ko_KR';
import zh_CN from '@fibrous/semi-foundation/locale/source/zh_CN';
import { LocaleProvider, LocaleConsumer } from '@fibrous/semi-ui-react';
// Custom component text joins the Locale type through declaration merging
declare module '@fibrous/semi-foundation/locale/interface' {
interface Locale {
ComponentA: { customKey: string };
}
}
function GetLocaleFromSemi() {
return (
<LocaleConsumer componentName="TimePicker">
{(localeData, localeCode) => (
<div>
{localeCode} : {localeData.begin}
</div>
)}
</LocaleConsumer>
);
}
function ExtractComponent() {
return <LocaleConsumer componentName="ComponentA">{(localeData) => <div>{localeData.customKey}</div>}</LocaleConsumer>;
}
export default function LocaleDemo() {
const new_zh_CN = { ...zh_CN, ComponentA: { customKey: 'semi' } };
const new_ko_KR = { ...ko_KR, ComponentA: { customKey: 'design' } };
const new_en_GB = { ...en_GB, ComponentA: { customKey: 'dsm' } };
return (
<>
<LocaleProvider locale={new_zh_CN}>
<GetLocaleFromSemi />
</LocaleProvider>
<LocaleProvider locale={new_ko_KR}>
<GetLocaleFromSemi />
</LocaleProvider>
<LocaleProvider locale={new_en_GB}>
<GetLocaleFromSemi />
</LocaleProvider>
<LocaleProvider locale={new_zh_CN}>
<ExtractComponent />
</LocaleProvider>
<LocaleProvider locale={new_ko_KR}>
<ExtractComponent />
</LocaleProvider>
<LocaleProvider locale={new_en_GB}>
<ExtractComponent />
</LocaleProvider>
</>
);
}支持多语言的组件
示例给出了目前所有支持多语言的组件
当你的网站有RTL适配需求时,推荐直接使用ConfigProvider,除了可配置locale外,还可以直接同时配置direction='rtl'/'ltr'
若无RTL适配需求,直接使用LocaleProvider即可
Pagination
- 总页数:10
- 每页条数:10
- 跳至页
Modal
Select & Cascader
DatePicker
TimePicker
TreeSelect
Table
| Name | Age | Address |
|---|
| Bytedance 0 | 32 | Beijing, Haidian. Zhichun Road 0 |
| Bytedance 1 | 32 | Beijing, Haidian. Zhichun Road 1 |
| Bytedance 2 | 32 | Beijing, Haidian. Zhichun Road 2 |
| Bytedance 3 | 32 | Beijing, Haidian. Zhichun Road 3 |
| Bytedance 4 | 32 | Beijing, Haidian. Zhichun Road 4 |
| Bytedance 5 | 32 | Beijing, Haidian. Zhichun Road 5 |
| Bytedance 6 | 32 | Beijing, Haidian. Zhichun Road 6 |
| Bytedance 7 | 32 | Beijing, Haidian. Zhichun Road 7 |
| Bytedance 8 | 32 | Beijing, Haidian. Zhichun Road 8 |
| Bytedance 9 | 32 | Beijing, Haidian. Zhichun Road 9 |
Table - Empty
| Name | Age | Address |
|---|
List - Empty
Calendar
Typography - Copyable
Click to copy text.
Typography - Collapsible
支持展开和折叠:Semi Design 是由抖音前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。
Transfer
Image




Form
Navigation
代码
import { useState, useMemo, useCallback, useRef } from 'react';
import zh_CN from '@fibrous/semi-foundation/locale/source/zh_CN';
import { IconUser, IconSemiLogo, IconStar } from '@fibrous/semi-icons-react';
import { LocaleProvider, ConfigProvider, Pagination, Modal, Button, Select, Cascader, DatePicker, TreeSelect, Table, TimePicker, List, Calendar, Typography, Transfer, ImagePreview, Image, Form, Nav } from '@fibrous/semi-ui-react';
// A pack loads when it is picked, so the page does not bundle every language up front
const loaders = {
ar: () => import('@fibrous/semi-foundation/locale/source/ar'),
az: () => import('@fibrous/semi-foundation/locale/source/az'),
bg: () => import('@fibrous/semi-foundation/locale/source/bg'),
bn_IN: () => import('@fibrous/semi-foundation/locale/source/bn_IN'),
ca: () => import('@fibrous/semi-foundation/locale/source/ca'),
ceb_PH: () => import('@fibrous/semi-foundation/locale/source/ceb_PH'),
cs_CZ: () => import('@fibrous/semi-foundation/locale/source/cs_CZ'),
da: () => import('@fibrous/semi-foundation/locale/source/da'),
de: () => import('@fibrous/semi-foundation/locale/source/de'),
el_GR: () => import('@fibrous/semi-foundation/locale/source/el_GR'),
en_GB: () => import('@fibrous/semi-foundation/locale/source/en_GB'),
en_US: () => import('@fibrous/semi-foundation/locale/source/en_US'),
es: () => import('@fibrous/semi-foundation/locale/source/es'),
es_419: () => import('@fibrous/semi-foundation/locale/source/es_419'),
et: () => import('@fibrous/semi-foundation/locale/source/et'),
fa_IR: () => import('@fibrous/semi-foundation/locale/source/fa_IR'),
fi_FI: () => import('@fibrous/semi-foundation/locale/source/fi_FI'),
fil_PH: () => import('@fibrous/semi-foundation/locale/source/fil_PH'),
fr: () => import('@fibrous/semi-foundation/locale/source/fr'),
fr_CA: () => import('@fibrous/semi-foundation/locale/source/fr_CA'),
ga: () => import('@fibrous/semi-foundation/locale/source/ga'),
he_IL: () => import('@fibrous/semi-foundation/locale/source/he_IL'),
hi_IN: () => import('@fibrous/semi-foundation/locale/source/hi_IN'),
hr: () => import('@fibrous/semi-foundation/locale/source/hr'),
hu_HU: () => import('@fibrous/semi-foundation/locale/source/hu_HU'),
id_ID: () => import('@fibrous/semi-foundation/locale/source/id_ID'),
is: () => import('@fibrous/semi-foundation/locale/source/is'),
it: () => import('@fibrous/semi-foundation/locale/source/it'),
ja_JP: () => import('@fibrous/semi-foundation/locale/source/ja_JP'),
jv_ID: () => import('@fibrous/semi-foundation/locale/source/jv_ID'),
kk: () => import('@fibrous/semi-foundation/locale/source/kk'),
km_KH: () => import('@fibrous/semi-foundation/locale/source/km_KH'),
ko_KR: () => import('@fibrous/semi-foundation/locale/source/ko_KR'),
lt: () => import('@fibrous/semi-foundation/locale/source/lt'),
lv: () => import('@fibrous/semi-foundation/locale/source/lv'),
ms_MY: () => import('@fibrous/semi-foundation/locale/source/ms_MY'),
my_MM: () => import('@fibrous/semi-foundation/locale/source/my_MM'),
nb: () => import('@fibrous/semi-foundation/locale/source/nb'),
nl_NL: () => import('@fibrous/semi-foundation/locale/source/nl_NL'),
pl_PL: () => import('@fibrous/semi-foundation/locale/source/pl_PL'),
pt: () => import('@fibrous/semi-foundation/locale/source/pt'),
pt_BR: () => import('@fibrous/semi-foundation/locale/source/pt_BR'),
ro: () => import('@fibrous/semi-foundation/locale/source/ro'),
ru_RU: () => import('@fibrous/semi-foundation/locale/source/ru_RU'),
sk: () => import('@fibrous/semi-foundation/locale/source/sk'),
sl: () => import('@fibrous/semi-foundation/locale/source/sl'),
sq: () => import('@fibrous/semi-foundation/locale/source/sq'),
sv_SE: () => import('@fibrous/semi-foundation/locale/source/sv_SE'),
sw: () => import('@fibrous/semi-foundation/locale/source/sw'),
th_TH: () => import('@fibrous/semi-foundation/locale/source/th_TH'),
tr_TR: () => import('@fibrous/semi-foundation/locale/source/tr_TR'),
uk_UA: () => import('@fibrous/semi-foundation/locale/source/uk_UA'),
ur: () => import('@fibrous/semi-foundation/locale/source/ur'),
uz: () => import('@fibrous/semi-foundation/locale/source/uz'),
vi_VN: () => import('@fibrous/semi-foundation/locale/source/vi_VN'),
zh_TW: () => import('@fibrous/semi-foundation/locale/source/zh_TW'),
};
type LocaleCode = 'zh_CN' | keyof typeof loaders;
const treeData = [
{
label: 'Asia',
value: 'asia',
key: '1',
children: [
{
label: 'China',
value: 'china',
key: '1-0',
children: [
{ label: 'Beijing', value: 'beijing', key: '1-0-0' },
{ label: 'Shanghai', value: 'shanghai', key: '1-0-1' },
],
},
{
label: 'Japan',
value: 'japan',
key: '1-1',
children: [{ label: 'Osaka', value: 'osaka', key: '1-1-0' }],
},
],
},
];
const I18nComponent = () => {
const [modalVisible, setModalVisible] = useState(false);
const columns = useMemo(
() => [
{
title: 'Name',
width: 250,
dataIndex: 'name',
},
{
title: 'Age',
width: 150,
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
],
[]
);
const dataSource = useMemo(() => {
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: '' + i,
name: `Bytedance ${i}`,
age: 32,
address: `Beijing, Haidian. Zhichun Road ${i}`,
});
}
return data;
}, []);
const transferData = useMemo(() => {
return Array.from({ length: 100 }, (_v, i) => {
return {
label: `选项名称 ${i}`,
value: i,
disabled: false,
key: i,
};
});
}, []);
const srcList = useMemo(() => ['/assets/root-web-sites/abstract.jpg', '/assets/root-web-sites/sky.jpg', '/assets/root-web-sites/greenleaf.jpg', '/assets/root-web-sites/colorful.jpg'], []);
const style = { margin: 10 };
return (
<>
<h5>Pagination</h5>
<Pagination total={100} showTotal showSizeChanger style={style} showQuickJumper />
<h5>Modal</h5>
<div style={style}>
<Button onClick={() => setModalVisible(true)}>Show Modal</Button>
<Modal title="Modal" visible={modalVisible} onOk={() => setModalVisible(false)} onCancel={() => setModalVisible(false)}>
<p>This is the content of a basic modal.</p>
<p>More content...</p>
</Modal>
</div>
<h5>Select & Cascader</h5>
<div style={style}>
<Select aria-label="Select" filter style={{ width: '180px' }}>
<Select.Option value="abc">abc</Select.Option>
<Select.Option value="vigo" disabled>
vigo
</Select.Option>
<Select.Option value="hotsoon">hotsoon</Select.Option>
</Select>
<Cascader style={{ width: 300, margin: 10 }} treeData={treeData} filterTreeNode prefix="Cascader" />
</div>
<h5>DatePicker</h5>
<DatePicker style={{ ...style, width: 250 }} />
<DatePicker style={{ ...style, width: 300 }} type="dateTime" />
<DatePicker style={{ ...style, width: 300 }} type="dateRange" />
<DatePicker style={{ ...style, width: 450 }} type="dateTimeRange" />
<h5>TimePicker</h5>
<TimePicker style={style} />
<TimePicker use12Hours style={style} />
<br />
<br />
<h5>TreeSelect</h5>
<TreeSelect style={{ ...style, width: 300 }} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} treeData={treeData} filterTreeNode />
<h5>Table</h5>
<Table columns={columns} dataSource={dataSource} scroll={{ y: 320 }} />
<h5>Table - Empty</h5>
<Table columns={columns} dataSource={[]} scroll={{ y: 320 }} />
<h5>List - Empty</h5>
<List header={<div>List</div>} dataSource={[]} />
<h5>Calendar</h5>
<Calendar mode="month" displayValue={new Date(2019, 6, 23)} />
<h5>Typography - Copyable</h5>
<Typography.Paragraph copyable>Click to copy text.</Typography.Paragraph>
<h5>Typography - Collapsible</h5>
<Typography.Paragraph ellipsis={{ rows: 3, expandable: true, collapsible: true }} style={{ width: 300 }}>
支持展开和折叠:Semi Design 是由抖音前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。
</Typography.Paragraph>
<h5>Transfer</h5>
<Transfer style={{ width: 568, height: 416 }} dataSource={transferData} />
<h5>Image</h5>
<ImagePreview showTooltip>
{srcList.map((src, index) => {
return <Image key={index} src={src} width={200} alt={`lamp${index + 1}`} style={{ marginRight: 5 }} />;
})}
</ImagePreview>
<h5>Form</h5>
<Form layout="horizontal" onValueChange={(values) => console.log(values)}>
<Form.Input field="UserName" label={{ text: '角色', optional: true }} style={{ width: 200 }} />
</Form>
<h5>Navigation</h5>
<Nav
bodyStyle={{ height: 320 }}
items={[
{ itemKey: 'user', text: '用户管理', icon: <IconUser /> },
{ itemKey: 'union', text: '活动管理', icon: <IconStar /> },
]}
header={{
logo: <IconSemiLogo style={{ height: '36px', fontSize: 36 }} />,
text: 'Semi 数据后台',
}}
footer={{
collapseButton: true,
}}
/>
</>
);
};
export default function LocaleDemo() {
const [current, setCurrent] = useState({ code: 'zh_CN', locale: zh_CN });
const latest = useRef('zh_CN');
const onLanguageChange = useCallback(async (value = 'zh_CN') => {
const code = value as LocaleCode;
latest.current = code;
const locale = code === 'zh_CN' ? zh_CN : (await loaders[code]()).default;
// A pick made while this pack was loading wins
if (latest.current === code) {
setCurrent({ code, locale });
}
}, []);
return (
<>
<div style={{ borderBottom: '1px solid var(--semi-color-border)', paddingBottom: 20 }}>
<Select aria-label="切换语言" onChange={onLanguageChange} prefix="切换语言" style={{ width: 250 }} defaultValue="zh_CN">
<Select.Option value="zh_CN">简体中文</Select.Option>
<Select.Option value="en_US">英语(美)</Select.Option>
<Select.Option value="en_GB">英语(英)</Select.Option>
<Select.Option value="ja_JP">日语</Select.Option>
<Select.Option value="ko_KR">韩语</Select.Option>
<Select.Option value="ar">阿拉伯语</Select.Option>
<Select.Option value="vi_VN">越南语</Select.Option>
<Select.Option value="ru_RU">俄罗斯语</Select.Option>
<Select.Option value="id_ID">印尼语</Select.Option>
<Select.Option value="ms_MY">马来语</Select.Option>
<Select.Option value="th_TH">泰语</Select.Option>
<Select.Option value="tr_TR">土耳其语</Select.Option>
<Select.Option value="pt_BR">葡萄牙语(巴西)</Select.Option>
<Select.Option value="zh_TW">繁体中文</Select.Option>
<Select.Option value="es">西班牙语</Select.Option>
<Select.Option value="de">德语</Select.Option>
<Select.Option value="it">意大利语</Select.Option>
<Select.Option value="fr">法语</Select.Option>
<Select.Option value="ro">罗马尼亚语</Select.Option>
<Select.Option value="sv_SE">瑞典语</Select.Option>
<Select.Option value="pl_PL">波兰语</Select.Option>
<Select.Option value="nl_NL">荷兰语</Select.Option>
<Select.Option value="az">阿塞拜疆语</Select.Option>
<Select.Option value="bn_IN">孟加拉语</Select.Option>
<Select.Option value="bg">保加利亚语</Select.Option>
<Select.Option value="ca">加泰罗尼亚语</Select.Option>
<Select.Option value="cs_CZ">捷克语</Select.Option>
<Select.Option value="ceb_PH">宿务语</Select.Option>
<Select.Option value="da">丹麦语</Select.Option>
<Select.Option value="el_GR">希腊语</Select.Option>
<Select.Option value="es_419">西班牙语(拉美)</Select.Option>
<Select.Option value="et">爱沙尼亚语</Select.Option>
<Select.Option value="fa_IR">波斯语</Select.Option>
<Select.Option value="fil_PH">菲律宾语</Select.Option>
<Select.Option value="fi_FI">芬兰语</Select.Option>
<Select.Option value="fr_CA">法语(加)</Select.Option>
<Select.Option value="ga">爱尔兰语</Select.Option>
<Select.Option value="he_IL">希伯来语</Select.Option>
<Select.Option value="hi_IN">印地语</Select.Option>
<Select.Option value="hr">克罗地亚语</Select.Option>
<Select.Option value="hu_HU">匈牙利语</Select.Option>
<Select.Option value="is">冰岛语</Select.Option>
<Select.Option value="jv_ID">爪哇语</Select.Option>
<Select.Option value="kk">哈萨克语</Select.Option>
<Select.Option value="km_KH">高棉语</Select.Option>
<Select.Option value="lt">立陶宛语</Select.Option>
<Select.Option value="lv">拉脱维亚语</Select.Option>
<Select.Option value="my_MM">缅甸语</Select.Option>
<Select.Option value="nb">挪威语</Select.Option>
<Select.Option value="pt">葡萄牙语</Select.Option>
<Select.Option value="sk">斯洛伐克语</Select.Option>
<Select.Option value="sl">斯洛文尼亚语</Select.Option>
<Select.Option value="sq">阿尔巴尼亚语</Select.Option>
<Select.Option value="sw">斯瓦希里语</Select.Option>
<Select.Option value="uk_UA">乌克兰语</Select.Option>
<Select.Option value="ur">乌尔都语</Select.Option>
<Select.Option value="uz">乌兹别克语</Select.Option>
</Select>
</div>
<LocaleProvider locale={current.locale}>
<ConfigProvider direction={current.code === 'ar' ? 'rtl' : 'ltr'} locale={current.locale}>
<I18nComponent key={current.code} />
</ConfigProvider>
</LocaleProvider>
</>
);
}