|
许多国内企业正积极开拓国际市场,如Shopee、阿里的Lazada、字节的TikTok、拼多多海外版Temu、以及服装快消领域的Shein等。当国内市场存量业务达到峰值预期时,海外业务成为各公司未来收入增长的主要动力,因此,国际化已成为越来越重要的职业发展方向。国内IT企业收入天花板:「10亿X2元X365天=7300亿元」,也就是10亿人口,企业每天赚取用户2元,保持365天,就是单业务增长的营收天花板(大部分业务赚不到2元,用户量也没到10亿)。比如视频如果60元一个月那会员营收天花板就可以这么预估. 甚至比这个还低, 毕竟用户会流失, 拉新也要成本, 运营成本是在递增的。国际化不仅仅是多语言文案适配这么简单,而是一全套的工程化解决方案。笔者觉得更重要的是「从业人员需要具备全球视野,对多元文化有包容心和敬畏心理,同时知识面要求也较高」。比如,了解SEA、US、UK等常见地区的简写,尊重伊斯兰教的斋月节等习俗。对于服务全球用户的产品来说,对应产品的要求更加复杂,多样性体现在不同的文化习俗差异上,其实即便在庞大的中国内部也存在南北差异。了解的越多越发现这个世界的“多样性”。概念说明苹果键盘怎么卖多国苹果键盘有很多型号不同型号的布局不一样https://www.apple.com/shop/product/MK2A3J/A/magic-keyboard-japaneseapple-keyboard那如何模仿苹果造一把可以卖到世界各地的键盘电路板等硬件配件统一生产制定三种布局方案(Arabic, Russian, Ukrainian归为一种, Chinese (Zhuyin)和Korean为一种, Japanese为一种),单独开孔键帽印刷不同语言的文案MacOS开发语言输入软件适配不同键盘的语言输入其中2,3,4都是为产品的全球化服务全球化=国际化i18n+本地化l10nhttps://en.wikipedia.org/wiki/Internationalization_and_localizationglobalization产品设计和开发部署需要需要考虑国际化i18n多语言多布局,如阿拉伯语的RTL多货币全球多地区多机房部署(离用户越近服务体验越好,数据物理存储隔离符合各个国家数据安全要求)产品本地化L10N是国际化后的「可选」流程,需要引入「本地化团队」转化和质量验收,再投入本地化市场本地化步骤是可选的,如英美产品UI,语言基本一致可互通,本地化投入少中文地区等有简体繁体,不同地区用语习惯不一样,也要特别兼顾,如香港的粤语和广州的粤语,在一些用词有区别,像吸管(广州)-饮筒(香港),你可以看https://www.zhihu.com/question/20663233阿拉伯语,希伯来语等地区RTL的阅读习惯,对产品改动较大需要特殊适配国际化产品面向全球用户,需要做语言适配,针对不同国家地区的用户提供对应语言的版本。本质是「文本替换」,也要考虑文本阅读方向,比如阿拉伯语和希伯来语是从右到左。可以看下Apple的做法,对不同国家地区提供不同服务Apple US 对应链接 https://www.apple.com/Apple CN 对应链接 https://www.apple.com.cn/Apple HK 对应链接 https://www.apple.com/hk/en/常见地区语言对应关系可以看 ISO 3166-1(https://baike.baidu.com/item/ISO%203166-1/5269555fr=ge_ala)IntlMDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl浏览器中的 Intl 是一个内置的 JavaScript 对象,用于国际化(Internationalization)支持。它提供了处理日期、时间、数字格式化以及货币和语言的能力,以便网站能够根据用户的语言和地区习惯来显示内容。在 JavaScript 中,您可以使用 Intl 对象来执行以下操作:格式化日期和时间:使用 Intl.DateTimeFormat 对象可以格式化日期和时间,并根据用户的地区偏好显示日期和时间。格式化数字:使用 Intl.NumberFormat 对象可以格式化数字,并根据用户的地区偏好显示数字。处理货币:使用 Intl.NumberFormat 对象结合指定的货币代码可以格式化货币,并根据用户的地区偏好显示货币。语言和区域设置信息:使用 Intl.getCanonicalLocales 方法可以获取支持的语言和区域设置信息。关于标点符号,通常在国际化的环境下,标点符号使用英文是比较常见的做法,因为英文标点符号在全球范围内都有通用性,可以避免因为地域差异而引起的误解。在 Intl 对象中,一般不会直接涉及到标点符号的处理,而是主要用于处理日期、时间、数字和货币等格式化需求。前置知识语言标识全地球有N个民族,有的民族有自己的语言, 有的民族用其他国家民族传递过来的语言, 融化吸收然后发展自己的文字。按照ISO标准(https://zh.m.wikipedia.org/wiki/ISO_639-1),语言可以用大类+小类表示, 比如「zh」就是汉语,是一个大类,而「zh-CN」就是简体中文的缩写, 新加坡华人众多久了就有「zh-SG」, 表示的是新加坡使用的中文,其次还有「zh-HK/zh-TW和zh-Hant/zh-Hans」等等语言声明是三段式结构 [language]-[script]-[region] , 如zh-Hans-CN表示中国地区简体中文, zh-Hant表示所有中文繁体Language Code Table(http://www.lingoes.net/zh/translator/langcode.htm)一起来看下苹果官网是如何适配多国语言的澳门apple https://www.apple.com/mo/香港apple英文 https://www.apple.com/hk/en/中文 https://www.apple.com/hk/中国大陆地区apple https://www.apple.com.cn/台湾apple https://www.apple.com/tw/新加坡apple https://www.apple.com/sg/日本apple https://www.apple.com/jp/可以看到有的是根域名下通过ISO地区的path比如**/hk/**这样来区分的,有的是直接换域名,比如中国大陆地区文字阅读顺序按照普通的中文和英文顺序,都是LTR,上到下,都是世界范围通用的而ar阿拉伯语, ur乌都语, he希伯来语都是特殊的从右到左, 即RTL的一般会通过标签的dir属性标识, 比如下面的解释HTML dir Attribute(https://www.w3schools.com/tags/att_global_dir.asp)HEBREW是指希伯来语,这是一种在以色列广泛使用的语言,也是犹太教的宗教经典文本的原始语言。它属于阿夫罗亚细亚语系,有着悠久的历史和文化价值。「希伯来语有其独特的书写系统,从右向左书写。」上图的概念很少人普及, 因为非国际化产品不需要多语言, 做需要支持海外业务和全球应用的同学可以多了解下.
传统的英文, 中文简体, 拉丁文等都是上图LATIN的阅读顺序, 如果用上「top, 下bottom, 左left, 右right」代表我们的习惯, 也就是「Z」这样的顺序. 即行到行是从上到下的顺序, 行内阅读顺序是从左到右.文档流和阅读顺序即left→right, top→bottom的顺序,有主次分别,left→right的优先级高于top→bottom而Web标准对其定义是下面这样的left = inline-startright = inline-endtop = block-startbottom = block-end讲个笑话, 古代书籍就是按照 writing-mode: vertical-rl 排版的joke布局content-flows比如 margin: left 或者 text-align: left 在多语言场景都是不合适的,你的左右不是其他人的左右。而应该用 margin-inline-start 和 text-align: start 替代,即inline轴和block轴//下面两两相等,请抛弃left/right/top/bottom等属性//https://web.dev/learn/css/logical-properties/#terminologymargin-left:1pxmargin-inline-start:1pxmargin-right:1pxmargin-inline-end:1pxmargin-top:1pxmargin-block-start:1pxmargin-bottom:1pxmargin-block-end:1pxtext-align:lefttext-align:starttext-align:righttext-align:endmax-width:100pxmax-inline-width:100pxmax-inline-size:150pxmax-height:100pxmax-block-width:100pxpadding-left:1pxpadding-inline-start:1pxpadding-top:1pxpadding-block-start:1pxtop:0.2em;inset-block-start:0.2em;bottom:0.2em;inset-block-end:0.2em;left:2px;inset-inline-start:2px;right:2px;inset-inline-end:2px;border-bottom:1pxsolidred;border-block-end:1pxsolidred;border-bottom-right-radius:1em;border-end-end-radius:1em;height:160px;block-size:160px;width:160px;inline-size:160px;也可以看下面的例子https://codepen.io/web-dot-dev/pen/gOxXOLKhttps://codepen.io/web-dot-dev/pen/mdMqdOx如上两个例子通过margin-inline-start等属性,再在html元素上添加 dir: rtl 就可以实现多语言的阅读顺序兼容由此, 常见的布局也会更新为以下形式,常见的物理盒模型用于尺寸计算, 逻辑盒模型用于国际化处理盒子模型writing mode 决定 content-flows上面写了文档有inline and block flow,对应english的left和right,top和bottom。而 writing-mode 可以修改content-flows,比如下面的值/*关键值*/writing-mode:horizontal-tb;writing-mode:vertical-rl;writing-mode:vertical-lr;可以这么理解 writing-mode: horizontal-tb ,前面的horizontal/vertical是指的inline轴的方向,https://codepen.io/manfredhu/pen/xxWdpaKvi和vb视口宽高viewport在这里也有特殊含义. 比如宽高vw和vh也被取代,用 vi(viewport inline) 和 vb(viewport block)替代1%宽度=1vw=1vi1%高度=1vh=1vbJS的scrollLeftDOM的API可以通过「Element.scrollLeft」获取到元素滚动的距离,下图是一个实际例子scrollLeft的rtl这里在最后做了一个遮罩(绿色边框区域),内部蓝色部分类似一个走马灯,通过overflow:hidden将蓝色高亮部分超出的区域遮住当蓝色部分滚动到最后,绿色遮罩隐藏,达到一个遮盖,滚动到最后消失的效果,代码如下const ref = document.querySelector('.tiktok-table__container') // 父节点,蓝色区域const ref2 = document.querySelector('.tiktok-table__container > table') // 子节点,表格区域const bufferWidth = 30 // 留一点buffer空间if (ref & ref2 & ref.clientWidth + ref.scrollLeft >= ref2.clientWidth - bufferWidth) { // 滚动到最后隐藏绿色遮罩 setTableRightMask(false)} else { setTableRightMask(true)}但是在RTL下,神奇的事情就发生了,scrollLeft居然是负数这是因为RTL的实现是通过HTML标签增加属性 dir="rtl” 实现的,会将文档完全翻转过来,所以scrollLeft就会是负数。因为此时(0, 0)这个原点已经是表格右边了解决方法也很简单,取绝对值呗,这样就忽略了方向的影响locale根据ISO标准对全球国家地区进行划分https://en.wikipedia.org/wiki/ISO_3166-2. 如 "US" 表示美国,"CN" 表示中国. 还有常见的如「zh-CN, en-US, en-GB等」CN是国家地区码, 根据国际标准 ISO 3166-1 规定的国家和地区代码。ISO 3166-1 是用于标识国家和地区的国际标准,每个国家或地区都有一个唯一的两字母代码。"CN" 代表中华人民共和国(People's Republic of China),即中国zh-CN是语言地区码, 它通常用于表示中文("zh" 代表中文)以及特定的地区或国家,这里 "CN" 代表中国。"zh" 代表中文,这是根据国际标准 ISO 639-1 规定的语言代码。ISO 639-1 是用于标识语言的国际标准,每个语言都有一个唯一的两字母代码。"zh" 代表中文,但不区分中文的不同方言,如普通话和粤语。zh-Hans-CN 表示中国大陆地区的简体中文,还有"zh-Hans-SG" 可用于表示新加坡的官方简化中文,"zh-Hans-TW" 可用于表示台湾的官方简化中文Intl. Locale举个说下Intl API对于locale的定义constkorean=newIntl.Locale('ko',{script:'Kore',region:'KR',hourCycle:'h23',calendar:'gregory',});constjapanese=newIntl.Locale('ja-Jpan-JP-u-ca-japanese-hc-h12');console.log(korean.baseName,japanese.baseName);//Expectedoutput:"ko-Kore-KR""ja-Jpan-JP"可以看到Intl. Locale就是把传入的字符串拆解为 [language]-[script]-[region] 的组成.ja:代表语言代码,表示日语(Japanese)Jpan:代表脚本代码,表示使用日语文字(Japanese script)JP:代表地区代码,表示日本(Japan)
这里的 u-ca-japanese 表示unicode calendar也就是日历格式(日本日历与众不同), hc表示hourCycle这里hc-h12表示12小时制. u-ca-japanese 和 hc-h12 的顺序无关, 也就是说如下两种用法完全等价constjapanese=newIntl.Locale('ja-Jpan-JP-u-ca-japanese-hc-h12');constjapanese2=newIntl.Locale('ja-Jpan-JP-u-hc-h12-ca-japanese');-u (unicode)可以理解为额外扩展插件, 插件系统支持以下扩展. 如上使用calendar扩展和hourCycle扩展calendarca(extension)caseFirstkf(extension)collationco(extension)hourCyclehc(extension)numberingSystemnu(extension)numerickn(extension)calendar:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar)caseFirst:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/caseFirst)collation:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/collation)hourCycle:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/hourCycle)numberingSystem:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/numberingSystem)numeric:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/numeric)语言声明
Ifeltsomeschadenfreude.DeutscheVersionlanguage设置语言属性通常代表语言标识符的主要部分。它标识了用于表达语言的基本信息。例如,在 BCP 47 标准中,语言标识符通常包含了语言的主要代码部分,例如 "en" 代表英语,"es" 代表西班牙语。constlocales=['en','de','ja'];constdisplayNames=newIntl.DisplayNames('en-US',{type:'language'});locales.forEach(locale=>console.log(displayNames.of(locale)));//English//German//Japanese如果已经有如上代码, 再进一步给这些内容添加样式是非常简单的, 我们可以使用CSS的选择器. 如 [lang|="fr"] 或者 :lang(fr)[lang|="fr"] 选择属性 lang=fr 或者lang属性以fr开头的元素, 如 lang=fr-CAscript设置脚本属性是语言标识符的可选部分,表示使用的书写系统或文字的风格。这是一个辅助信息,用于更精确地表示特定语言的书写习惯。例如,"Hans" 代表简体中文,"Latn" 代表拉丁文。script的of支持传入BCP47规范的二字码(https://en.wikipedia.org/wiki/IETF_language_tag), 如zhconstscriptNames=newIntl.DisplayNames('en-US',{type:'script'});console.log(scriptNames.of('Hans'));// output:Simplifiedconsole.log(scriptNames.of('Hant'));// output:Traditionalconsole.log(scriptNames.of('Latn'));// output:LatinconstscriptNames=newIntl.DisplayNames('zh-CN',{type:'script'});console.log(scriptNames.of('Hans'));// output:简体console.log(scriptNames.of('Hant'));// output:繁体console.log(scriptNames.of('Latn'));// output:拉丁文region设置region 的of支持传入https://en.wikipedia.org/wiki/ISO_3166-2 里的国家二字码constregionNamesInEnglish=newIntl.DisplayNames(['en'],{type:'region'});constregionNamesInTraditionalChinese=newIntl.DisplayNames(['zh-Hant'],{type:'region'});console.log(regionNamesInEnglish.of('US'));//Expectedoutput:"UnitedStates"console.log(regionNamesInTraditionalChinese.of('US'));//Expectedoutput:"美國"文本阅读顺序声明文本的阅读顺序声明html标签dir属性可以通过html标签的dir属性设置css属性writing-mode(https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode)writing-mode:horizontal-tb;writing-mode:vertical-lr;writing-mode:vertical-rl;文本行的布局方向, 以及块的排列方向。如果想作用在整个文档需要设置html标签, 则全局生效「第一个属性horizontal/vertical指的是行块的排列, 第二个属性则是指文本内容的流向(content flows)」翻转形如 「」 这种符号类,在多语言下是不一样的比如ar阿拉伯语和ur乌尔都斯语问号是RTL的,即 「」(https://zh.m.wiktionary.org/wiki/%D8%9F)是OK的而he希伯来语是LTR的,即 「」 是OK的是不是很神奇?一个问号也能玩出花来线索管理页-英文和阿拉伯语常见的需要RTL的语言有下面这些「阿拉伯语(AR)」:阿拉伯语是使用 RTL 方向书写的最著名的语言之一。它是中东和北非地区的主要语言,以及伊斯兰教的官方语言。「希伯来语(HE)」:希伯来语是犹太人的宗教和文化语言,以及以色列的官方语言。它也是一个使用 RTL 方向书写的语言。「波斯语(FA)」:波斯语,也称为法尔西语,是伊朗的官方语言,以及一些中东国家的官方或辅助语言。它使用 RTL 方向书写。「乌尔都语(UR)」:乌尔都语是巴基斯坦和印度的官方语言之一,以及一种使用 RTL 方向书写的语言。「帕斯图语(PS)」:帕斯图语是阿富汗的官方语言之一,也是使用 RTL 方向书写的语言。constrtlLangs=['ar',//阿拉伯语'ur',//巴基斯坦'he',//以色列'he-IL',//希伯来语(以色列)'fa-IR',//波斯语(伊朗)'ps'//帕斯图语];多语言文案l10n本地化的一个比较多工作量的部分是文本的翻译, 一种文本到N种文本的翻译需要引入本地化团队. 技术实现上选择也很多程序打包嵌入文案通过 key: text 映射, 比如 t('key') 最后程序跑出来就是text文案, 这种方式不会依赖其他东西, 跟普通网页一样内容都是CDN文件. 缺点是文案做为静态资源需要用户额外获取, 如果处理不好替换错误就展示 key 内容而不是vue i18n:https://kazupon.github.io/vue-i18n/zh/started.html#htmlreact i18n:https://react.i18next.com以下例子以Vue为例, 配置如「en.json, fr.json」等等的静态配置文案, 打包嵌入CDN的JS文件里Vue i18n example:https://codesandbox.io/p/sandbox/o7n6pkpwoyfile=%2Fstore.js%3A10%2C14接口获取程序运行时通过接口拿文案,可以通过html标签添加query参数 lang=xxx 标记页面语言, 或者cookie标记语言选择实时翻译替换加载翻译的脚本, 在切换语言的时候替换掉加载的文本。好处是加载的脚本是当前语言所需要的, 不会有其他语言的冗余. 缺点是依赖一个翻译服务, 如果翻译服务宕机了网页就不能正常访问了User -> gateway -> SSR -> i18n cache -> read-time translation services(实时翻译服务)占位符与单复数处理-ICU语法DevPal - ICU message editor:https://devpal.co/icu-message-editor/data=I%20have%20%7Bnum%2C%20plural%2C%20one%7B%7Bnumber%7D%20%23%20apple%7D%20other%7B%23%20apples%7D%7D%2Cbut%20it%27s%20too%20small%0AICU语法即通用的有if-else逻辑的DSL,如下DSL可以根据传入的值换取不同的表示,常用于国际化业务I have {num, plural, one{{number} # apple} other{# apples}},but it's too smallIntl. Segmenter 分段器如果你用过vim一定知道w(word)可以移动到下个单词, 英文里把文本分为单词、句子和段落,同理中文也是「w」 word 一个单词「s」 sentence 一个句子「p」 paragraph 一个段落constsegmenter=newIntl.Segmenter('en-US',{granularity:'word'});consttext='Thisisasampletextfordemonstrationpurposes.';//使用Segmenter对文本进行分割constsegments=[...segmenter.segment(text)];console.log(segments);//0:{segment:'This',index:0,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//1:{segment:'',index:4,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//2:{segment:'is',index:5,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//3:{segment:'',index:7,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//4:{segment:'a',index:8,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//5:{segment:'',index:9,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//6:{segment:'sample',index:10,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//7:{segment:'',index:16,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//8:{segment:'text',index:17,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//9:{segment:'',index:21,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//10:{segment:'for',index:22,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//11:{segment:'',index:25,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//12:{segment:'demonstration',index:26,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//13:{segment:'',index:39,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}//14:{segment:'purposes',index:40,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:true}//15:{segment:'.',index:48,input:'Thisisasampletextfordemonstrationpurposes.',isWordLike:false}Intl. Segmenter分段器可以把句子, 段落, 文章等按照配置切割为不同的segment数组, 结构类似正则, 有segment属性再举个例子, 中文语境下「真的」其实是一个词//创建分段器,指定语言环境和分段类型为'word'constsegmenter=newIntl.Segmenter(['en','zh'],{granularity:'word'});//要分割的字符串consttext='Hello世界Helloworld';//使用分段器分割字符串constsegments=segmenter.segment(text);//遍历并打印每个分段的结果for(constsegmentofsegments){console.log(`Segment{segment.segment},Index{segment.index},IsWordLike{segment.isWordLike}`);}//Segment:Hello,Index:0,IsWordLike:true//Segment:世界,Index:5,IsWordLike:true//Segment:Hello,Index:7,IsWordLike:true//Segment:,Index:12,IsWordLike:false//Segment:world,Index:13,IsWordLike:trueconststr="我真的很强,强哥的强";constsegmenterJa=newIntl.Segmenter("zh-CN",{granularity:"word"});constsegments=segmenterJa.segment(str);console.log(Array.from(segments));//0:{segment:'我',index:0,input:'我真的很强,强哥的强',isWordLike:true}//1:{segment:'真的',index:1,input:'我真的很强,强哥的强',isWordLike:true}//2:{segment:'很',index:3,input:'我真的很强,强哥的强',isWordLike:true}//3:{segment:'强',index:4,input:'我真的很强,强哥的强',isWordLike:true}//4:{segment:',',index:5,input:'我真的很强,强哥的强',isWordLike:false}//5:{segment:'',index:6,input:'我真的很强,强哥的强',isWordLike:false}//6:{segment:'强',index:7,input:'我真的很强,强哥的强',isWordLike:true}//7:{segment:'哥',index:8,input:'我真的很强,强哥的强',isWordLike:true}//8:{segment:'的',index:9,input:'我真的很强,强哥的强',isWordLike:true}//9:{segment:'强',index:10,input:'我真的很强,强哥的强',isWordLike:true}时间&时区国际化会有时区划分问题, 时区产生于太阳下地球自转导致的昼夜交替. 而全球不同国家地区当地时间与UTC时间是不一致的. 全球大部分人都可以说自己早上起床, 晚上睡觉. 上下文是通的. 但是这个早上的时间根据UTC来定义是不一样的GMT和UTCGTM = Greenwich Mean Time,GTM是英国格林威治时间,但是与太阳时偏差较大,已成为历史不再作为标准UTC = 「协调世界时(UTC: Coordinated Universal Time)- 由原子钟提供」时间的往事--记一次与夏令时的斗智斗勇:https://jiangyixiong.top/2021/05/25/%E6%97%B6%E9%97%B4%E7%9A%84%E5%BE%80%E4%BA%8B%E2%80%94%E2%80%94%E8%AE%B0%E4%B8%80%E6%AC%A1%E4%B8%8E%E5%A4%8F%E4%BB%A4%E6%97%B6%E7%9A%84%E6%96%97%E6%99%BA%E6%96%97%E5%8B%87GMT 标准时间 全球时区查询:https://time.artjoey.com/cn通过NTP协议(https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E6%99%82%E9%96%93%E5%8D%94%E5%AE%9A), 让计算机在全球网络里保持时间一致「Offset与Timezone」Offset即偏移量,比如中国在东八区,Offset是+08:00:00而东八区不止包括中国时间,而是一组东西经符合一个区域的集合,比如东八区={CST(中国标准时),SGT(新加坡时间),AWST(澳洲西部标准时)...}如何获取当前用户的时区信息//所在地区的时区标识符,如America/New_YorkconsttimeZone=newIntl.DateTimeFormat().resolvedOptions().timeZone;console.log("用户时区偏移:"+ timeZone);//用户时区偏移:Asia/Shanghai//获取本地时间与UTC时间偏移值,最小单位是分钟.如"-480",表示-8小时.其中正负表示UTC前后,如美国东部时间是UTC-5,中国北京时间是UTC+8constdate=newDate();consttimeZoneOffset=date.getTimezoneOffset();console.log("时区偏移:"+ timeZoneOffset);//时区偏移:-480Intl是新的浏览器API, 与Math类似是全局静态对象, 专门用于处理国际化和本地化业务. 其下的DateTimeFormat可以处理时间相关国际化问题DSTDST (Daylight saving time),日光节约时,夏令时/冬令时等等名称。「它会在每年春天的某一天将时钟向后拨一小时,又在秋天的某一天将时钟向前拨动一个小时。」非国际化业务很少遇到这个情况,主要因为「中国不实行夏令时/冬令时。」为什么要实行夏令时?一战时德国率先实行,将每年夏天增加1h,冬天较少1h会产生什么现象?因为是行政约定,每年都可以自由选择某天某时进入夏令时,各国自由发布。IANA会存储(https://www.iana.org/time-zones)同步各国DST,计算机每小时同步时间后会在某一秒发生「突变」,比如1:59到2点的时候突变会1:00计算机如何表示时间?计算机都有一个unixTime,它表示当前时间距离世界标准时的1970年1月1日0点0分0秒的毫秒数,是一个绝对值,也就是UTC时间但是不同地区设备会根据本地有一个格式化,将UTC时间转化为本地时间,比如中国在东八区2021-03-1401:59:59GMT-08:00(太平洋标准时间,PST)2021-03-14T01:59:59.000-08:00(ISO格式表示)2021-03-14T09:59:59.000Z(转换为UTC时间并以ISO格式表示)//下一秒时间突变2021-03-1403:00:00GMT-07:00(太平洋夏令时间,PDT)2021-03-14T03:00:00.000-07:00(ISO格式表示)2021-03-14T10:00:00.000Z(转换为UTC时间并以ISO格式表示)//原始时间字符串consttimeString="2021-03-14T09:59:59.000Z";//将时间字符串转换为Date对象constdate=newDate(timeString);constpstOutput=date.toLocaleString("en-US",{timeZone:"America/Los_Angeles",hour12:false});console.log(pstOutput);//3/14/2021,01:59:59//获取时间戳consttimestamp=date.getTime();//增加1秒constnewTimestamp=timestamp+1000;//创建新的Date对象并格式化为PDT时间constnewDate=newDate(newTimestamp);constpdtOutput=newDate.toLocaleString("en-US",{timeZone:"America/Los_Angeles",hour12:false});console.log(pdtOutput);//3/14/2021,03:00:00时间处理Dayjs插件dayjs: https://day.js.org/docs/zh-CN/i18n/i18n国际化支持 https://github.com/iamkun/dayjs/tree/dev/src/locale原理:通过拉取多语言文案输出不同的formated日期时间字符串可以看这个demoDays of the week:https://codesandbox.io/s/dayjs-dynamic-locale0import-forked-wnk2zqfile=/src/index.js因我本地系统设置了每周第一天为星期日Intl APIconstdate=newDate();constformattedDate=newIntl.DateTimeFormat('en-US').format(date);console.log(formattedDate);//10/29/2023constformattedDate=newIntl.DateTimeFormat('zh-CN').format(date);console.log(formattedDate);//2023/10/29本地时间输出//创建DateTimeFormat对象,并指定语言和地区constdateFormatterCN=newIntl.DateTimeFormat('zh-CN',{year:'numeric',month:'long',//使用完整的月份名称day:'numeric',});console.log(dateFormatterCN.format(newDate('2024-04-28')));//2024年4月28日constdateFormatterUS=newIntl.DateTimeFormat('en-US',{year:'numeric',month:'long',//使用完整的月份名称day:'numeric',});console.log(dateFormatterUS.format(newDate('2024-04-28')));//April28,2024「Intl. RelativeTimeFormat」 相对时间「Intl.RelativeTimeFormat」 是 JavaScript 中的国际化 API,用于格式化相对时间,例如“1 小时前”或“2 天后”。这个 API 可以根据不同的语言和地区设置,以自然语言的方式呈现相对时间,使应用程序能够更好地适应多语言环境。constrtf1=newIntl.RelativeTimeFormat('zh',{style:'short'});console.log(rtf1.format(3,'quarter'));//Expectedoutput:"3个季度后"console.log(rtf1.format(-1,'day'));//Expectedoutput:"1天前"constrtf2=newIntl.RelativeTimeFormat('jp',{numeric:'auto'});console.log(rtf2.format(2,'day'));//Expectedoutput:"后天"我们知道中文语境是一万以上可以缩写为1万, 或者是 1 0000. 也就是4位数字. 比如 1 2345 6789或者1’2345’6789(’是万位分隔符)可以一眼看出来是一亿两千三百四十五万六千七百八十九. 而如果是123, 456, 789可能很多人会愣很久重新数才知道是多少. 但是现在很多银行APP都在推跟欧美一样的属于后者的千位分隔符. 可以看这篇讨论觉得写的在理设计产品时,你是如何掉入从众的陷阱中的?– 人人都是产品经理:https://www.woshipm.com/pd/1500589.html)类似以上例子可以再看下面的举例, 可以发现在德语和法语下, 千分位分隔符分别是.和 (空格)constnumber=1234567.89;constformattedNumber=newIntl.NumberFormat('zh-CN').format(number);console.log(formattedNumber);//1,234,567.89constnumber=1234567.89;constformattedNumber=newIntl.NumberFormat('en-US').format(number);console.log(formattedNumber);//1,234,567.89constnumber=1234567.89;constformattedNumber=newIntl.NumberFormat('de-DE').format(number);console.log(formattedNumber);//1.234.567,89constnumber=1234567.89;constformattedNumber=newIntl.NumberFormat('fr-FR').format(number);console.log(formattedNumber);//1234567,89单复数英文复数是要加s的, 比如applesconstnumbers=[1,2,5,10,100];for(constnumberofnumbers){constpluralRules=newIntl.PluralRules('en-US');//使用英语环境constpluralForm=pluralRules.select(number);console.log(`InEnglish,${number}item${pluralForm!=='one''s':''}.`);}//InEnglish,1item.//InEnglish,2items.//InEnglish,5items.//InEnglish,10items.//InEnglish,100items.再比如顺序, 第一第二第三, 英文分别为 first, second, third, fourth, fifth. 聪明的你一定发现规律了. 除了123后面就是数字+th. 简写是1st 2nd. 根据下表可以发现规律1 → st, 后面除了11外. 21-91都是21st, 91st这种2→ nd, 后面除了12外. 22-92都是22nd, 92nd这种3 → rd, 后面除了13外. 23-93都是23rd, 93rd这种其他都是补th数字英文第N1One1st2Two2nd3Three3rd4Four4th10Ten10th11Eleven11th12Twelve12th13Thirteen13th20Twenty20th21Twenty-one21st30Thirty22nd31Thirty-one21st100One hundred100thconstenOrdinalRules=newIntl.PluralRules("en-US",{type:"ordinal"});constsuffixes=newMap([["one","st"],["two","nd"],["few","rd"],["other","th"],]);constformatOrdinals=(n)=>{construle=enOrdinalRules.select(n);constsuffix=suffixes.get(rule);return`${n}${suffix}`;};formatOrdinals(0);//'0th'formatOrdinals(1);//'1st'formatOrdinals(2);//'2nd'formatOrdinals(3);//'3rd'formatOrdinals(4);//'4th'formatOrdinals(11);//'11th'formatOrdinals(21);//'21st'formatOrdinals(42);//'42nd'formatOrdinals(103);//'103rd'数字格式化整数分隔和小数分隔常见的整数分隔符号有千分位分隔, 比如 1000,000 也有万位分隔比如 1000 0000 . 不同语言不一样常见的小数分隔符号 . , 比如 1000.00 . 不同语言不一样constnumber=1234567.89;//格式化为默认数字格式constformattedNumber=newIntl.NumberFormat().format(number);console.log(formattedNumber);//输出:1,234,567.89//格式化为指定语言环境的数字格式constformattedNumberDE=newIntl.NumberFormat('de-DE').format(number);console.log(formattedNumberDE);//输出:1.234.567,89//格式化为指定语言环境的数字格式constformattedNumberFR=newIntl.NumberFormat('fr-FR').format(number);console.log(formattedNumberFR);//输出:1234567,89constformattedNumberCN=newIntl.NumberFormat('zh-CN').format(number);console.log(formattedNumberCN)//输出:1,234,567.89也可以通过参数配置控制小数部分最多/最少有多少位constnumber=1234567.89123;constformattedNumber=newIntl.NumberFormat('en-US',{style:'decimal',//可选'decimal'表示常规数字格式maximumFractionDigits:3,//小数部分最多显示三位}).format(number);console.log(formattedNumber);//输出:1,234,567.891百分比正常百分比是0-100数字+%, 但是法语环境百分比符号习惯是 '% '而不是'%', 多了一个空格constpercentage=0.75;//使用默认语言环境constformattedPercentageDefault=newIntl.NumberFormat('fr-FR',{style:'percent'}).format(percentage);console.log(formattedPercentageDefault);//输出:'75%'//使用指定语言环境constformattedPercentageFR=newIntl.NumberFormat('fr-FR',{style:'percent',minimumFractionDigits:2,maximumFractionDigits:2,}).format(percentage);console.log(formattedPercentageFR);//输出:'75,00%'//使用默认语言环境constformattedPercentageUS=newIntl.NumberFormat('en-US',{style:'percent'}).format(percentage);console.log(formattedPercentageUS);//输出:'75%'//使用指定语言环境constformattedPercentageCN=newIntl.NumberFormat('zh-CN',{style:'percent',minimumFractionDigits:2,maximumFractionDigits:2,}).format(percentage);console.log(formattedPercentageCN);//输出:'75.00%'缩写console.log(newIntl.NumberFormat('en-US',{notation:"compact",compactDisplay:"short",maximumFractionDigits:2}).format(987654321))//987.65Mconsole.log(newIntl.NumberFormat('zh-CN',{notation:"compact",compactDisplay:"short",maximumFractionDigits:2}).format(987654321))//9.88亿货币货币符号比如人民币是 ¥ , 美元是 $ , 欧元 € , 英镑 £newIntl.NumberFormat('en-US',{style:'currency',currency:'USD'}).formatToParts().filter(i=>i.type==='currency')[0].value//'
newIntl.NumberFormat('zh-CN',{style:'currency',currency:'CNY'}).formatToParts().filter(i=>i.type==='currency')[0].value//'¥'newIntl.NumberFormat('de-DE',{style:'currency',currency:'EUR'}).formatToParts().filter(i=>i.type==='currency')[0].value//'€'货币格式化用常见的几个经济体和身边用的多的case举例说明, 注意看输出//美元$是美元符号constnumberUSD=123456789.12;constformattedNumberUSD=newIntl.NumberFormat('en-US',{style:'currency',currency:'USD'}).format(numberUSD);console.log(formattedNumberUSD);//$123,456,789.12//人民币¥是人民币符号constnumberCNY=123456789.12;constformattedNumberCNY=newIntl.NumberFormat('zh-CN',{style:'currency',currency:'CNY'}).format(numberCNY);console.log(formattedNumberCNY);//¥123,456,789.12//欧元€是欧元符号constnumberEUR=123456789.12;constformattedNumberEUR=newIntl.NumberFormat('de-DE',{style:'currency',currency:'EUR'}).format(numberEUR);console.log(formattedNumberEUR);//123.456.789,12€//日元constnumberJPY=123456789.12;constformattedNumberJPY=newIntl.NumberFormat('ja-JP',{style:'currency',currency:'JPY'}).format(numberJPY);console.log(formattedNumberJPY);//¥123,456,789//英镑£是英镑符号constnumberGBP=123456789.12;constformattedNumberGBP=newIntl.NumberFormat('en-GB',{style:'currency',currency:'GBP'}).format(numberGBP);console.log(formattedNumberGBP);//£123,456,789.12//港币constnumberHKD=123456789.12;constformattedNumberHKD=newIntl.NumberFormat('zh-HK',{style:'currency',currency:'HKD'}).format(numberHKD);console.log(formattedNumberHKD);//HK$123,456,789.12//韩元constnumberKRW=123456789.12;constformattedNumberKRW=newIntl.NumberFormat('ko-KR',{style:'currency',currency:'KRW'}).format(numberKRW);console.log(formattedNumberKRW);//123,456,789.12货币的兼容性兜底可以用 Number.prototype.toLocaleString 实现, 也可以用formatjs提供的polyfill//美元$是美元符号constnumberUSD=123456789.12;constformattedNumberUSD=newIntl.NumberFormat('en-US',{style:'currency',currency:'USD'}).format(numberUSD);constformatttdNumberUSDByLocaleString=Number(numberUSD).toLocaleString('en-US',{style:'currency',currency:'USD'});console.log(formattedNumberUSD);//$123,456,789.12console.log(numberUSD.toLocaleString())//123,456,789.12console.log(formatttdNumberUSDByLocaleString)//$123,456,789.12货币单位显示比如美国是美元, 中国有人民币. 可以直接格式化出来currencyNames=newIntl.DisplayNames(["zh-Hans"],{type:"currency"});console.log(currencyNames.of("USD"));//"美元"console.log(currencyNames.of("EUR"));//"欧元"console.log(currencyNames.of("TWD"));//"新台币"console.log(currencyNames.of("CNY"));//"人民币"currencyNames=newIntl.DisplayNames(["zh-Hant"],{type:"currency"});console.log(currencyNames.of("USD"));//"美元"console.log(currencyNames.of("EUR"));//"歐元"console.log(currencyNames.of("TWD"));//"新台幣"console.log(currencyNames.of("CNY"));//"人民幣"排序&列表Intl. Collator常见的电话本, 地址簿排序. 不同语言因为字母转换后排序不一致. 「Intl.Collator」 是 JavaScript 的国际化 API 之一,用于字符串比较和排序,以便在多语言环境中执行正确的排序操作。它允许你创建一个 「Collator」 对象,用于根据特定语言和区域设置执行字符串比较和排序,考虑到不同语言的差异。console.log(['Z','a','z',''].sort(newIntl.Collator('de').compare));//Expectedoutput:Array["a","","z","Z"]console.log(['Z','a','z',''].sort(newIntl.Collator('sv').compare));//Expectedoutput:Array["a","z","Z",""]console.log(['Z','a','z',''].sort(newIntl.Collator('de',{caseFirst:'upper'}).compare));//Expectedoutput:Array["a","","Z","z"]//创建一个Intl.Collator对象constcollator=newIntl.Collator('en-US',{sensitivity:'base',usage:'sort'});//可以看出以下输出是按照拼音排序,guangjinmeimingtianyangconsole.log(['今','天','阳','光','明','媚'].sort(newIntl.Collator('zh').compare));//['光','今','媚','明','天','阳']可以发现 options可以传递参数usage和sensitivity, 有如下取值「排序方式 (usage)」: 「usage」 选项指定排序的目的'sort':用于排序。'search':用于搜索操作,通常不区分大小写。「敏感性 (sensitivity)」: 「sensitivity」 选项指定字符串比较的敏感性级别'base':基本敏感性,不区分重音符号。'accent':考虑重音符号,但不区分大小写。'case':区分大小写,同时考虑重音符号。'case':区分大小写,但不考虑重音符号。大小写 (caseFirst)"upper":表示大写字母(uppercase)在排序中优先考虑。这意味着排序会先考虑所有大写字母,然后再考虑小写字母。在 「caseFirst: "upper"」 情况下,大写字母会排在小写字母之前。"lower":表示小写字母(lowercase)在排序中优先考虑。这意味着排序会先考虑所有小写字母,然后再考虑大写字母。在 「caseFirst: "lower"」 情况下,小写字母会排在大写字母之前。"false":表示不指定大写字母和小写字母的排序顺序,它们会一起排序,不区分大小写。ignorePunctuation: boolean, 表示是否忽略标点符号有如下的应用方式字符串比较constcollator=newIntl.Collator('en-US',{sensitivity:'base',usage:'sort'});//创建一个Intl.Collator对象constresult=collator.compare('apple','Banana');console.log(result);//根据配置输出-1(apple在Banana前面)数组排序我们知道英文字母默认按照ASCII排序, 而如果需要AaBb这样排序只能自己写排序回调//创建一个自定义Collator对象constcustomCollator=newIntl.Collator('en-US',{sensitivity:'base',usage:'sort',ignorePunctuation:true,caseFirst:'false',});//自定义比较函数,忽略空格并不区分大小写functioncustomCompare(a,b){//移除字符串中的空格并转为小写后再比较conststringA=a.replace(/\\s/g,'').toLowerCase();conststringB=b.replace(/\\s/g,'').toLowerCase();if(stringAstringB){return1;}return0;}constdata=['Apple','banana','cherry','applepie','BananaSplit','cherrytart'];constdata2=data.slice()//老方式:使用sort回调排序console.log(data.sort(customCompare));//输出排序结果:['Apple', 'apple pie', 'banana', 'Banana Split', 'cherry', 'cherry tart']//新方式:使用自定义Collator对象进行排序console.log(data2.sort(customCollator.compare));//输出排序结果:['Apple', 'apple pie', 'banana', 'Banana Split', 'cherry', 'cherry tart']可以发现两种方式结果一样, 但是明显Intl. Collator更加优雅, 是配置化的.Intl.「ListFormat」「Intl.ListFormat」 是 JavaScript 的国际化 API 之一,它用于格式化列表,以便在多语言环境中创建自然语言的列表表示。Intl.ListFormat允许你指定列表项的连接方式(如逗号、"和" 等),以及列表项的样式和语言设置。constlistFormatter=newIntl.ListFormat('en-US',{style:'long',type:'disjunction'});constitems=['apples','bananas','cherries'];constformattedList=listFormatter.format(items);console.log(formattedList);//根据配置输出例如:"apples, bananas, or cherries"constlistFormatter=newIntl.ListFormat('en-US',{style:'short',type:'conjunction'});constitems=['apples','bananas','cherries'];constformattedList=listFormatter.format(items);console.log(formattedList);//根据配置输出例如:"apples, bananas, & cherries"constlistFormatter=newIntl.ListFormat('en-US',{style:'narrow',type:'conjunction'});constitems=['apples','bananas','cherries'];constformattedList=listFormatter.format(items);console.log(formattedList);//根据配置输出例如:"apples, bananas, cherries"可以发现 options可以传递参数style和type, 有如下取值「样式 (style)」: 「style」 选项指定列表的样式,有三个可能的值'long':使用完整的语言表达,例如 "A, B, and C"。'short':使用缩略形式,例如 "A, B, & C"。'narrow':使用极简的形式,例如 "A B C"。「连接方式 (type)」: 「type」 选项指定连接列表项的方式,有两个可能的值'conjunction':使用 "和"(默认值),例如 "A、B和C"。'disjunction':使用 "或",例如 "A、B或C"。日历日历是一种常见的东西, 在中国我们经常接触到公历和农历,公历全称格里高利历, 英文gregory。现在国家节日很多都是跟随农历的,比如春节,中秋节等。以前家家人手一本农历, 上面会今日宜做什么, 现在很少见但是老人家还是信这个。而与此相同, 每个地方都有自己的历法「伊斯兰历(Hijri Calendar)」:也称为伊斯兰农历,是伊斯兰教的官方日历,基于月亮的循环。伊斯兰历的年份比公历年份短,每年有12个月,因此季节日期会变化。「希伯来历(Hebrew Calendar)」:希伯来历是犹太教的官方日历,基于太阳和月亮的周期。它有13个月,其中一些月份可以有不同的天数,以保持与农历季节的一致性。「农历(Lunar Calendar)」:农历基于月亮的循环,不同地区和文化有不同的农历系统,如中国农历、韩国农历、越南农历等。「波斯历(Persian Calendar)」:波斯历,也称波斯太阳历,是伊朗和一些邻近国家使用的太阳历,与公历有一些差异。「印度历法(Indian Calendar)」:印度有多种历法,包括维基历(Vikram Samvat)、国民历法(Saka Samvat)、泰米尔历法(Tamil Calendar)等。「巴哈伊历(Bahá'í Calendar)」:巴哈伊信仰使用的独特日历,包括19个月,每个月19天。「民族历法」:一些文化和民族拥有自己的独特历法,用于纪念特定历史事件和节日。constdate=newDate();//当前日期,MonOct30202320:00:50GMT+0800(中国标准时间)constformattedDate=newIntl.DateTimeFormat('ar-SA-u-ca-islamic',{year:'numeric',month:'long',day:'numeric'}).format(date);console.log(formattedDate);//constdate=newDate(1994,1,26);constformattedDate=newIntl.DateTimeFormat('zh-CN-u-ca-chinese',{year:'numeric',month:'long',day:'numeric'}).format(date);console.log(formattedDate);//1994甲戌年正月17//不同语言下不同日历的名称constcalendarNames=newIntl.DisplayNames('en-US',{type:'calendar'});console.log(calendarNames.of('gregory'));//输出:Gregorianconsole.log(calendarNames.of('islamic'));//输出:IslamicconstcalendarNames=newIntl.DisplayNames('zh-CN',{type:'calendar'});console.log(calendarNames.of('gregory'));//输出:公历console.log(calendarNames.of('islamic'));//输出:伊斯兰历除了上述gregory格里高利历, 取值还有下面这些"buddhist":佛教历(Buddhist Calendar)"chinese":中国农历(Chinese Lunar Calendar)"coptic":科普特历(Coptic Calendar)"ethiopic":埃塞俄比亚历(Ethiopic Calendar)"gregory":格里高利历(Gregorian Calendar,即公历)"hebrew":希伯来历(Hebrew Calendar)"indian":印度历法(Indian Calendar),包括维基历(Vikram Samvat)等"islamic":伊斯兰历(Islamic Calendar)"persian":波斯历(Persian Calendar)"islamic-civil":伊斯兰历的公民版本(Islamic Civil Calendar),通常用于文书、合同等民事事务星期不得不说日本的和历, 真的是很神奇. 不是中文的周一到周日, 也不是Sunday-Saturday. 首先日本还有皇帝, 有皇帝就有年号. 常见下面的一些年份「明治时代 (Meiji Era)」:年号:明治(Meiji)年份范围:1868年 - 1912年注释:明治时代标志着日本的近代化和工业化的开始。「大正时代 (Taisho Era)」:年号:大正(Taisho)年份范围:1912年 - 1926年注释:大正时代是日本的一个相对短暂时期,也标志着日本的一些政治和社会变革。「昭和时代 (Showa Era)」:年号:昭和(Showa)年份范围:1926年 - 1989年注释:昭和时代见证了日本的战争和战后重建,以及日本成为现代工业强国。「平成时代 (Heisei Era)」:年号:平成(Heisei)年份范围:1989年 - 2019年注释:平成时代包括了日本的经济繁荣和一些社会变革。「令和时代 (Reiwa Era)」: 令和系奥特曼(https://baike.baidu.com/item/%E4%BB%A4%E5%92%8C%E7%B3%BB%E5%A5%A5%E7%89%B9%E6%9B%BC/50340521)年号:令和(Reiwa)年份范围:2019年 - 至今注释:令和时代是日本当前的年号,始于2019年5月1日,标志着新的时代的开始。可以看到日本的日历起始时周日(日), 但是周一到周六分别对应月火水木金土. 与众不同//获取今天过去7天的日期//日期对象consttoday=newDate();//创建一个选项对象,指定输出的语言和风格constoptionsCN={weekday:'long'};constoptionsJP={weekday:'long'};//获取过去一周的日期console.log('\n过去一周的日期:');constCNArr=[]constJPArr=[]for(leti=0;i0.25%,notdead"//指定目标浏览器,选取全球使用率超过0.25%的浏览器版本},],],};「babel底层使用core-js(https://github.com/zloirock/core-js)进行polyfill, 但是core-js不包含Intl API部分的polyfill(https://github.com/zloirock/core-jstab=readme-ov-file#missing-polyfills), 所以babel并不能为Intl API做polyfill」Nodejs使用安装npmi@formatjs/intl使用import{createIntl,createIntlCache}from'@formatjs/intl'//Thisisoptionalbuthighlyrecommended//sinceitpreventsmemoryleakconstcache=createIntlCache()constintlFr=createIntl({locale:'fr-FR',messages:{},},cache)constintlEn=createIntl({locale:'en-US',message:{},cache})//Callimperativelyconsole.log(intlFr.formatNumber(2000000000000))//2000000000000console.log(intlEn.formatNumber(2000000000000))//2,000,000,000,000
|
|