浏览代码

feat: inputnumber add currency support (#2742)

* feat: inputnumber add currency support

* feat: inputnumber add currency support

* feat: update locale source currency

* feat: remove keydown event process in currency mode

* feat: update inputnumber currency type

* feat: remove useless console log

* docs: add inputnumber currency doc
YannLynn 7 月之前
父节点
当前提交
23e87e4dbc
共有 32 个文件被更改,包括 904 次插入26 次删除
  1. 168 0
      content/input/inputnumber/index-en-US.md
  2. 167 0
      content/input/inputnumber/index.md
  3. 176 10
      packages/semi-foundation/inputNumber/foundation.ts
  4. 2 2
      packages/semi-ui/input/index.tsx
  5. 132 0
      packages/semi-ui/inputNumber/__test__/inputNumber.test.js
  6. 163 1
      packages/semi-ui/inputNumber/_story/inputNumber.stories.jsx
  7. 35 3
      packages/semi-ui/inputNumber/index.tsx
  8. 5 2
      packages/semi-ui/locale/_story/locale.stories.jsx
  9. 3 0
      packages/semi-ui/locale/interface.ts
  10. 3 2
      packages/semi-ui/locale/localeConsumer.tsx
  11. 2 0
      packages/semi-ui/locale/source/ar.ts
  12. 2 0
      packages/semi-ui/locale/source/de.ts
  13. 3 1
      packages/semi-ui/locale/source/en_GB.ts
  14. 3 1
      packages/semi-ui/locale/source/en_US.ts
  15. 2 0
      packages/semi-ui/locale/source/es.ts
  16. 2 0
      packages/semi-ui/locale/source/fr.ts
  17. 2 0
      packages/semi-ui/locale/source/id_ID.ts
  18. 2 0
      packages/semi-ui/locale/source/it.ts
  19. 2 0
      packages/semi-ui/locale/source/ja_JP.ts
  20. 2 0
      packages/semi-ui/locale/source/ko_KR.ts
  21. 2 0
      packages/semi-ui/locale/source/ms_MY.ts
  22. 3 1
      packages/semi-ui/locale/source/nl_NL.ts
  23. 4 2
      packages/semi-ui/locale/source/pl_PL.ts
  24. 2 0
      packages/semi-ui/locale/source/pt_BR.ts
  25. 2 0
      packages/semi-ui/locale/source/ro.ts
  26. 2 0
      packages/semi-ui/locale/source/ru_RU.ts
  27. 3 1
      packages/semi-ui/locale/source/sv_SE.ts
  28. 2 0
      packages/semi-ui/locale/source/th_TH.ts
  29. 2 0
      packages/semi-ui/locale/source/tr_TR.ts
  30. 2 0
      packages/semi-ui/locale/source/vi_VN.ts
  31. 2 0
      packages/semi-ui/locale/source/zh_CN.ts
  32. 2 0
      packages/semi-ui/locale/source/zh_TW.ts

+ 168 - 0
content/input/inputnumber/index-en-US.md

@@ -197,6 +197,170 @@ function Demo () {
 }
 }
 ```
 ```
 
 
+### Currency Display
+Version 2.77.0 supports currency display. In internationalization mode, enable currency={true} and the component will automatically display the corresponding currency type according to localeCode. (Note that the component key value needs to be updated after switching the language type)
+
+```jsx live=true
+import React from 'react';
+import zh_CN from '@douyinfe/semi-ui/lib/es/locale/source/zh_CN';
+import en_GB from '@douyinfe/semi-ui/lib/es/locale/source/en_GB';
+import en_US from '@douyinfe/semi-ui/lib/es/locale/source/en_US';
+import ko_KR from '@douyinfe/semi-ui/lib/es/locale/source/ko_KR';
+import ja_JP from '@douyinfe/semi-ui/lib/es/locale/source/ja_JP';
+import ar from '@douyinfe/semi-ui/lib/es/locale/source/ar';
+import vi_VN from '@douyinfe/semi-ui/lib/es/locale/source/vi_VN';
+import ru_RU from '@douyinfe/semi-ui/lib/es/locale/source/ru_RU';
+import id_ID from '@douyinfe/semi-ui/lib/es/locale/source/id_ID';
+import ms_MY from '@douyinfe/semi-ui/lib/es/locale/source/ms_MY';
+import th_TH from '@douyinfe/semi-ui/lib/es/locale/source/th_TH';
+import tr_TR from '@douyinfe/semi-ui/lib/es/locale/source/tr_TR';
+import pt_BR from '@douyinfe/semi-ui/lib/es/locale/source/pt_BR';
+import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/lib/es/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/lib/es/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/lib/es/locale/source/nl_NL';
+import es from '@douyinfe/semi-ui/lib/es/locale/source/es';
+import it from '@douyinfe/semi-ui/lib/es/locale/source/it';
+import de from '@douyinfe/semi-ui/lib/es/locale/source/de';
+import fr from '@douyinfe/semi-ui/lib/es/locale/source/fr';
+import ro from '@douyinfe/semi-ui/lib/es/locale/source/ro';
+import { LocaleProvider, InputNumber, Select } from '@douyinfe/semi-ui';
+
+class I18nDemo extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            locale: zh_CN,
+            localeCode: 'zh_CN',
+        };
+        this.onLanguageChange = this.onLanguageChange.bind(this);
+    }
+
+    onLanguageChange(code) {
+        let language = {
+            'zh_CN': zh_CN,
+            'en_GB': en_GB,
+            'en_US': en_US,
+            'ko_KR': ko_KR,
+            'ja_JP': ja_JP,
+            'ar': ar,
+            'vi_VN': vi_VN,
+            'ru_RU': ru_RU,
+            'id_ID': id_ID,
+            'ms_MY': ms_MY,
+            'th_TH': th_TH,
+            'tr_TR': tr_TR,
+            'pt_BR': pt_BR,
+            'zh_TW': zh_TW,
+            'es': es,
+            'sv_SE': sv_SE,
+            'pl_PL': pl_PL,
+            'nl_NL': nl_NL,
+            de,
+            it,
+            fr,
+            ro
+        };
+        this.setState({ locale: language[code], localeCode: code });
+    }
+
+    render() {
+        const { locale, localeCode } = this.state;
+        return (
+            <>
+                <div style={{ paddingBottom: 20 }}>
+                    <Select onChange={this.onLanguageChange} insetLabel='切换语言' style={{ width: 250 }} defaultValue='zh_CN'>
+                        <Select.Option value='zh_CN'>Chinese</Select.Option>
+                        <Select.Option value='en_GB'>English</Select.Option>
+                        <Select.Option value='ja_JP'>Japanese</Select.Option>
+                        <Select.Option value='ko_KR'>Korean</Select.Option>
+                        <Select.Option value='ar'>Arabic</Select.Option>
+                        <Select.Option value='vi_VN'>Vietnamese</Select.Option>
+                        <Select.Option value='ru_RU'>Russian</Select.Option>
+                        <Select.Option value='id_ID'>Indonesian</Select.Option>
+                        <Select.Option value='ms_MY'>Malay</Select.Option>
+                        <Select.Option value='th_TH'>Thai</Select.Option>
+                        <Select.Option value='tr_TR'>Turkish</Select.Option>
+                        <Select.Option value='es'>Spanish</Select.Option>
+                        <Select.Option value='de'>German</Select.Option>
+                        <Select.Option value='it'>Italian</Select.Option>
+                        <Select.Option value='fr'>French</Select.Option>
+                        <Select.Option value='ro'>Romanian</Select.Option>
+                        <Select.Option value='sv_SE'>Swedish</Select.Option>
+                        <Select.Option value='pl_PL'>Polish</Select.Option>
+                        <Select.Option value='nl_NL'>Dutch</Select.Option>
+                    </Select>
+                </div>
+                <LocaleProvider locale={locale}>
+                    <InputNumber key={localeCode} currency={true} defaultValue={123456.78} />
+                </LocaleProvider>
+            </>
+        );
+    }
+}
+
+```
+You can also specify the currency to be displayed by manually passing localeCode and currency.
+
+```jsx live=true
+import React from 'react';
+import { InputNumber } from '@douyinfe/semi-ui';
+
+() => {
+    const defaultValue = 123456.78;
+    return (
+        <div>
+            <div>🇨🇳 CNY</div>
+            <InputNumber localeCode="zh-CN" currency="CNY" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇪🇺 EUR</div>
+            <InputNumber localeCode="de-DE" currency="EUR" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇯🇵 JPY</div>
+            <InputNumber localeCode="ja-JP" currency="JPY" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇻🇳 VND</div>
+            <InputNumber localeCode="vi-VN" currency="VND" defaultValue={defaultValue} />
+            <br />
+            <br />
+        </div>
+    );
+};
+```
+Supports three display modes: symbol, code, and name. It is controlled by the currencyDisplay property. The currency symbol is displayed by default. Set showCurrencySymbol to false to hide the display of currency symbol/code/name
+
+```jsx live=true
+import React from 'react';
+import { InputNumber } from '@douyinfe/semi-ui';    
+
+() => {
+    const defaultValue = 123456.78;
+    return (
+        <div>
+            <div>🇨🇳 CNY ➕ code</div>
+            <InputNumber currency="CNY" currencyDisplay="code" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇨🇳 CNY ➕ symbol</div>
+            <InputNumber currency="CNY" currencyDisplay="symbol" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇨🇳 CNY ➕ name</div>
+            <InputNumber currency="CNY" currencyDisplay="name" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>Hide display of currency symbols/codes/names</div>
+            <InputNumber currency="CNY" currencyDisplay="name" defaultValue={defaultValue} showCurrencySymbol={false}/>
+            <br />
+            <br />
+        </div>
+    );
+};
+```
+
 ## API Reference
 ## API Reference
 
 
 | Properties   | Instructions                                                                                    | type                              | Default   | Version    |
 | Properties   | Instructions                                                                                    | type                              | Default   | Version    |
