Languages supported
| Minimum supported version | Language |
|---|---|
| v0.0.1 | Simplified Chinese: zh_CN |
| v0.7.0 | English: en_GB, Japanese: ja_JP, Korean: ko_KR |
| v1.8.0 | Arabic: ar |
| v1.11.0 | Vietnamese: vi_VN、Russian: ru_RU、Indonesian: id_ID、Malay: ms_MY、Thai: th_TH、Turkish: tr_TR |
| v1.17.0 | Portuguese: pt_BR |
| v1.28.0 | Traditional Chinese: zh_TW |
| v2.2.0 | Spanish: es |
| v2.15.0 | Italian: it、French:fr、German:de |
| v2.21.0 | Romanian: ro |
| v2.29.0 | Swedish: sv_SE、 Polish: pl_PL、Dutch: nl_NL |
| v2.88.0 | Azerbaijani: az, Bulgarian: bg, Catalan: ca, Czech: cs_CZ, Cebu: ceb_PH, Danish: da, Greek: el_GR, Spanish (Latin America): es_419, Estonian: et, Persian: fa_IR, Filipino: fil_PH, Finnish: fi_FI, French (Canada): fr_CA, Irish: ga, Hebrew: he_IL, Hindi: hi_IN, Croatian: hr, Hungarian: hu_HU, Icelandic: is, Javanese: jv_ID, Kazakh: kk, Khmer: km_KH, Lithuanian: lt, Latvian: lv, Burmese: my_MM, Norwegian: nb, Portuguese: pt, Slovak: sk, Slovenian: sl, Albanian: sq, Swahili: sw, Ukrainian: uk_UA, Urdu: ur, Uzbek: uz |
Components supported
Calendar、Cascader、Chat、DatePicker、Form、Image、List、Modal、Navigation、Nav、Pagination、Popconfirm、Select、Table、TimePicker、Transfer、Tree、TreeSelect、Typography、Upload
How to use
LocaleProvider passes the locale pack to its descendants through context, and you only need to wrap the periphery of the application once to take effect globally.
When you need to switch the language, bind locale to state that changes; the subtree updates automatically.
<script lang="ts">
import en_GB from '@fibrae/semi-foundation/locale/source/en_GB';
import { LocaleProvider } from '@fibrae/semi-ui-svelte';
</script>
<LocaleProvider locale={en_GB}>
<App />
</LocaleProvider>
When locale is omitted, LocaleProvider inherits the language of the outer LocaleProvider; without any outer provider, zh_CN is used. The locale of ConfigProvider takes precedence over LocaleProvider.
Code example
Internationalization
- Total pages: 10
- Items per page: 10
- 合計ページ数:10
- 1ページあたりのアイテム数:10
Code
<script lang="ts">
import en_GB from '@fibrae/semi-foundation/locale/source/en_GB';
import ja_JP from '@fibrae/semi-foundation/locale/source/ja_JP';
import { LocaleProvider, Pagination } from '@fibrae/semi-ui-svelte';
</script>
<LocaleProvider locale={en_GB}>
<Pagination showSizeChanger showTotal style="margin: 20px" total={100} />
</LocaleProvider>
<LocaleProvider locale={ja_JP}>
<Pagination showSizeChanger showTotal style="margin: 20px" total={100} />
</LocaleProvider>Custom Internationalization Component
When your custom component also wants to consume the localeCode in the Semi LocaleProvider or read the i18n text of a specific component, use LocaleConsumer. Its children snippet receives { locale, localeCode, dateFnsLocale, currency }. During component initialisation, you can also call useLocale(componentName), which returns a getter; call it to read the same four fields. The language priority is ConfigProvider, LocaleProvider, then zh_CN. Custom namespaces (ComponentA in the example) extend the Locale type through declaration merging.
Code
<script lang="ts" module>
// Custom component text joins the Locale type through declaration merging
declare module '@fibrae/semi-foundation/locale/interface' {
export interface Locale {
ComponentA: { customKey: string };
}
}
</script>
<script lang="ts">
import en_GB from '@fibrae/semi-foundation/locale/source/en_GB';
import ko_KR from '@fibrae/semi-foundation/locale/source/ko_KR';
import zh_CN from '@fibrae/semi-foundation/locale/source/zh_CN';
import { LocaleConsumer, LocaleProvider } from '@fibrae/semi-ui-svelte';
const locales = [
{ ...zh_CN, ComponentA: { customKey: 'semi' } },
{ ...ko_KR, ComponentA: { customKey: 'design' } },
{ ...en_GB, ComponentA: { customKey: 'dsm' } },
];
</script>
{#each locales as locale (`semi-${locale.code}`)}
<LocaleProvider {locale}>
<LocaleConsumer componentName="TimePicker">
{#snippet children({ locale: timePicker, localeCode })}
<div>{localeCode} : {timePicker.begin}</div>
{/snippet}
</LocaleConsumer>
</LocaleProvider>
{/each}
{#each locales as locale (`custom-${locale.code}`)}
<LocaleProvider {locale}>
<LocaleConsumer componentName="ComponentA">
{#snippet children({ locale: componentA })}
<div>{componentA.customKey}</div>
{/snippet}
</LocaleConsumer>
</LocaleProvider>
{/each}Components that support multilingualism
The example gives all the current multilingual components
When your site needs RTL support, use ConfigProvider directly: besides locale, it also configures direction='rtl'/'ltr'.
Without RTL needs, LocaleProvider is enough.
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
Code
<script lang="ts">
import type { Locale } from '@fibrae/semi-foundation/locale/interface';
import zh_CN from '@fibrae/semi-foundation/locale/source/zh_CN';
import { IconSemiLogo, IconStar, IconUser } from '@fibrae/semi-icons-svelte';
import { Button, Calendar, Cascader, ConfigProvider, DatePicker, Form, FormInput, Image, ImagePreview, List, LocaleProvider, Modal, Nav, NavHeader, Pagination, Select, Table, TimePicker, Transfer, TreeSelect, TypographyParagraph, type ColumnProps, type NavItems } from '@fibrae/semi-ui-svelte';
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 columns: ColumnProps[] = [
{ title: 'Name', width: 250, dataIndex: 'name' },
{ title: 'Age', width: 150, dataIndex: 'age' },
{ title: 'Address', dataIndex: 'address' },
];
const dataSource = Array.from({ length: 46 }, (_, i) => ({
key: String(i),
name: `Bytedance ${i}`,
age: 32,
address: `Beijing, Haidian. Zhichun Road ${i}`,
}));
const transferData = Array.from({ length: 100 }, (_, i) => ({
label: `选项名称 ${i}`,
value: i,
disabled: false,
key: i,
}));
const srcList = ['/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 navItems: NavItems = [
{ itemKey: 'user', text: '用户管理' },
{ itemKey: 'union', text: '活动管理' },
];
const navIcons = { user: IconUser, union: IconStar };
// A pack loads when it is picked, so the page does not bundle every language up front
const loaders = {
ar: () => import('@fibrae/semi-foundation/locale/source/ar'),
az: () => import('@fibrae/semi-foundation/locale/source/az'),
bg: () => import('@fibrae/semi-foundation/locale/source/bg'),
bn_IN: () => import('@fibrae/semi-foundation/locale/source/bn_IN'),
ca: () => import('@fibrae/semi-foundation/locale/source/ca'),
ceb_PH: () => import('@fibrae/semi-foundation/locale/source/ceb_PH'),
cs_CZ: () => import('@fibrae/semi-foundation/locale/source/cs_CZ'),
da: () => import('@fibrae/semi-foundation/locale/source/da'),
de: () => import('@fibrae/semi-foundation/locale/source/de'),
el_GR: () => import('@fibrae/semi-foundation/locale/source/el_GR'),
en_GB: () => import('@fibrae/semi-foundation/locale/source/en_GB'),
en_US: () => import('@fibrae/semi-foundation/locale/source/en_US'),
es: () => import('@fibrae/semi-foundation/locale/source/es'),
es_419: () => import('@fibrae/semi-foundation/locale/source/es_419'),
et: () => import('@fibrae/semi-foundation/locale/source/et'),
fa_IR: () => import('@fibrae/semi-foundation/locale/source/fa_IR'),
fi_FI: () => import('@fibrae/semi-foundation/locale/source/fi_FI'),
fil_PH: () => import('@fibrae/semi-foundation/locale/source/fil_PH'),
fr: () => import('@fibrae/semi-foundation/locale/source/fr'),
fr_CA: () => import('@fibrae/semi-foundation/locale/source/fr_CA'),
ga: () => import('@fibrae/semi-foundation/locale/source/ga'),
he_IL: () => import('@fibrae/semi-foundation/locale/source/he_IL'),
hi_IN: () => import('@fibrae/semi-foundation/locale/source/hi_IN'),
hr: () => import('@fibrae/semi-foundation/locale/source/hr'),
hu_HU: () => import('@fibrae/semi-foundation/locale/source/hu_HU'),
id_ID: () => import('@fibrae/semi-foundation/locale/source/id_ID'),
is: () => import('@fibrae/semi-foundation/locale/source/is'),
it: () => import('@fibrae/semi-foundation/locale/source/it'),
ja_JP: () => import('@fibrae/semi-foundation/locale/source/ja_JP'),
jv_ID: () => import('@fibrae/semi-foundation/locale/source/jv_ID'),
kk: () => import('@fibrae/semi-foundation/locale/source/kk'),
km_KH: () => import('@fibrae/semi-foundation/locale/source/km_KH'),
ko_KR: () => import('@fibrae/semi-foundation/locale/source/ko_KR'),
lt: () => import('@fibrae/semi-foundation/locale/source/lt'),
lv: () => import('@fibrae/semi-foundation/locale/source/lv'),
ms_MY: () => import('@fibrae/semi-foundation/locale/source/ms_MY'),
my_MM: () => import('@fibrae/semi-foundation/locale/source/my_MM'),
nb: () => import('@fibrae/semi-foundation/locale/source/nb'),
nl_NL: () => import('@fibrae/semi-foundation/locale/source/nl_NL'),
pl_PL: () => import('@fibrae/semi-foundation/locale/source/pl_PL'),
pt: () => import('@fibrae/semi-foundation/locale/source/pt'),
pt_BR: () => import('@fibrae/semi-foundation/locale/source/pt_BR'),
ro: () => import('@fibrae/semi-foundation/locale/source/ro'),
ru_RU: () => import('@fibrae/semi-foundation/locale/source/ru_RU'),
sk: () => import('@fibrae/semi-foundation/locale/source/sk'),
sl: () => import('@fibrae/semi-foundation/locale/source/sl'),
sq: () => import('@fibrae/semi-foundation/locale/source/sq'),
sv_SE: () => import('@fibrae/semi-foundation/locale/source/sv_SE'),
sw: () => import('@fibrae/semi-foundation/locale/source/sw'),
th_TH: () => import('@fibrae/semi-foundation/locale/source/th_TH'),
tr_TR: () => import('@fibrae/semi-foundation/locale/source/tr_TR'),
uk_UA: () => import('@fibrae/semi-foundation/locale/source/uk_UA'),
ur: () => import('@fibrae/semi-foundation/locale/source/ur'),
uz: () => import('@fibrae/semi-foundation/locale/source/uz'),
vi_VN: () => import('@fibrae/semi-foundation/locale/source/vi_VN'),
zh_TW: () => import('@fibrae/semi-foundation/locale/source/zh_TW'),
};
type LocaleCode = 'zh_CN' | keyof typeof loaders;
const languageOptions: { value: LocaleCode; label: string }[] = [
{ value: 'zh_CN', label: '简体中文' },
{ value: 'en_US', label: '英语(美)' },
{ value: 'en_GB', label: '英语(英)' },
{ value: 'ja_JP', label: '日语' },
{ value: 'ko_KR', label: '韩语' },
{ value: 'ar', label: '阿拉伯语' },
{ value: 'vi_VN', label: '越南语' },
{ value: 'ru_RU', label: '俄罗斯语' },
{ value: 'id_ID', label: '印尼语' },
{ value: 'ms_MY', label: '马来语' },
{ value: 'th_TH', label: '泰语' },
{ value: 'tr_TR', label: '土耳其语' },
{ value: 'pt_BR', label: '葡萄牙语(巴西)' },
{ value: 'zh_TW', label: '繁体中文' },
{ value: 'es', label: '西班牙语' },
{ value: 'de', label: '德语' },
{ value: 'it', label: '意大利语' },
{ value: 'fr', label: '法语' },
{ value: 'ro', label: '罗马尼亚语' },
{ value: 'sv_SE', label: '瑞典语' },
{ value: 'pl_PL', label: '波兰语' },
{ value: 'nl_NL', label: '荷兰语' },
{ value: 'az', label: '阿塞拜疆语' },
{ value: 'bn_IN', label: '孟加拉语' },
{ value: 'bg', label: '保加利亚语' },
{ value: 'ca', label: '加泰罗尼亚语' },
{ value: 'cs_CZ', label: '捷克语' },
{ value: 'ceb_PH', label: '宿务语' },
{ value: 'da', label: '丹麦语' },
{ value: 'el_GR', label: '希腊语' },
{ value: 'es_419', label: '西班牙语(拉美)' },
{ value: 'et', label: '爱沙尼亚语' },
{ value: 'fa_IR', label: '波斯语' },
{ value: 'fil_PH', label: '菲律宾语' },
{ value: 'fi_FI', label: '芬兰语' },
{ value: 'fr_CA', label: '法语(加)' },
{ value: 'ga', label: '爱尔兰语' },
{ value: 'he_IL', label: '希伯来语' },
{ value: 'hi_IN', label: '印地语' },
{ value: 'hr', label: '克罗地亚语' },
{ value: 'hu_HU', label: '匈牙利语' },
{ value: 'is', label: '冰岛语' },
{ value: 'jv_ID', label: '爪哇语' },
{ value: 'kk', label: '哈萨克语' },
{ value: 'km_KH', label: '高棉语' },
{ value: 'lt', label: '立陶宛语' },
{ value: 'lv', label: '拉脱维亚语' },
{ value: 'my_MM', label: '缅甸语' },
{ value: 'nb', label: '挪威语' },
{ value: 'pt', label: '葡萄牙语' },
{ value: 'sk', label: '斯洛伐克语' },
{ value: 'sl', label: '斯洛文尼亚语' },
{ value: 'sq', label: '阿尔巴尼亚语' },
{ value: 'sw', label: '斯瓦希里语' },
{ value: 'uk_UA', label: '乌克兰语' },
{ value: 'ur', label: '乌尔都语' },
{ value: 'uz', label: '乌兹别克语' },
];
let localeCode = $state<LocaleCode>('zh_CN');
let locale = $state.raw<Locale>(zh_CN);
let latest: LocaleCode = 'zh_CN';
const onLanguageChange = async (code: LocaleCode) => {
latest = code;
const pack = code === 'zh_CN' ? zh_CN : (await loaders[code]()).default;
// A pick made while this pack was loading wins
if (latest === code) {
localeCode = code;
locale = pack;
}
};
let modalVisible = $state(false);
const closeModal = () => {
modalVisible = false;
};
</script>
<div>
<div style="border-bottom: 1px solid var(--semi-color-border); padding-bottom: 20px">
<Select aria-label="切换语言" onChange={(value) => onLanguageChange(value as LocaleCode)} optionList={languageOptions} style="width: 250px" value="zh_CN">
{#snippet prefix()}切换语言{/snippet}
</Select>
</div>
<LocaleProvider {locale}>
<ConfigProvider direction={localeCode === 'ar' ? 'rtl' : 'ltr'} {locale}>
{#key localeCode}
<div>
<h5>Pagination</h5>
<Pagination showQuickJumper showSizeChanger showTotal style="margin: 10px" total={100} />
<h5>Modal</h5>
<div style="margin: 10px">
<Button onclick={() => (modalVisible = true)}>Show Modal</Button>
<Modal bind:visible={modalVisible} onCancel={closeModal} onOk={closeModal} title="Modal">
<p>This is the content of a basic modal.</p>
<p>More content...</p>
</Modal>
</div>
<h5>Select & Cascader</h5>
<div style="margin: 10px">
<Select
aria-label="Select"
filter
optionList={[
{ value: 'abc', label: 'abc' },
{ value: 'vigo', label: 'vigo', disabled: true },
{ value: 'hotsoon', label: 'hotsoon' },
]}
style="width: 180px"
/>
<Cascader filterTreeNode style="width: 300px; margin: 10px" {treeData}>
{#snippet prefix()}Cascader{/snippet}
</Cascader>
</div>
<h5>DatePicker</h5>
<DatePicker style="margin: 10px; width: 250px" />
<DatePicker style="margin: 10px; width: 300px" type="dateTime" />
<DatePicker style="margin: 10px; width: 300px" type="dateRange" />
<DatePicker style="margin: 10px; width: 450px" type="dateTimeRange" />
<h5>TimePicker</h5>
<TimePicker style="margin: 10px" />
<TimePicker style="margin: 10px" use12Hours />
<br />
<br />
<h5>TreeSelect</h5>
<TreeSelect dropdownStyle="max-height: 400px; overflow: auto" filterTreeNode style="margin: 10px; width: 300px" {treeData} />
<h5>Table</h5>
<Table {columns} {dataSource} scroll={{ y: 320 }} />
<h5>Table - Empty</h5>
<Table {columns} dataSource={[]} scroll={{ y: 320 }} />
<h5>List - Empty</h5>
<List dataSource={[]}>
{#snippet header()}
<div>List</div>
{/snippet}
</List>
<h5>Calendar</h5>
<Calendar displayValue={new Date(2024, 4, 6, 12)} mode="month" />
<h5>Typography - Copyable</h5>
<TypographyParagraph copyable>Click to copy text.</TypographyParagraph>
<h5>Typography - Collapsible</h5>
<TypographyParagraph ellipsis={{ rows: 3, expandable: true, collapsible: true }} style="width: 300px">
支持展开和折叠:Semi Design 是由抖音前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。
</TypographyParagraph>
<h5>Transfer</h5>
<Transfer dataSource={transferData} style="width: 568px; height: 416px" />
<h5>Image</h5>
<ImagePreview showTooltip src={srcList}>
{#each srcList as src, index (src)}
<Image alt="lamp{index + 1}" {src} style="margin-right: 5px" width={200} />
{/each}
</ImagePreview>
<h5>Form</h5>
<Form layout="horizontal" onValueChange={(values) => console.log(values)}>
<FormInput field="UserName" label={{ text: '角色', optional: true }} style="width: 200px" />
</Form>
<h5>Navigation</h5>
<Nav bodyStyle="height: 320px" footer={{ collapseButton: true }} items={navItems}>
{#snippet header()}
<NavHeader text="Semi 数据后台">
{#snippet logo()}
<IconSemiLogo style="height: 36px; font-size: 36px" />
{/snippet}
</NavHeader>
{/snippet}
{#snippet icon({ item, size })}
{@const Icon = navIcons[item.itemKey as keyof typeof navIcons]}
{#if Icon}
<Icon {size} />
{/if}
{/snippet}
</Nav>
</div>
{/key}
</ConfigProvider>
</LocaleProvider>
</div>API Reference
LocaleProvider
Props
| Property | Description | Type | Default |
|---|---|---|---|
| children | Subtree that uses the locale pack. | Snippet | - |
| locale | Locale pack. When omitted, undefined or null, the language of the outer LocaleProvider is inherited; without any outer provider, components fall back to zh_CN. The locale of ConfigProvider takes precedence over it. | Locale | null | - |
LocaleProvider renders no DOM element, so class, style and native attributes and events are not applied to the page.
LocaleConsumer
Props
| Property | Description | Type | Default |
|---|---|---|---|
| children | Snippet receiving { locale, localeCode, dateFnsLocale, currency }, where locale is the text of componentName. | TypeSnippet<[result: LocaleResult<Locale[K]>]> | - |
| componentName | Namespace of the locale pack to read, such as TimePicker. Only component names declared in Locale are accepted; extend Locale by declaration merging for custom namespaces. | K | - |
LocaleConsumer renders no element of its own, so class, style and native attributes and events are not applied to the page.
useLocale
useLocale(componentName) returns a () => LocaleResult getter; call it to read the result. componentName can be a string or a function that returns a string.
| Field | Description | Type |
|---|---|---|
| locale | Text of the namespace named by componentName | Locale[K] |
| localeCode | Language code | string |
| dateFnsLocale | date-fns locale object; date-fns uses its default enUS when it is missing | Locale (date-fns) |
| currency | ISO 4217 currency code of the language's region | string |