如何在React Native中为JavaScript Intl API实现Polyfill?
已关闭: 此问题源于我方bug,详情见答案。
问题描述
我正在安卓平台测试一款自定义JavaScript运行时,使用的示例React Native应用为Zulip Mobile(对应某GitHub提交版本)。该运行时支持多数现代JavaScript特性,但缺少对Intl API的支持。
这款RN应用通过react-intl的IntlProvider组件使用Intl,核心代码如下:
import { IntlProvider, IntlContext } from 'react-intl'; // ... export default function TranslationProvider(props: Props): Node { const { children } = props; const language = useGlobalSelector(state => getGlobalSettings(state).language); return ( <IntlProvider locale={language} textComponent={Text} messages={messages[language]}> <TranslationContextTranslator>{children}</TranslationContextTranslator> </IntlProvider> ); }
运行时触发以下错误:
ReferenceError: 'Intl' is not defined Component Stack: in IntlProvider in Unknown in RCTView in Unknown in L in Connect(L) in Unknown in RNCSafeAreaProvider in Unknown in Unknown in n in Unknown in b in O in Unknown in RCTView in Unknown in RCTView in Unknown in b Call Stack: at <anonymous> (index.android.bundle) at <anonymous> (index.android.bundle) at <anonymous> (index.android.bundle) at o (index.android.bundle) at jt (index.android.bundle) at ul (index.android.bundle) at ql (index.android.bundle) at Fa (index.android.bundle) at Ua (index.android.bundle) at La (index.android.bundle) at Ea (index.android.bundle) at ft (index.android.bundle) at Te (index.android.bundle) at Pe (index.android.bundle) at notify (index.android.bundle) at notifyNestedSubs (index.android.bundle) at f (index.android.bundle) at b (index.android.bundle) at <anonymous> (index.android.bundle) at <anonymous> (index.android.bundle) at <anonymous> (index.android.bundle) at <anonymous> (index.android.bundle) at call (native) at v (index.android.bundle) at <anonymous> (index.android.bundle) at <anonymous> (index.android.bundle) at call (native) at v (index.android.bundle) at o (index.android.bundle) at <anonymous> (index.android.bundle) at u (index.android.bundle) at <anonymous> (index.android.bundle) at apply (native) at <anonymous> (index.android.bundle) at k (index.android.bundle) at w (index.android.bundle) at callReactNativeMicrotasks (index.android.bundle) at value (index.android.bundle) at <anonymous> (index.android.bundle) at value (index.android.bundle) at value (index.android.bundle) at value (index.android.bundle)
我尝试用intl和formatjs包做Polyfill,但均未成功,错误信息一致。我没有React Native开发经验,可能Polyfill方法有误,希望得到指点。
已尝试的两种Polyfill写法:
写法一
// index.js /* @flow strict-local */ // React Navigation requires this react-native-gesture-handler import, // as the very first import of this entry-point file. See our #5373. import 'react-native-gesture-handler'; import IntlPolyfill from 'intl'; // <- import { AppRegistry } from 'react-native'; import ZulipMobile from './src/ZulipMobile'; if (!globalThis.Intl) { globalThis.Intl = IntlPolyfill; } AppRegistry.registerComponent('ZulipMobile', () => ZulipMobile);
写法二
// index.js /* @flow strict-local */ // React Navigation requires this react-native-gesture-handler import, // as the very first import of this entry-point file. See our #5373. import 'react-native-gesture-handler'; import { AppRegistry } from 'react-native'; import ZulipMobile from './src/ZulipMobile'; // polyfills import '@formatjs/intl-getcanonicallocales/polyfill'; import '@formatjs/intl-locale/polyfill'; import '@formatjs/intl-displaynames/polyfill'; import '@formatjs/intl-displaynames/locale-data/en'; if (!global.Intl) { throw new Error('Intl is missing???'); } AppRegistry.registerComponent('ZulipMobile', () => ZulipMobile);
也参考过多个相关问题的解决方案,但均无效。
解决方案
1. 调整Polyfill加载顺序,确保全局注入优先
问题大概率是react-intl在Polyfill注入全局前就已加载。修改index.js,将Polyfill的导入和全局赋值放在所有业务代码之前(除了强制要求的react-native-gesture-handler):
// index.js import 'react-native-gesture-handler'; // 优先加载Intl Polyfill及对应语言包 import IntlPolyfill from 'intl'; import 'intl/locale-data/jsonp/en'; // 按需添加应用需要的语言包 // 注入全局对象 if (typeof globalThis.Intl === 'undefined') { globalThis.Intl = IntlPolyfill; } // 再导入其他依赖和应用入口 import { AppRegistry } from 'react-native'; import ZulipMobile from './src/ZulipMobile'; AppRegistry.registerComponent('ZulipMobile', () => ZulipMobile);
2. 直接向IntlProvider传入Polyfill实例
react-intl支持手动传入Intl实例,无需依赖全局变量。修改TranslationProvider组件:
import { IntlProvider, IntlContext } from 'react-intl'; import IntlPolyfill from 'intl'; import 'intl/locale-data/jsonp/en'; // 按需添加语言包 // ... export default function TranslationProvider(props: Props): Node { const { children } = props; const language = useGlobalSelector(state => getGlobalSettings(state).language); return ( <IntlProvider locale={language} textComponent={Text} messages={messages[language]} intl={new IntlPolyfill.Intl(language)} // 手动传入Polyfill实例 > <TranslationContextTranslator>{children}</TranslationContextTranslator> </IntlProvider> ); }
3. 确认自定义运行时的全局对象
部分自定义JS运行时可能不使用globalThis或global作为全局对象,可先打印确认:
// 在index.js最顶部添加(在任何导入之前) console.log('全局对象:', this); console.log('当前Intl:', this.Intl);
根据打印结果调整Polyfill的赋值目标,比如如果全局对象是window,就赋值给window.Intl。
4. 补全formatjs的所有必要Polyfill
若使用formatjs,需确保安装并导入react-intl依赖的全部Polyfill包:
import 'react-native-gesture-handler'; // 导入所有基础Polyfill import '@formatjs/intl-polyfill'; import '@formatjs/intl-getcanonicallocales/polyfill'; import '@formatjs/intl-locale/polyfill'; import '@formatjs/intl-displaynames/polyfill'; import '@formatjs/intl-displaynames/locale-data/en'; // 补充react-intl常用的格式化类Polyfill import '@formatjs/intl-datetimeformat/polyfill'; import '@formatjs/intl-datetimeformat/locale-data/en'; import '@formatjs/intl-numberformat/polyfill'; import '@formatjs/intl-numberformat/locale-data/en'; // 验证Polyfill是否生效 if (!globalThis.Intl) { throw new Error('Intl Polyfill加载失败'); } import { AppRegistry } from 'react-native'; import ZulipMobile from './src/ZulipMobile'; AppRegistry.registerComponent('ZulipMobile', () => ZulipMobile);
内容的提问来源于stack exchange,提问作者Rynco Maekawa
相关产品推荐
相关产品推荐