@@ -204,6 +368,8 @@ function Demo () {
 | autofocus    | Automatic access to focus                                                                       | boolean                           | false     |            |
 | autofocus    | Automatic access to focus                                                                       | boolean                           | false     |            |
 | className    | class name of InputNumber                                                               | string  | -      |
 | className    | class name of InputNumber                                                               | string  | -      |
 | clearIcon    | Can be used to customize the clear button, valid when showClear is true                       | ReactNode                       |     | 2.25.0 |
 | clearIcon    | Can be used to customize the clear button, valid when showClear is true                       | ReactNode                       |     | 2.25.0 |
+| currency | Currency type. In international mode, currency={true} is enabled. The component will automatically display the corresponding currency type according to the locale. You can also manually pass in localeCode and currency to specify the currency type to display. The optional values ​​of currency are `CNY`,`EUR`,`USD`, etc. | boolean\|string | false | **2.77.0** |
+| currencyDisplay | Currency display method. Optional values: symbol, code, name | string | symbol | **2.77.0** |
 | defaultValue | Default                                                                                         | number                            |           |            |
 | defaultValue | Default                                                                                         | number                            |           |            |
 | disabled     | Disabled status                                                                                 | boolean                           | false     |            |
 | disabled     | Disabled status                                                                                 | boolean                           | false     |            |
 | formatter    | Specifies the format of the input box to display the value                                      | (value: number\|string) => string | -         |            |
 | formatter    | Specifies the format of the input box to display the value                                      | (value: number\|string) => string | -         |            |
@@ -211,6 +377,7 @@ function Demo () {
 | innerButtons  | Show the "up/down" button in input box when passing `true`                                 | boolean                           | false         | **1.5.0** |
 | innerButtons  | Show the "up/down" button in input box when passing `true`                                 | boolean                           | false         | **1.5.0** |
 | insetLabel   | Prefix label, lower priority than `prefix`                                                      | string\|ReactNode                 |           |            |
 | insetLabel   | Prefix label, lower priority than `prefix`                                                      | string\|ReactNode                 |           |            |
 | keepFocus    | Keep the input box focused when you click the button                                        | boolean                 |     false               | **1.10.0** |
 | keepFocus    | Keep the input box focused when you click the button                                        | boolean                 |     false               | **1.10.0** |
+| localeCode | Used to specify the country code in currency mode. Optional values ​​include `zh-CN`, `en-US`, `en-GB`, `ja-JP`, `ko-KR`, `ar`, `vi-VN`, `ru-RU`, `id-ID`, `ms-MY`, `th-TH`, `tr-TR`, `pt-BR`, `zh-TW`, `es`, `de`, `it`, `fr`, `ro`, `sv-SE`, `pl-PL`, `nl-NL`, etc. | string | - | **2.77.0** |
 | max          | Limit maximum value                                                                             | number                            | Infinity  |            |
 | max          | Limit maximum value                                                                             | number                            | Infinity  |            |
 | min          | Limit minimum value                                                                             | number                            | -Infinity |            |
 | min          | Limit minimum value                                                                             | number                            | -Infinity |            |
 | parser       | Specifies how to convert back number string from formatter and use them in conjunction with formatter | (value: string) => string         | -         |      |
 | parser       | Specifies how to convert back number string from formatter and use them in conjunction with formatter | (value: string) => string         | -         |      |
@@ -221,6 +388,7 @@ function Demo () {
 | preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean |  |  |
 | preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean |  |  |
 | shiftStep    | Step size for pressing the shift key, it can be a decimal. The default value was adjusted from 1 to 10 in v2.13                     | number                            | 10         | **1.5.0** |
 | shiftStep    | Step size for pressing the shift key, it can be a decimal. The default value was adjusted from 1 to 10 in v2.13                     | number                            | 10         | **1.5.0** |
 | showClear    | Do you show the clear button?                                                                   | boolean                           | false     | **0.35.0** |
 | showClear    | Do you show the clear button?                                                                   | boolean                           | false     | **0.35.0** |
+| showCurrencySymbol | Whether to display the currency symbol/code/name, only valid in currency mode | boolean | true | **2.77.0** |
 | size         | Enter box size, optional value: "default"\|"small"\|"large"                                     | string                            | 'default' |            |
 | size         | Enter box size, optional value: "default"\|"small"\|"large"                                     | string                            | 'default' |            |
 | step         | Each time you change the number of steps, it can be a decimal.                                  | number                            | 1         |            |
 | step         | Each time you change the number of steps, it can be a decimal.                                  | number                            | 1         |            |
 | style        | Inline style of InputNumber                                                             | CSSProperties  | -      |
 | style        | Inline style of InputNumber                                                             | CSSProperties  | -      |

+ 167 - 0
content/input/inputnumber/index.md

@@ -171,6 +171,169 @@ function Demo () {
 }
 }
 ```
 ```
 
 
+### 货币展示
+2.77.0 版本开始支持货币展示,国际化模式下通过 currency={true} 开启,组件会自动根据 localeCode 展示对应货币种类。(注意切换语言类型后需要更新组件 key 值)
+```jsx live=true
+import React from 'react';
+import zh_CN from '@douyinfe/semi-ui/lib/es/locale/source/zh_CN';
+import en_GB from '@douyinfe/semi-ui/lib/es/locale/source/en_GB';
+import en_US from '@douyinfe/semi-ui/lib/es/locale/source/en_US';
+import ko_KR from '@douyinfe/semi-ui/lib/es/locale/source/ko_KR';
+import ja_JP from '@douyinfe/semi-ui/lib/es/locale/source/ja_JP';
+import ar from '@douyinfe/semi-ui/lib/es/locale/source/ar';
+import vi_VN from '@douyinfe/semi-ui/lib/es/locale/source/vi_VN';
+import ru_RU from '@douyinfe/semi-ui/lib/es/locale/source/ru_RU';
+import id_ID from '@douyinfe/semi-ui/lib/es/locale/source/id_ID';
+import ms_MY from '@douyinfe/semi-ui/lib/es/locale/source/ms_MY';
+import th_TH from '@douyinfe/semi-ui/lib/es/locale/source/th_TH';
+import tr_TR from '@douyinfe/semi-ui/lib/es/locale/source/tr_TR';
+import pt_BR from '@douyinfe/semi-ui/lib/es/locale/source/pt_BR';
+import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/lib/es/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/lib/es/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/lib/es/locale/source/nl_NL';
+import es from '@douyinfe/semi-ui/lib/es/locale/source/es';
+import it from '@douyinfe/semi-ui/lib/es/locale/source/it';
+import de from '@douyinfe/semi-ui/lib/es/locale/source/de';
+import fr from '@douyinfe/semi-ui/lib/es/locale/source/fr';
+import ro from '@douyinfe/semi-ui/lib/es/locale/source/ro';
+import { LocaleProvider, InputNumber, Select } from '@douyinfe/semi-ui';
+
+class I18nDemo extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            locale: zh_CN,
+            localeCode: 'zh_CN',
+        };
+        this.onLanguageChange = this.onLanguageChange.bind(this);
+    }
+
+    onLanguageChange(code) {
+        let language = {
+            'zh_CN': zh_CN,
+            'en_GB': en_GB,
+            'en_US': en_US,
+            'ko_KR': ko_KR,
+            'ja_JP': ja_JP,
+            'ar': ar,
+            'vi_VN': vi_VN,
+            'ru_RU': ru_RU,
+            'id_ID': id_ID,
+            'ms_MY': ms_MY,
+            'th_TH': th_TH,
+            'tr_TR': tr_TR,
+            'pt_BR': pt_BR,
+            'zh_TW': zh_TW,
+            'es': es,
+            'sv_SE': sv_SE,
+            'pl_PL': pl_PL,
+            'nl_NL': nl_NL,
+            de,
+            it,
+            fr,
+            ro
+        };
+        this.setState({ locale: language[code], localeCode: code });
+    }
+
+    render() {
+        const { locale, localeCode } = this.state;
+        return (
+            <>
+                <div style={{ paddingBottom: 20 }}>
+                    <Select onChange={this.onLanguageChange} insetLabel='切换语言' 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>
+                </div>
+                <LocaleProvider locale={locale}>
+                    <InputNumber key={localeCode} currency={true} defaultValue={123456.78} />
+                </LocaleProvider>
+            </>
+        );
+    }
+}
+```
+也可以通过手动传 localeCode 和 currency 指定展示的货币种类
+```jsx live=true
+import React from 'react';
+import { InputNumber } from '@douyinfe/semi-ui';
+
+() => {
+    const defaultValue = 123456.78;
+    return (
+        <div>
+            <div>🇨🇳 人民币</div>
+            <InputNumber localeCode="zh-CN" currency="CNY" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇪🇺 欧元</div>
+            <InputNumber localeCode="de-DE" currency="EUR" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇯🇵 日元</div>
+            <InputNumber localeCode="ja-JP" currency="JPY" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇻🇳 越南盾</div>
+            <InputNumber localeCode="vi-VN" currency="VND" defaultValue={defaultValue} />
+            <br />
+            <br />
+        </div>
+    );
+};
+```
+支持 symbol、code、name 三种展示方式,通过 currencyDisplay 属性控制,默认以货币符号展示。showCurrencySymbol 设置为 false 隐藏货币符号/代码/名称的展示
+```jsx live=true
+import React from 'react';
+import { InputNumber } from '@douyinfe/semi-ui';    
+
+() => {
+    const defaultValue = 123456.78;
+    return (
+        <div>
+            <div>🇨🇳 CNY ➕ code</div>
+            <InputNumber currency="CNY" currencyDisplay="code" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇨🇳 CNY ➕ symbol</div>
+            <InputNumber currency="CNY" currencyDisplay="symbol" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>🇨🇳 CNY ➕ name</div>
+            <InputNumber currency="CNY" currencyDisplay="name" defaultValue={defaultValue} />
+            <br />
+            <br />
+            <div>隐藏货币符号/代码/名称的展示</div>
+            <InputNumber currency="CNY" currencyDisplay="name" defaultValue={defaultValue} showCurrencySymbol={false}/>
+            <br />
+            <br />
+        </div>
+    );
+};
+```
+
 ## API 参考
 ## API 参考
 
 
 | 属性         | 说明                                                           | 类型                              | 默认值    | 版本      |
 | 属性         | 说明                                                           | 类型                              | 默认值    | 版本      |
@@ -178,6 +341,8 @@ function Demo () {
 | autofocus    | 自动获取焦点                                                   | boolean                           | false     |           |
 | autofocus    | 自动获取焦点                                                   | boolean                           | false     |           |
 | className | 类名                                                               | string  | -      |
 | className | 类名                                                               | string  | -      |
 | clearIcon | 可用于自定义清除按钮, showClear为true时有效 | ReactNode |   | 2.25.0|
 | clearIcon | 可用于自定义清除按钮, showClear为true时有效 | ReactNode |   | 2.25.0|
+| currency | 货币种类,国际化模式下通过 currency={true} 开启,组件会自动根据 locale 展示对应货币种类, 也可以手动传入 localeCode 和 currency 指定展示的货币种类, currency 的可选值有 `CNY`,`EUR`,`USD`等| boolean\|string | false | **2.77.0** |
+| currencyDisplay | 货币展示方式,可选值:symbol、code、name | string | symbol | **2.77.0** |
 | defaultValue | 默认值                                                         | number                            |           |           |
 | defaultValue | 默认值                                                         | number                            |           |           |
 | disabled     | 禁用                                                           | boolean                           | false     |           |
 | disabled     | 禁用                                                           | boolean                           | false     |           |
 | formatter    | 指定输入框展示值的格式                                         | (value: number\|string) => string | -         |           |
 | formatter    | 指定输入框展示值的格式                                         | (value: number\|string) => string | -         |           |
@@ -185,6 +350,7 @@ function Demo () {
 | innerButtons | 为 `true` 时 “上/下” 按钮显示在输入框内部                                  | boolean                           | false     | **1.5.0** |
 | innerButtons | 为 `true` 时 “上/下” 按钮显示在输入框内部                                  | boolean                           | false     | **1.5.0** |
 | insetLabel   | 前缀标签,优先级低于 `prefix`                                  | string\|ReactNode                 |           |           |
 | insetLabel   | 前缀标签,优先级低于 `prefix`                                  | string\|ReactNode                 |           |           |
 | keepFocus    | 点击按钮时保持输入框聚焦                                        | boolean                 |     false      |   **1.10.0**        |
 | keepFocus    | 点击按钮时保持输入框聚焦                                        | boolean                 |     false      |   **1.10.0**        |
+|  localeCode    | 货币模式下用于指定国家地区代码,可选值有 `zh-CN`, `en-US`, `en-GB`, `ja-JP`, `ko-KR`, `ar`, `vi-VN`, `ru-RU`, `id-ID`, `ms-MY`, `th-TH`, `tr-TR`, `pt-BR`, `zh-TW`, `es`, `de`, `it`, `fr`, `ro`, `sv-SE`, `pl-PL`, `nl-NL`等 | string                 |     -      |   **2.77.0**  |
 | max          | 限定最大值                                                     | number                            | Infinity  |           |
 | max          | 限定最大值                                                     | number                            | Infinity  |           |
 | min          | 限定最小值                                                     | number                            | -Infinity |           |
 | min          | 限定最小值                                                     | number                            | -Infinity |           |
 | parser       | 指定从 `formatter` 里转换回数字串的方式,和 `formatter` 搭配使用 | (str: string) => string           | -         |           |
 | parser       | 指定从 `formatter` 里转换回数字串的方式,和 `formatter` 搭配使用 | (str: string) => string           | -         |           |
@@ -195,6 +361,7 @@ function Demo () {
 | preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean |  |  |
 | preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean |  |  |
 | shiftStep    | 按住 shift 键每次改变步数,可以为小数,v2.13 默认值由 1 调整为 10                           | number                            | 10         | **1.5.0** |
 | shiftStep    | 按住 shift 键每次改变步数,可以为小数,v2.13 默认值由 1 调整为 10                           | number                            | 10         | **1.5.0** |
 | showClear    | 是否显示清除按钮                                               | boolean                           | false     | **0.35.0**   |
 | showClear    | 是否显示清除按钮                                               | boolean                           | false     | **0.35.0**   |
+| showCurrencySymbol | 是否显示货币符号/代码/名称,仅货币模式下生效 | boolean | true | **2.77.0** |
 | size         | 输入框大小,可选值:"default"\|"small"\|"large"                | string                            | 'default' |           |
 | size         | 输入框大小,可选值:"default"\|"small"\|"large"                | string                            | 'default' |           |
 | step         | 每次改变步数,可以为小数                                       | number                            | 1         |           |
 | step         | 每次改变步数,可以为小数                                       | number                            | 1         |           |
 | style     | 样式                                                               | CSSProperties  | -      |
 | style     | 样式                                                               | CSSProperties  | -      |

+ 176 - 10
packages/semi-foundation/inputNumber/foundation.ts

@@ -23,7 +23,8 @@ export interface InputNumberAdapter extends DefaultAdapter {
     restoreCursor: (str?: string) => boolean;
     restoreCursor: (str?: string) => boolean;
     fixCaret: (start: number, end: number) => void;
     fixCaret: (start: number, end: number) => void;
     setClickUpOrDown: (clicked: boolean) => void;
     setClickUpOrDown: (clicked: boolean) => void;
-    updateStates: (states: BaseInputNumberState, callback?: () => void) => void
+    updateStates: (states: BaseInputNumberState, callback?: () => void) => void;
+    getInputCharacter: (index: number) => string
 }
 }
 
 
 export interface BaseInputNumberState {
 export interface BaseInputNumberState {
@@ -38,8 +39,13 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
     _interval: any;
     _interval: any;
     _timerHasRegistered: boolean;
     _timerHasRegistered: boolean;
     _timer: any;
     _timer: any;
+    _decimalPointSymbol: string = undefined;
+    _currencySymbol: string = '';
 
 
     init() {
     init() {
+        if (this._isCurrency()) {
+            this._setCurrencySymbol();
+        }
         this._setInitValue();
         this._setInitValue();
     }
     }
 
 
@@ -53,6 +59,16 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
         return this._isControlledComponent('value');
         return this._isControlledComponent('value');
     }
     }
 
 
+    _isCurrency() {
+        const { currency } = this.getProps();
+        return currency === true || (typeof currency === 'string' && currency.trim() !== '');
+    }
+
+    _getFinalCurrency() {
+        const { currency } = this.getProps();
+        return currency === true ? this.getProp('defaultCurrency') : currency;
+    }
+
     _doInput(v = '', event: any = null, updateCb: any = null) {
     _doInput(v = '', event: any = null, updateCb: any = null) {
         let notifyVal = v;
         let notifyVal = v;
         let number = v;
         let number = v;
@@ -122,6 +138,28 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
         }
         }
     }
     }
 
 
+    _setCurrencySymbol() {
+        const { localeCode, currencyDisplay } = this.getProps();
+        const parts = new Intl.NumberFormat(localeCode, {
+            style: 'currency',
+            currency: this._getFinalCurrency() || this.getCurrencyByLocaleCode(),
+            currencyDisplay
+        }).formatToParts(1234.5);
+
+        for (const part of parts) {
+            if (part.type === 'decimal') {
+                this._decimalPointSymbol = part.value;
+                console.log('this._decimalPointSymbol: ', this._decimalPointSymbol);
+            }
+            // if (part.type === 'group') {
+            //     groupSeparator = part.value;
+            // }
+            if (part.type === 'currency') {
+                this._currencySymbol = part.value;
+            }
+        }
+    }
+
     handleInputFocus(e: any) {
     handleInputFocus(e: any) {
         const value = this.getState('value');
         const value = this.getState('value');
 
 
@@ -198,7 +236,7 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
             this._adapter.setNumber(num);
             this._adapter.setNumber(num);
         }
         }
 
 
-        this._adapter.setValue(this.isControlled() ? formattedNum : this.doFormat(valueAfterParser as unknown as number, false), () => {
+        this._adapter.setValue(this.isControlled() && !this._isCurrency() ? formattedNum : this.doFormat(valueAfterParser as unknown as number, false), () => {
             this._adapter.restoreCursor();
             this._adapter.restoreCursor();
         });
         });
 
 
@@ -242,7 +280,7 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
                 numHasChanged = true;
                 numHasChanged = true;
             }
             }
 
 
-            const currentFormattedNum = this.doFormat(currentNumber, true);
+            const currentFormattedNum = this.doFormat(currentNumber, true, true);
 
 
             if (currentFormattedNum !== currentValue) {
             if (currentFormattedNum !== currentValue) {
                 willSetVal = currentFormattedNum;
                 willSetVal = currentFormattedNum;
@@ -366,14 +404,15 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
         const { defaultValue, value } = this.getProps();
         const { defaultValue, value } = this.getProps();
 
 
         const propsValue = this._isControlledComponent('value') ? value : defaultValue;
         const propsValue = this._isControlledComponent('value') ? value : defaultValue;
-        const tmpNumber = this.doParse(toString(propsValue), false, true, true);
+
+        const tmpNumber = this.doParse(this._isCurrency() ? propsValue : toString(propsValue), false, true, true);
 
 
         let number = null;
         let number = null;
         if (typeof tmpNumber === 'number' && !isNaN(tmpNumber)) {
         if (typeof tmpNumber === 'number' && !isNaN(tmpNumber)) {
             number = tmpNumber;
             number = tmpNumber;
         }
         }
 
 
-        const formattedValue = typeof number === 'number' ? this.doFormat(number, true) : '';
+        const formattedValue = typeof number === 'number' ? this.doFormat(number, true, true) : '';
 
 
         this._adapter.setNumber(number);
         this._adapter.setNumber(number);
         this._adapter.setValue(formattedValue);
         this._adapter.setValue(formattedValue);
@@ -417,7 +456,7 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
 
 
         // console.log('scale: ', scale, 'curNum: ', curNum);
         // console.log('scale: ', scale, 'curNum: ', curNum);
 
 
-        return this.doFormat(curNum, true);
+        return this.doFormat(curNum, true, true);
     }
     }
 
 
     minus(step?: number, event?: any): string {
     minus(step?: number, event?: any): string {
@@ -448,19 +487,43 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
         return toString(num);
         return toString(num);
     }
     }
 
 
+    formatCurrency(value: number | string) {
+        const { localeCode, minimumFractionDigits, precision, maximumFractionDigits, currencyDisplay, showCurrencySymbol } = this.getProps();
+
+        let formattedValue = value;
+        if (typeof value === 'string' && Number.isNaN(Number(value))) {
+            formattedValue = this.parseInternationalCurrency(value);
+        }
+
+        const formatter = new Intl.NumberFormat(localeCode, {
+            style: 'currency',
+            currency: this._getFinalCurrency() || this.getCurrencyByLocaleCode(),
+            currencyDisplay: currencyDisplay,
+            minimumFractionDigits: minimumFractionDigits || precision || undefined,
+            maximumFractionDigits: maximumFractionDigits || precision || undefined,
+        });
+
+        const formatted = formatter.format(Number(formattedValue)); 
+        return showCurrencySymbol ? formatted : formatted.replace(this._currencySymbol, '').trim();
+    }
+
     /**
     /**
      * format number to string
      * format number to string
      * @param {string|number} value
      * @param {string|number} value
      * @param {boolean} needAdjustPrec
      * @param {boolean} needAdjustPrec
      * @returns {string}
      * @returns {string}
      */
      */
-    doFormat(value: string | number = 0, needAdjustPrec = true): string {
+    doFormat(value: string | number = 0, needAdjustPrec = true, needAdjustCurrency = false): string {
         // if (typeof value === 'string') {
         // if (typeof value === 'string') {
         //     return value;
         //     return value;
         // }
         // }
+        const { formatter } = this.getProps();
         let str;
         let str;
-        const formatter = this.getProp('formatter');
-        if (needAdjustPrec) {
+
+        // AdjustCurrency conversion is done only in blur situation, otherwise it is just converted to normal string
+        if (this._isCurrency() && needAdjustCurrency) {
+            str = this.formatCurrency(value);
+        } else if (needAdjustPrec) {
             str = this._adjustPrec(value);
             str = this._adjustPrec(value);
         } else {
         } else {
             str = toString(value);
             str = toString(value);
@@ -487,6 +550,22 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
         return current;
         return current;
     }
     }
 
 
+    // 将货币模式的货币转化为纯数字
+    // Convert currency in currency mode to pure numbers
+    // eg:¥123456.78 to 123456.78
+    // eg:123456.78 to 123456.78
+    parseInternationalCurrency(currencyString: string) {
+        let cleaned = currencyString
+            .replace(this._currencySymbol, '')
+            .replace(new RegExp(`[^\\d${this._decimalPointSymbol}\\-]`, 'g'), '');
+
+        // Convert the localized decimal point to the standard decimal point
+        if (this._decimalPointSymbol && this._decimalPointSymbol !== '.') {
+            cleaned = cleaned.replace(this._decimalPointSymbol, '.');
+        }
+        return parseFloat(cleaned);
+    }
+      
     /**
     /**
      * parse to number
      * parse to number
      * @param {string|number} value
      * @param {string|number} value
@@ -496,6 +575,11 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
      * @returns {number}
      * @returns {number}
      */
      */
     doParse(value: string | number, needCheckPrec = true, needAdjustPrec = false, needAdjustMaxMin = false) {
     doParse(value: string | number, needCheckPrec = true, needAdjustPrec = false, needAdjustMaxMin = false) {
+
+        if (this._isCurrency() && typeof value === 'string') {
+            value = this.parseInternationalCurrency(value);
+        }
+
         if (typeof value === 'number') {
         if (typeof value === 'number') {
             if (needAdjustMaxMin) {
             if (needAdjustMaxMin) {
                 value = this.fetchMinOrMax(value);
                 value = this.fetchMinOrMax(value);
@@ -557,7 +641,11 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
             return value;
             return value;
         }
         }
         if (typeof value === 'string') {
         if (typeof value === 'string') {
-            const parser = this.getProp('parser');
+            const { parser } = this.getProps();
+
+            if (this._isCurrency()) {
+                value = this.parseInternationalCurrency(value);
+            }
 
 
             if (typeof parser === 'function') {
             if (typeof parser === 'function') {
                 value = parser(value);
                 value = parser(value);
@@ -630,6 +718,84 @@ class InputNumberFoundation extends BaseFoundation<InputNumberAdapter> {
     updateStates(states: BaseInputNumberState, callback?: () => void) {
     updateStates(states: BaseInputNumberState, callback?: () => void) {
         this._adapter.updateStates(states, callback);
         this._adapter.updateStates(states, callback);
     }
     }
+
+    /**
+     * Get currency by locale code
+     * @param {string} localeCode
+     * @returns {string}
+     */
+    getCurrencyByLocaleCode() {
+        const { localeCode } = this.getProps();
+
+        // Mapping table of region codes to currency codes
+        const localeToCurrency: Record<string, string> = {
+            // Asia
+            'zh-CN': 'CNY', // China
+            'zh-HK': 'HKD', // Hong Kong
+            'zh-TW': 'TWD', // Taiwan
+            'ja-JP': 'JPY', // Japan
+            'ko-KR': 'KRW', // Korea
+            'th-TH': 'THB', // Thailand
+            'vi-VN': 'VND', // Vietnam
+            'ms-MY': 'MYR', // Malaysia
+            'id-ID': 'IDR', // Indonesia
+            'hi-IN': 'INR', // India
+            'ar-SA': 'SAR', // Saudi Arabia
+        
+            // Europe
+            'en-GB': 'GBP', // United Kingdom
+            'de-DE': 'EUR', // Germany
+            'fr-FR': 'EUR', // France
+            'it-IT': 'EUR', // Italy
+            'es-ES': 'EUR', // Spain
+            'pt-PT': 'EUR', // Portugal
+            'ru-RU': 'RUB', // 俄罗斯
+        
+            // North America
+            'en-US': 'USD', // United States
+            'en-CA': 'CAD', // Canada
+            'es-MX': 'MXN', // Mexico
+        
+            // South America
+            'pt-BR': 'BRL', // Brazil
+            'es-AR': 'ARS', // Argentina
+        
+            // Oceania
+            'en-AU': 'AUD', // Australia
+            'en-NZ': 'NZD', // New Zealand
+        
+            // Africa
+            'en-ZA': 'ZAR', // South Africa
+            'ar-EG': 'EGP', // Egypt
+        };
+    
+        // Try to match the full region code directly
+        if (localeToCurrency[localeCode]) {
+            return localeToCurrency[localeCode];
+        }
+    
+        // If no direct match, try to match the language part (the first two characters)
+        const languageCode = localeCode.split('-')[0];
+        const fallbackMap: Record<string, string> = {
+            'en': 'USD', // English defaults to USD
+            'zh': 'CNY', // Chinese defaults to CNY
+            'es': 'EUR', // Spanish defaults to EUR
+            'fr': 'EUR', // French defaults to EUR
+            'de': 'EUR', // German defaults to EUR
+            'it': 'EUR', // Italian defaults to EUR
+            'ja': 'JPY', // Japanese defaults to JPY
+            'ko': 'KRW', // Korean defaults to KRW
+            'ru': 'RUB', // Russian defaults to RUB
+            'ar': 'SAR', // Arabic defaults to SAR
+        };
+    
+        if (fallbackMap[languageCode]) {
+            return fallbackMap[languageCode];
+        }
+    
+        // If no match, return USD as the default value
+        return 'USD';
+    }
 }
 }
 
 
 export default InputNumberFoundation;
 export default InputNumberFoundation;

+ 2 - 2
packages/semi-ui/input/index.tsx

@@ -479,7 +479,7 @@ class Input extends BaseComponent<InputProps, InputState> {
             [`${wrapperPrefix}-hidden`]: type === 'hidden',
             [`${wrapperPrefix}-hidden`]: type === 'hidden',
             [`${wrapperPrefix}-${size}`]: size,
             [`${wrapperPrefix}-${size}`]: size,
             [`${prefixCls}-borderless`]: borderless,
             [`${prefixCls}-borderless`]: borderless,
-            [`${prefixCls}-only_border`]: onlyBorder!==undefined && onlyBorder!==null,
+            [`${prefixCls}-only_border`]: onlyBorder !== undefined && onlyBorder !== null,
         });
         });
         const inputCls = cls(prefixCls, {
         const inputCls = cls(prefixCls, {
             [`${prefixCls}-${size}`]: size,
             [`${prefixCls}-${size}`]: size,
@@ -516,7 +516,7 @@ class Input extends BaseComponent<InputProps, InputState> {
         }
         }
 
 
         let wrapperStyle = { ...style };
         let wrapperStyle = { ...style };
-        if (onlyBorder!==undefined) {
+        if (onlyBorder !== undefined) {
             wrapperStyle = {
             wrapperStyle = {
                 borderWidth: onlyBorder,
                 borderWidth: onlyBorder,
                 ...style
                 ...style

+ 132 - 0
packages/semi-ui/inputNumber/__test__/inputNumber.test.js

@@ -431,4 +431,136 @@ describe(`InputNumber`, () => {
         expect(spyChange.getCall(0).args[0]).toEqual(1);
         expect(spyChange.getCall(0).args[0]).toEqual(1);
         expect(formApi.getValue('minControlled')).toBe(1);
         expect(formApi.getValue('minControlled')).toBe(1);
     });
     });
+
+    it('Common currency display', () => {
+        const defaultValue = 123456.78;
+        let inputNumber = mount(<InputNumber currency="CNY" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('¥123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="en-US" currency="USD" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('$123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="de-DE" currency="EUR" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.456,78 €');
+
+        inputNumber = mount(<InputNumber localeCode="ja-JP" currency="JPY" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('¥123,457');
+
+        inputNumber = mount(<InputNumber localeCode="vi-VN" currency="VND" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.457 ₫');  
+
+        inputNumber = mount(<InputNumber localeCode="th-TH" currency="THB" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('฿123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="id-ID" currency="IDR" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('Rp 123.456,78');
+        
+    });
+
+    it('Common currency display defaultValue is string', () => {
+        const defaultValue = 123456.78;
+        let inputNumber = mount(<InputNumber currency="CNY" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('¥123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="en-US" currency="USD" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('$123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="de-DE" currency="EUR" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.456,78 €');
+
+        inputNumber = mount(<InputNumber localeCode="ja-JP" currency="JPY" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('¥123,457');
+
+        inputNumber = mount(<InputNumber localeCode="vi-VN" currency="VND" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.457 ₫');  
+
+        inputNumber = mount(<InputNumber localeCode="th-TH" currency="THB" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('฿123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="id-ID" currency="IDR" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('Rp 123.456,78');
+        
+    });
+
+    it('Uncontrolled + add/minus button', () => {
+        const defaultValue = 123459.78;
+        let inputNumber = mount(<InputNumber localeCode="en-US" currency="USD" defaultValue={defaultValue} />);
+
+        const inputElem = inputNumber.find('input');
+        const btns = inputNumber.find(`.${BASE_CLASS_PREFIX}-input-number-suffix-btns .${BASE_CLASS_PREFIX}-input-number-button`);
+        const addBtn = btns.first();
+        const minusBtn = btns.last();
+
+        _.times(1, () => addBtn.simulate('mousedown', { button: numbers.MOUSE_BUTTON_LEFT  }));
+        expect(inputElem.instance().value).toBe('$123,460.78');
+        _.times(3, () => minusBtn.simulate('mousedown', { button: numbers.MOUSE_BUTTON_LEFT }));
+        expect(inputElem.instance().value).toBe('$123,457.78');
+        
+    });
+
+     it('Controlled + add/minus button', () => {
+        const defaultValue = 123459.78;
+        const spyChange = sinon.spy();
+        let inputNumber = mount(<InputNumber localeCode="en-US" currency="USD" value={defaultValue} onChange={spyChange} />);
+
+        const inputElem = inputNumber.find('input');
+        const btns = inputNumber.find(`.${BASE_CLASS_PREFIX}-input-number-suffix-btns .${BASE_CLASS_PREFIX}-input-number-button`);
+        const addBtn = btns.first();
+        const minusBtn = btns.last();
+
+        _.times(1, () => {
+            addBtn.simulate('mousedown', { button: numbers.MOUSE_BUTTON_LEFT  })
+            const updatedValue = spyChange.lastCall.args[0];
+            inputNumber.setProps({ value: updatedValue });
+        });
+        expect(spyChange.callCount).toBe(2);
+        expect(inputElem.instance().value).toBe('$123,460.78');
+
+        _.times(3, () => {
+            minusBtn.simulate('mousedown', { button: numbers.MOUSE_BUTTON_LEFT });
+            // 每次点击后模拟值更新
+            const updatedValue = spyChange.lastCall.args[0];
+            inputNumber.setProps({ value: updatedValue });
+        });
+        expect(spyChange.callCount).toBe(5);
+        expect(inputElem.instance().value).toBe('$123,457.78');
+     });
+
+     it('Common currency display with showCurrencySymbol=false', () => {
+        const defaultValue = 123456.78;
+        let inputNumber = mount(<InputNumber currency="CNY" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="en-US" currency="USD" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="de-DE" currency="EUR" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.456,78');
+
+        inputNumber = mount(<InputNumber localeCode="ja-JP" currency="JPY" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123,457');
+
+        inputNumber = mount(<InputNumber localeCode="vi-VN" currency="VND" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.457');  
+
+        inputNumber = mount(<InputNumber localeCode="th-TH" currency="THB" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123,456.78');
+
+        inputNumber = mount(<InputNumber localeCode="id-ID" currency="IDR" defaultValue={defaultValue} showCurrencySymbol={false} />);
+        expect(inputNumber.find('input').instance().value).toBe('123.456,78');
+    });
+
+    it('CNY currency with different currencyDisplay options', () => {
+        const defaultValue = 123456.78;
+        
+        let inputNumber = mount(<InputNumber currency="CNY" defaultValue={defaultValue} />);
+        expect(inputNumber.find('input').instance().value).toBe('¥123,456.78');
+        
+        inputNumber = mount(<InputNumber currency="CNY" defaultValue={defaultValue} currencyDisplay="code" />);
+        expect(inputNumber.find('input').instance().value).toBe('CNY 123,456.78');
+        
+        inputNumber = mount(<InputNumber currency="CNY" defaultValue={defaultValue} currencyDisplay="name" />);
+        expect(inputNumber.find('input').instance().value).toBe('123,456.78人民币');
+    });
+
 });
 });

+ 163 - 1
packages/semi-ui/inputNumber/_story/inputNumber.stories.jsx

@@ -795,4 +795,166 @@ export const Fix1772 = () => {
       step={60000}
       step={60000}
     />
     />
   );
   );
-}
+}
+
+export const BasicCurrency = () => {
+  return (
+    <div>
+      <div>🇨🇳 人民币 ➕ 非受控</div>
+      <InputNumber 
+      currency="CNY" 
+      defaultValue={123456.78} 
+      onChange={v => {console.log('onChange', v); }}
+      onNumberChange={v => {console.log('onNumberChange', v);}}
+      onBlur={() => {console.log('onBlur');}}
+      onFocus={() => {console.log('onFocus');}}
+    />
+    </div>
+  );
+}
+export const TypicalCurrency = () => {
+  return (
+    <div>
+      <div>🇨🇳 人民币</div>
+      <InputNumber localeCode="zh-CN" currency="CNY" defaultValue={123456.78} />
+      <br />
+      <br />
+      <div>🇪🇺 欧元</div>
+      <InputNumber localeCode="de-DE" currency="EUR" defaultValue={123456.78} />
+      <br />
+      <br />
+      <div>🇯🇵 日元</div>
+      <InputNumber localeCode="ja-JP" currency="JPY" defaultValue={123456.78} />
+      <br />
+      <br />
+      <div>🇻🇳 越南盾</div>
+      <InputNumber localeCode="vi-VN" currency="VND" defaultValue={123456.78} />
+      <br />
+      <br />
+    </div>
+  );  
+}
+
+export const CommonCurrency = () => {
+  const [value1, setValue1] = useState(123456.78);
+  const [value2, setValue2] = useState(123456.78);
+
+  return (
+    <div>
+      <div>🇺🇸 美元 + 受控</div>
+      <InputNumber  localeCode="en-US" currency="USD" value={value1} 
+        onChange={v => {console.log('onChange', v); 
+          setValue1(v)
+        }}
+      />
+      <br />
+      <br />
+      <div>🇨🇳 人民币 + 受控</div>
+      <InputNumber  currency="CNY" value={value2} onChange={v => {console.log('onChange', v); setValue2(v)}}/>
+      <br />
+      <br />
+      <div>🇨🇳 人民币 + 非受控</div>
+      <InputNumber  currency="CNY" defaultValue={'123456.78'} onChange={v => {console.log('onChange', v);}}/>
+      <br />
+      <br />
+      <div>🇪🇺 欧元</div>
+      <InputNumber  localeCode="de-DE" currency="EUR" defaultValue={123456.78} />
+      <br />
+      <br />
+      <div>🇹🇭 泰铢</div>
+      <InputNumber  localeCode="th-TH" currency="THB" defaultValue={123456.78} onChange={v => console.log('onChange', v)}/>
+      <br />
+      <br />
+      <div>🇮🇩 印尼盾</div> 
+      <InputNumber  localeCode="id-ID" currency="IDR" defaultValue={123456.78} />
+      <br />
+      <br />
+      <div>🇯🇵 日元</div>
+      <InputNumber localeCode="ja-JP" currency="JPY" defaultValue={123456.78}  onChange={v => console.log('onChange', v)}/>
+      <br />
+      <br />
+      <div>🇻🇳 越南盾</div>
+      <InputNumber  localeCode="vi-VN" currency="VND" defaultValue={123456.78} />
+      <br />
+      <br />
+    </div>
+  );
+}
+
+export const ShowCurrencySymbol = () => {
+  const [value1, setValue1] = useState(123456.78);
+  const [value2, setValue2] = useState(123456.78);
+
+  return (
+    <div>
+      <div>🇺🇸 美元 + 受控</div>
+      <InputNumber  localeCode="en-US" currency="USD" value={value1} 
+        onChange={v => {console.log('onChange', v); 
+          setValue1(v)
+        }}
+        showCurrencySymbol={false}
+      />
+      <br />
+      <br />
+      <div>🇨🇳 人民币 + 受控</div>
+      <InputNumber currency="CNY" value={value2} showCurrencySymbol={false} onChange={v => {console.log('onChange', v); setValue2(v)}}/>
+      <br />
+      <br />
+      <div>🇨🇳 人民币 + 非受控</div>
+      <InputNumber currency="CNY" defaultValue={'123456.78'} showCurrencySymbol={false} onChange={v => {console.log('onChange', v);}}/>
+      <br />
+      <br />
+      <div>🇪🇺 欧元</div>
+      <InputNumber  localeCode="de-DE" currency="EUR" defaultValue={123456.78} showCurrencySymbol={false} />
+      <br />
+      <br />
+      <div>🇹🇭 泰铢</div>
+      <InputNumber  localeCode="th-TH" currency="THB" defaultValue={123456.78} showCurrencySymbol={false} onChange={v => console.log('onChange', v)}/>
+      <br />
+      <br />
+      <div>🇮🇩 印尼盾</div> 
+      <InputNumber  localeCode="id-ID" currency="IDR" defaultValue={123456.78} showCurrencySymbol={false} />
+      <br />
+      <br />
+      <div>🇯🇵 日元</div>
+      <InputNumber localeCode="ja-JP" currency="JPY" defaultValue={123456.78}  showCurrencySymbol={false} onChange={v => console.log('onChange', v)}/>
+      <br />
+      <br />
+      <div>🇻🇳 越南盾</div>
+      <InputNumber  localeCode="vi-VN" currency="VND" defaultValue={123456.78} showCurrencySymbol={false} />
+      <br />
+      <br />
+    </div>
+  );
+}
+
+export const CurrencyDisplay = () => {
+  return (
+    <div>
+      <div>🇨🇳 CNY ➕ code</div>
+      <InputNumber 
+        currency="CNY" 
+        currencyDisplay="code"
+        defaultValue={123456.78}
+      />
+      <br />
+      <br />
+      <div>🇨🇳 CNY ➕ symbol</div>
+      <InputNumber 
+        currency="CNY" 
+        currencyDisplay="symbol" 
+        defaultValue={123456.78}
+      />
+      <br />
+      <br />
+      <div>🇨🇳 CNY ➕ name</div>
+      <InputNumber 
+        currency="CNY" 
+        currencyDisplay="name" 
+        defaultValue={123456.78}
+      />
+      <br />
+      <br />
+    </div>
+  );
+}

+ 35 - 3
packages/semi-ui/inputNumber/index.tsx

@@ -14,12 +14,17 @@ import { IconChevronUp, IconChevronDown } from '@douyinfe/semi-icons';
 import '@douyinfe/semi-foundation/inputNumber/inputNumber.scss';
 import '@douyinfe/semi-foundation/inputNumber/inputNumber.scss';
 import { isNaN, isString, noop } from 'lodash';
 import { isNaN, isString, noop } from 'lodash';
 import { ArrayElement } from '../_base/base';
 import { ArrayElement } from '../_base/base';
+import LocaleConsumer from '../locale/localeConsumer';
+import { Locale } from '../locale/interface';
 
 
 export interface InputNumberProps extends InputProps {
 export interface InputNumberProps extends InputProps {
     autofocus?: boolean;
     autofocus?: boolean;
     className?: string;
     className?: string;
     clearIcon?: React.ReactNode;
     clearIcon?: React.ReactNode;
+    currency?: string | boolean;
+    currencyDisplay?: 'code' | 'symbol' | 'name';
     defaultValue?: number | string;
     defaultValue?: number | string;
+    defaultCurrency?: string;
     disabled?: boolean;
     disabled?: boolean;
     formatter?: (value: number | string) => string;
     formatter?: (value: number | string) => string;
     forwardedRef?: React.MutableRefObject<HTMLInputElement> | ((instance: HTMLInputElement) => void);
     forwardedRef?: React.MutableRefObject<HTMLInputElement> | ((instance: HTMLInputElement) => void);
@@ -28,8 +33,11 @@ export interface InputNumberProps extends InputProps {
     insetLabel?: React.ReactNode;
     insetLabel?: React.ReactNode;
     insetLabelId?: string;
     insetLabelId?: string;
     keepFocus?: boolean;
     keepFocus?: boolean;
+    localeCode?: string;
     max?: number;
     max?: number;
     min?: number;
     min?: number;
+    minimumFractionDigits?: number;
+    maximumFractionDigits?: number;
     parser?: (value: string) => string;
     parser?: (value: string) => string;
     precision?: number;
     precision?: number;
     prefixCls?: string;
     prefixCls?: string;
@@ -37,6 +45,7 @@ export interface InputNumberProps extends InputProps {
     pressTimeout?: number;
     pressTimeout?: number;
     shiftStep?: number;
     shiftStep?: number;
     showClear?: boolean;
     showClear?: boolean;
+    showCurrencySymbol?: boolean;
     size?: ArrayElement<typeof strings.SIZE>;
     size?: ArrayElement<typeof strings.SIZE>;
     step?: number;
     step?: number;
     style?: React.CSSProperties;
     style?: React.CSSProperties;
@@ -82,6 +91,7 @@ class InputNumber extends BaseComponent<InputNumberProps, InputNumberState> {
         pressTimeout: PropTypes.number,
         pressTimeout: PropTypes.number,
         preventScroll: PropTypes.bool,
         preventScroll: PropTypes.bool,
         shiftStep: PropTypes.number,
         shiftStep: PropTypes.number,
+        showCurrencySymbol: PropTypes.bool,
         step: PropTypes.number,
         step: PropTypes.number,
         style: PropTypes.object,
         style: PropTypes.object,
         suffix: PropTypes.any,
         suffix: PropTypes.any,
@@ -104,6 +114,7 @@ class InputNumber extends BaseComponent<InputNumberProps, InputNumberState> {
         pressInterval: numbers.DEFAULT_PRESS_TIMEOUT,
         pressInterval: numbers.DEFAULT_PRESS_TIMEOUT,
         pressTimeout: numbers.DEFAULT_PRESS_TIMEOUT,
         pressTimeout: numbers.DEFAULT_PRESS_TIMEOUT,
         shiftStep: numbers.DEFAULT_SHIFT_STEP,
         shiftStep: numbers.DEFAULT_SHIFT_STEP,
+        showCurrencySymbol: true,
         size: strings.DEFAULT_SIZE,
         size: strings.DEFAULT_SIZE,
         step: numbers.DEFAULT_STEP,
         step: numbers.DEFAULT_STEP,
         onBlur: noop,
         onBlur: noop,
@@ -144,6 +155,9 @@ class InputNumber extends BaseComponent<InputNumberProps, InputNumberState> {
                     this.adapter.setCache(eventName, null);
                     this.adapter.setCache(eventName, null);
                 }
                 }
             },
             },
+            getInputCharacter: (index: number) => {
+                return this.inputNode.value[index];
+            },
             recordCursorPosition: () => {
             recordCursorPosition: () => {
                 // Record position
                 // Record position
                 try {
                 try {
@@ -316,7 +330,7 @@ class InputNumber extends BaseComponent<InputNumberProps, InputNumberState> {
                         this.foundation.updateStates({ value: valueStr });
                         this.foundation.updateStates({ value: valueStr });
                     }
                     }
                 } else if (this.foundation.isValidNumber(parsedNum)) {
                 } else if (this.foundation.isValidNumber(parsedNum)) {
-                    newValue = this.foundation.doFormat(parsedNum);
+                    newValue = this.foundation.doFormat(parsedNum, true, true);
                     this.foundation.updateStates({ number: parsedNum, value: newValue });
                     this.foundation.updateStates({ number: parsedNum, value: newValue });
                 } else {
                 } else {
                     // Invalid digital analog blurring effect instead of controlled failure
                     // Invalid digital analog blurring effect instead of controlled failure
@@ -325,7 +339,19 @@ class InputNumber extends BaseComponent<InputNumberProps, InputNumberState> {
                 }
                 }
             }
             }
             if (newValue && isString(newValue) && newValue !== String(this.props.value)) {
             if (newValue && isString(newValue) && newValue !== String(this.props.value)) {
-                this.foundation.notifyChange(newValue, null);
+                if (this.foundation._isCurrency()) {
+                    // 仅在解析后的数值而不是格式化的字符串变化时 notifyChange
+                    // notifyChange only when the parsed value changes, not the formatted string
+                    const parsedNewValue = this.foundation.doParse(newValue);
+                    const parsedPropValue = typeof this.props.value === 'string' ? 
+                        this.foundation.doParse(this.props.value) : this.props.value;
+                    
+                    if (parsedNewValue !== parsedPropValue) {
+                        this.foundation.notifyChange(newValue, null);
+                    }
+                } else {
+                    this.foundation.notifyChange(newValue, null);
+                }
             }
             }
         }
         }
 
 
@@ -505,7 +531,13 @@ class InputNumber extends BaseComponent<InputNumberProps, InputNumberState> {
 
 
 export default forwardStatics(
 export default forwardStatics(
     React.forwardRef<HTMLInputElement, InputNumberProps>(function SemiInputNumber(props, ref) {
     React.forwardRef<HTMLInputElement, InputNumberProps>(function SemiInputNumber(props, ref) {
-        return <InputNumber {...props} forwardedRef={ref} />;
+        return (
+            <LocaleConsumer<Locale['InputNumber']> componentName="InputNumber">
+                {(locale: Locale['InputNumber'], localeCode: string, dateFnsLocale, currency: string) => (
+                    <InputNumber localeCode={localeCode} defaultCurrency={currency} {...props} forwardedRef={ref}/>
+                )}
+            </LocaleConsumer>
+        );
     }),
     }),
     InputNumber
     InputNumber
 );
 );

+ 5 - 2
packages/semi-ui/locale/_story/locale.stories.jsx

@@ -21,6 +21,7 @@ import {
     Image,
     Image,
     Form,
     Form,
     Nav,
     Nav,
+    InputNumber
 } from '../../index';
 } from '../../index';
 
 
 import zh_CN from '@douyinfe/semi-ui/locale/source/zh_CN';
 import zh_CN from '@douyinfe/semi-ui/locale/source/zh_CN';
@@ -327,7 +328,7 @@ const treeData = [
     },
     },
 ];
 ];
 
 
-const I18nComponent2 = () => {
+const I18nComponent2 = (props) => {
     const [modalVisible, setModalVisible] = useState(false);
     const [modalVisible, setModalVisible] = useState(false);
     const columns = useMemo(() => [
     const columns = useMemo(() => [
         {
         {
@@ -395,6 +396,8 @@ const I18nComponent2 = () => {
                     <p>More content...</p>
                     <p>More content...</p>
                 </Modal>
                 </Modal>
             </div>
             </div>
+            <h5>InputNumber</h5>
+            <InputNumber mode="currency" key={props.localeCode} defaultValue={1234567.89} />
             <h5>Select & Cascader</h5>
             <h5>Select & Cascader</h5>
             <div style={style}>
             <div style={style}>
                 <Select filter style={{ width: '180px' }}>
                 <Select filter style={{ width: '180px' }}>
@@ -556,7 +559,7 @@ class I18nDemo extends React.Component {
                 </div>
                 </div>
                 <LocaleProvider locale={locale}>
                 <LocaleProvider locale={locale}>
                     <ConfigProvider direction={localeCode === 'ar' ? 'rtl' : 'ltr'} locale={locale}>
                     <ConfigProvider direction={localeCode === 'ar' ? 'rtl' : 'ltr'} locale={locale}>
-                        <I18nComponent2 />
+                        <I18nComponent2 localeCode={localeCode}/>
                     </ConfigProvider>
                     </ConfigProvider>
                 </LocaleProvider>
                 </LocaleProvider>
             </>
             </>

+ 3 - 0
packages/semi-ui/locale/interface.ts

@@ -3,6 +3,7 @@ import { Locale as dateFnsLocale } from 'date-fns';
 export interface Locale {
 export interface Locale {
     code: string;
     code: string;
     dateFnsLocale: dateFnsLocale;
     dateFnsLocale: dateFnsLocale;
+    currency: string;
     Pagination: {
     Pagination: {
         pageSize: string;
         pageSize: string;
         total: string;
         total: string;
@@ -180,5 +181,7 @@ export interface Locale {
         copy: string;
         copy: string;
         copied: string;
         copied: string;
         dropAreaText: string
         dropAreaText: string
+    };
+    InputNumber: {
     }
     }
 }
 }

+ 3 - 2
packages/semi-ui/locale/localeConsumer.tsx

@@ -7,7 +7,7 @@ import ConfigContext from '../configProvider/context';
 import DefaultLocale from './source/zh_CN';
 import DefaultLocale from './source/zh_CN';
 import { Locale } from './interface';
 import { Locale } from './interface';
 
 
-type ChildrenRender<T> = (componentLocal: T, localeCode: string, dateFnsLocale: dateFns) => React.ReactNode;
+type ChildrenRender<T> = (componentLocal: T, localeCode: string, dateFnsLocale: dateFns, currency: string) => React.ReactNode;
 export interface LocaleConsumerProps<T> {
 export interface LocaleConsumerProps<T> {
     componentName: string;
     componentName: string;
     children?: ChildrenRender<T>
     children?: ChildrenRender<T>
@@ -40,7 +40,8 @@ export default class LocaleConsumer<T> extends Component<LocaleConsumerProps<T>>
          */
          */
         const defaultFnsLocale = get(DefaultLocale, 'dateFnsLocale');
         const defaultFnsLocale = get(DefaultLocale, 'dateFnsLocale');
         const dateFnsLocale = get(locale, 'dateFnsLocale', defaultFnsLocale);
         const dateFnsLocale = get(locale, 'dateFnsLocale', defaultFnsLocale);
-        return children(locale[componentName], locale.code, dateFnsLocale);
+        const currency = get(locale, 'currency');  
+        return children(locale[componentName], locale.code, dateFnsLocale, currency);
     }
     }
 
 
     render() {
     render() {

+ 2 - 0
packages/semi-ui/locale/source/ar.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'ar',
     code: 'ar',
     dateFnsLocale: arSA,
     dateFnsLocale: arSA,
+    currency: 'SAR',
     Pagination: {
     Pagination: {
         pageSize: 'العناصر في كل صفحة: ${pageSize}',
         pageSize: 'العناصر في كل صفحة: ${pageSize}',
         total: 'إجمالي الصفحات: ${total}',
         total: 'إجمالي الصفحات: ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'نسخ',
         copied: 'نسخ',
         dropAreaText: 'ضع الملف هنا',
         dropAreaText: 'ضع الملف هنا',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Arabic]
 // [i18n-Arabic]

+ 2 - 0
packages/semi-ui/locale/source/de.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'de',
     code: 'de',
     dateFnsLocale: de,
     dateFnsLocale: de,
+    currency: 'EUR',
     Pagination: {
     Pagination: {
         pageSize: 'Elemente pro Seite: ${pageSize}',
         pageSize: 'Elemente pro Seite: ${pageSize}',
         total: 'Seiten gesamt: ${total}',
         total: 'Seiten gesamt: ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'Kopiert',
         copied: 'Kopiert',
         dropAreaText: 'Datei hier ablegen',
         dropAreaText: 'Datei hier ablegen',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-German]
 // [i18n-German]

+ 3 - 1
packages/semi-ui/locale/source/en_GB.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'en-GB',
     code: 'en-GB',
     dateFnsLocale: enGB,
     dateFnsLocale: enGB,
+    currency: 'GBP',
     Pagination: {
     Pagination: {
         pageSize: 'Items per page: ${pageSize}',
         pageSize: 'Items per page: ${pageSize}',
         total: 'Total pages: ${total}',
         total: 'Total pages: ${total}',
@@ -181,7 +182,8 @@ const local: Locale = {
         copy: 'Copy',
         copy: 'Copy',
         copied: 'Copied',
         copied: 'Copied',
         dropAreaText: 'Put the file here',
         dropAreaText: 'Put the file here',
-    }
+    },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-English(GB)]
 // [i18n-English(GB)]

+ 3 - 1
packages/semi-ui/locale/source/en_US.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'en-US',
     code: 'en-US',
     dateFnsLocale: enUS,
     dateFnsLocale: enUS,
+    currency: 'USD',
     Pagination: {
     Pagination: {
         pageSize: 'Items per page: ${pageSize}',
         pageSize: 'Items per page: ${pageSize}',
         total: 'Total pages: ${total}',
         total: 'Total pages: ${total}',
@@ -181,7 +182,8 @@ const local: Locale = {
         copy: 'Copy',
         copy: 'Copy',
         copied: 'Copied',
         copied: 'Copied',
         dropAreaText: 'Put the file here',
         dropAreaText: 'Put the file here',
-    }
+    },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-English(US)]
 // [i18n-English(US)]

+ 2 - 0
packages/semi-ui/locale/source/es.ts

@@ -9,6 +9,7 @@ import { Locale } from '../interface';
 const locale: Locale = {
 const locale: Locale = {
     code: 'es',
     code: 'es',
     dateFnsLocale: es,
     dateFnsLocale: es,
+    currency: 'EUR',
     Pagination: {
     Pagination: {
         pageSize: 'Elementos por página: ${pageSize}',
         pageSize: 'Elementos por página: ${pageSize}',
         total: 'Páginas totales: ${total}',
         total: 'Páginas totales: ${total}',
@@ -187,6 +188,7 @@ const locale: Locale = {
         copied: 'Copiado',
         copied: 'Copiado',
         dropAreaText: 'Coloca el archivo aquí',
         dropAreaText: 'Coloca el archivo aquí',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 export default locale;
 export default locale;

+ 2 - 0
packages/semi-ui/locale/source/fr.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'fr',
     code: 'fr',
     dateFnsLocale: fr,
     dateFnsLocale: fr,
+    currency: 'EUR',
     Pagination: {
     Pagination: {
         pageSize: 'Éléments par page : ${pageSize}',
         pageSize: 'Éléments par page : ${pageSize}',
         total: 'Total des pages : ${total}',
         total: 'Total des pages : ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'Copié',
         copied: 'Copié',
         dropAreaText: 'Déposez le fichier ici',
         dropAreaText: 'Déposez le fichier ici',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-French]
 // [i18n-French]

+ 2 - 0
packages/semi-ui/locale/source/id_ID.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'id-ID',
     code: 'id-ID',
     dateFnsLocale: id,
     dateFnsLocale: id,
+    currency: 'IDR',
     Pagination: {
     Pagination: {
         pageSize: 'Item per halaman: ${pageSize}',
         pageSize: 'Item per halaman: ${pageSize}',
         total: 'Total halaman: ${total}',
         total: 'Total halaman: ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'Disalin',
         copied: 'Disalin',
         dropAreaText: 'Letakkan file di sini',
         dropAreaText: 'Letakkan file di sini',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Indonesia(ID)]
 // [i18n-Indonesia(ID)]

+ 2 - 0
packages/semi-ui/locale/source/it.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'it',
     code: 'it',
     dateFnsLocale: it,
     dateFnsLocale: it,
+    currency: 'EUR',
     Pagination: {
     Pagination: {
         pageSize: 'Elementi per pagina: ${pageSize}',
         pageSize: 'Elementi per pagina: ${pageSize}',
         total: 'Pagine totali: ${total}',
         total: 'Pagine totali: ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'Copiato',
         copied: 'Copiato',
         dropAreaText: 'Metti il file qui',
         dropAreaText: 'Metti il file qui',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Italian]
 // [i18n-Italian]

+ 2 - 0
packages/semi-ui/locale/source/ja_JP.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'ja-JP',
     code: 'ja-JP',
     dateFnsLocale: ja,
     dateFnsLocale: ja,
+    currency: 'JPY',
     Pagination: {
     Pagination: {
         pageSize: '1ページあたりのアイテム数:${pageSize}',
         pageSize: '1ページあたりのアイテム数:${pageSize}',
         total: '合計ページ数:${total}',
         total: '合計ページ数:${total}',
@@ -183,6 +184,7 @@ const local: Locale = {
         copied: 'コピーしました',
         copied: 'コピーしました',
         dropAreaText: 'ファイルをここに置いてください',
         dropAreaText: 'ファイルをここに置いてください',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Japan]
 // [i18n-Japan]

+ 2 - 0
packages/semi-ui/locale/source/ko_KR.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'ko-KR',
     code: 'ko-KR',
     dateFnsLocale: ko,
     dateFnsLocale: ko,
+    currency: 'KRW',
     Pagination: {
     Pagination: {
         pageSize: '페이지당 항목: ${pageSize}',
         pageSize: '페이지당 항목: ${pageSize}',
         total: '총 페이지: ${total}',
         total: '총 페이지: ${total}',
@@ -183,6 +184,7 @@ const local: Locale = {
         copied: '복사했습니다',
         copied: '복사했습니다',
         dropAreaText: '파일을 여기에 놓으세요',
         dropAreaText: '파일을 여기에 놓으세요',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Korea]
 // [i18n-Korea]

+ 2 - 0
packages/semi-ui/locale/source/ms_MY.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'ms-MY',
     code: 'ms-MY',
     dateFnsLocale: ms,
     dateFnsLocale: ms,
+    currency: 'MYR',
     Pagination: {
     Pagination: {
         pageSize: 'Item setiap halaman: ${pageSize}',
         pageSize: 'Item setiap halaman: ${pageSize}',
         total: 'Jumlah halaman: ${total}',
         total: 'Jumlah halaman: ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'Disalin',
         copied: 'Disalin',
         dropAreaText: 'Letakkan fail di sini',
         dropAreaText: 'Letakkan fail di sini',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Malaysia(MY)]
 // [i18n-Malaysia(MY)]

+ 3 - 1
packages/semi-ui/locale/source/nl_NL.ts

@@ -9,8 +9,9 @@ import { Locale } from '../interface';
  */
  */
 
 
 const local: Locale = {
 const local: Locale = {
-    code: 'nl_NL',
+    code: 'nl-NL',
     dateFnsLocale: nl, // locale code to dateFns locale
     dateFnsLocale: nl, // locale code to dateFns locale
+    currency: 'EUR',
     Pagination: {
     Pagination: {
         page: 'pagina',
         page: 'pagina',
         pageSize: 'Items per pagina: ${pageSize}',
         pageSize: 'Items per pagina: ${pageSize}',
@@ -189,6 +190,7 @@ const local: Locale = {
         copied: 'Gekopieerd',
         copied: 'Gekopieerd',
         dropAreaText: 'Plaats het bestand hier',
         dropAreaText: 'Plaats het bestand hier',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 export default local;
 export default local;

+ 4 - 2
packages/semi-ui/locale/source/pl_PL.ts

@@ -10,8 +10,9 @@ import { Locale } from '../interface';
  */
  */
 
 
 const local: Locale = {
 const local: Locale = {
-    code: 'pl_PL',
+    code: 'pl-PL',
     dateFnsLocale: pl, // locale code to dateFns locale
     dateFnsLocale: pl, // locale code to dateFns locale
+    currency: 'PLN',
     Pagination: {
     Pagination: {
         pageSize: 'Liczba pozycji na stronie: ${pageSize}',
         pageSize: 'Liczba pozycji na stronie: ${pageSize}',
         total: 'Strony ogółem: ${total}',
         total: 'Strony ogółem: ${total}',
@@ -189,7 +190,8 @@ const local: Locale = {
         copy: 'Kopiuj',
         copy: 'Kopiuj',
         copied: 'Skopiowano',
         copied: 'Skopiowano',
         dropAreaText: 'Umieść plik tutaj',
         dropAreaText: 'Umieść plik tutaj',
-    },
+    },  
+    InputNumber: {}
 };
 };
 
 
 export default local;
 export default local;

+ 2 - 0
packages/semi-ui/locale/source/pt_BR.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'pt-BR',
     code: 'pt-BR',
     dateFnsLocale: ptBR,
     dateFnsLocale: ptBR,
+    currency: 'BRL',
     Pagination: {
     Pagination: {
         pageSize: 'Itens por página: ${pageSize}',
         pageSize: 'Itens por página: ${pageSize}',
         total: 'Total de páginas: ${total}',
         total: 'Total de páginas: ${total}',
@@ -190,6 +191,7 @@ const local: Locale = {
         copied: 'Cópia bem sucedida',
         copied: 'Cópia bem sucedida',
         dropAreaText: 'Coloque o arquivo aqui',
         dropAreaText: 'Coloque o arquivo aqui',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // 葡萄牙语
 // 葡萄牙语

+ 2 - 0
packages/semi-ui/locale/source/ro.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'ro',
     code: 'ro',
     dateFnsLocale: ro,
     dateFnsLocale: ro,
+    currency: 'RON',
     Pagination: {
     Pagination: {
         pageSize: 'Elemente per pagină: ${pageSize}',
         pageSize: 'Elemente per pagină: ${pageSize}',
         total: 'Total pagini: ${total}',
         total: 'Total pagini: ${total}',
@@ -182,6 +183,7 @@ const local: Locale = {
         copied: 'Copiat',
         copied: 'Copiat',
         dropAreaText: 'Puneți fișierul aici',
         dropAreaText: 'Puneți fișierul aici',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Romanian] 罗马尼亚语
 // [i18n-Romanian] 罗马尼亚语

+ 2 - 0
packages/semi-ui/locale/source/ru_RU.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'ru-RU',
     code: 'ru-RU',
     dateFnsLocale: ru,
     dateFnsLocale: ru,
+    currency: 'RUB',
     Pagination: {
     Pagination: {
         pageSize: 'Позиции на странице: ${pageSize}',
         pageSize: 'Позиции на странице: ${pageSize}',
         total: 'Всего страниц: ${total}',
         total: 'Всего страниц: ${total}',
@@ -185,6 +186,7 @@ const local: Locale = {
         copied: 'Скопировано',
         copied: 'Скопировано',
         dropAreaText: 'Положите файл здесь',
         dropAreaText: 'Положите файл здесь',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Russia] 俄罗斯语
 // [i18n-Russia] 俄罗斯语

+ 3 - 1
packages/semi-ui/locale/source/sv_SE.ts

@@ -7,8 +7,9 @@ import { Locale } from '../interface';
  */
  */
 
 
 const local: Locale = {
 const local: Locale = {
-    code: 'sv_SE',
+    code: 'sv-SE',
     dateFnsLocale: sv, 
     dateFnsLocale: sv, 
+    currency: 'SEK',
     Pagination: {
     Pagination: {
         pageSize: 'Artiklar per sida: ${pageSize}',
         pageSize: 'Artiklar per sida: ${pageSize}',
         total: 'Totalt antal sidor: ${total}',
         total: 'Totalt antal sidor: ${total}',
@@ -187,6 +188,7 @@ const local: Locale = {
         copied: 'Kopierad',
         copied: 'Kopierad',
         dropAreaText: 'Placera filen här',   
         dropAreaText: 'Placera filen här',   
     }, 
     }, 
+    InputNumber: {}
 };
 };
 
 
 export default local;
 export default local;

+ 2 - 0
packages/semi-ui/locale/source/th_TH.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'th-TH',
     code: 'th-TH',
     dateFnsLocale: th,
     dateFnsLocale: th,
+    currency: 'THB',
     Pagination: {
     Pagination: {
         pageSize: 'รายการต่อหน้า: ${pageSize}',
         pageSize: 'รายการต่อหน้า: ${pageSize}',
         total: 'หน้าทั้งหมด: ${total}',
         total: 'หน้าทั้งหมด: ${total}',
@@ -186,6 +187,7 @@ const local: Locale = {
         copied: 'คัดลอกสำเร็จ',
         copied: 'คัดลอกสำเร็จ',
         dropAreaText: 'วางไฟล์ที่นี่',
         dropAreaText: 'วางไฟล์ที่นี่',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Thai]
 // [i18n-Thai]

+ 2 - 0
packages/semi-ui/locale/source/tr_TR.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'tr-TR',
     code: 'tr-TR',
     dateFnsLocale: tr,
     dateFnsLocale: tr,
+    currency: 'TRY',
     Pagination: {
     Pagination: {
         page: 'Sayfa',
         page: 'Sayfa',
         pageSize: 'Sayfa başı öğe: ${pageSize}',
         pageSize: 'Sayfa başı öğe: ${pageSize}',
@@ -183,6 +184,7 @@ const local: Locale = {
         copied: 'Kopyalama başarılı',
         copied: 'Kopyalama başarılı',
         dropAreaText: 'Dosyayı buraya yerleştirin',
         dropAreaText: 'Dosyayı buraya yerleştirin',
     },
     },
+    InputNumber: {},
 };
 };
 
 
 // [i18n-Turkish] 
 // [i18n-Turkish] 

+ 2 - 0
packages/semi-ui/locale/source/vi_VN.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'vi-VN',
     code: 'vi-VN',
     dateFnsLocale: vi,
     dateFnsLocale: vi,
+    currency: 'VND',
     Pagination: {
     Pagination: {
         pageSize: 'Số mục mỗi trang: ${pageSize}',
         pageSize: 'Số mục mỗi trang: ${pageSize}',
         total: 'Tổng số trang: ${total}',
         total: 'Tổng số trang: ${total}',
@@ -185,6 +186,7 @@ const local: Locale = {
         copied: 'Đã sao chép',
         copied: 'Đã sao chép',
         dropAreaText: 'Đặt tệp vào đây',
         dropAreaText: 'Đặt tệp vào đây',
     }, 
     }, 
+    InputNumber: {}
 };
 };
 
 
 // [i18n-Vietnam] 越南语
 // [i18n-Vietnam] 越南语

+ 2 - 0
packages/semi-ui/locale/source/zh_CN.ts

@@ -3,6 +3,7 @@ import { Locale } from '../interface';
 
 
 const local: Locale = {
 const local: Locale = {
     code: 'zh-CN',
     code: 'zh-CN',
+    currency: 'CNY',
     dateFnsLocale: zhCN, // locale code to dateFns locale
     dateFnsLocale: zhCN, // locale code to dateFns locale
     Pagination: {
     Pagination: {
         pageSize: '每页条数:${pageSize}',
         pageSize: '每页条数:${pageSize}',
@@ -183,6 +184,7 @@ const local: Locale = {
         copied: '复制成功',
         copied: '复制成功',
         dropAreaText: '将文件放到这里',
         dropAreaText: '将文件放到这里',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // 中文
 // 中文

+ 2 - 0
packages/semi-ui/locale/source/zh_TW.ts

@@ -4,6 +4,7 @@ import { Locale } from '../interface';
 const local: Locale = {
 const local: Locale = {
     code: 'zh-TW',
     code: 'zh-TW',
     dateFnsLocale: zhTW, // locale code to dateFns locale
     dateFnsLocale: zhTW, // locale code to dateFns locale
+    currency: 'TWD',
     Pagination: {
     Pagination: {
         pageSize: '每頁項目數:${pageSize}',
         pageSize: '每頁項目數:${pageSize}',
         total: '總頁數:${total}',
         total: '總頁數:${total}',
@@ -183,6 +184,7 @@ const local: Locale = {
         copied: '複制成功',
         copied: '複制成功',
         dropAreaText: '將文件放到這裡',
         dropAreaText: '將文件放到這裡',
     },
     },
+    InputNumber: {}
 };
 };
 
 
 // 中文
 // 中文