ReactNative项目OpenHarmony三方库集成实战:react-native-localize
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
📋 前言
国际化(i18n)和本地化(l10n)是移动应用走向全球市场的关键步骤。无论是多语言支持、货币格式化、日期时间显示,还是度量单位转换,都需要获取用户设备的本地化设置。react-native-localize是一个强大的本地化工具库,提供了丰富的 API 来获取设备的语言、地区、货币、时区等本地化信息,帮助开发者轻松实现应用的国际化适配。
🎯 库简介
基本信息
- 库名称:
react-native-localize - 版本信息:
3.1.0+@react-native-ohos/react-native-localize: 支持 RN 0.72 版本3.4.2+@react-native-ohos/react-native-localize: 支持 RN 0.77 版本3.6.2+@react-native-ohos/react-native-localize: 支持 RN 0.82 版本
- 官方仓库: https://github.com/zoontek/react-native-localize
- 鸿蒙仓库: https://atomgit.com/openharmony-sig/rntpc_react-native-localize
- 主要功能:
- 🌐 获取用户首选语言列表
- 💱 获取用户首选货币代码
- 📅 获取日历格式和时区
- 🔢 获取数字格式设置
- 🕐 判断是否使用24小时制
为什么需要本地化库?
| 特性 | 手动获取 | react-native-localize |
|---|---|---|
| 语言列表 | ⚠️ 需调用原生API | ✅ 统一接口获取 |
| 货币代码 | ❌ 需自行推断 | ✅ 直接获取用户偏好 |
| 时区信息 | ⚠️ 需原生模块 | ✅ 跨平台一致API |
| 数字格式 | ❌ 需手动处理 | ✅ 获取格式设置 |
| 最佳语言匹配 | ❌ 需自行实现 | ✅ 内置匹配算法 |
| HarmonyOS 支持 | ❌ 无 | ✅ 完善适配 |
核心功能
| 功能 | 说明 | HarmonyOS 支持 |
|---|---|---|
| getLocales() | 获取用户首选语言列表 | ✅ |
| getCurrencies() | 获取用户首选货币代码 | ✅ |
| getCountry() | 获取用户所在国家代码 | ✅ |
| getCalendar() | 获取日历格式 | ✅ |
| getTimeZone() | 获取时区信息 | ✅ |
| uses24HourClock() | 判断是否使用24小时制 | ✅ |
| getNumberFormatSettings() | 获取数字格式设置 | ✅ |
| findBestLanguageTag() | 查找最佳匹配语言 | ✅ |
| getTemperatureUnit() | 获取温度单位 | ❌ |
| usesMetricSystem() | 判断是否使用公制 | ❌ |
兼容性验证
在以下环境验证通过:
- RNOH: 0.72.90;SDK: HarmonyOS 6.0.0 (API Version 20);IDE: DevEco Studio 6.0.2;ROM: HarmonyOS 6.0.0
📦 安装步骤
1. 安装依赖
请到三方库的 Releases 发布地址查看配套的版本信息:
| 三方库版本 | 发布信息 | 支持 RN 版本 |
|---|---|---|
| 3.1.0 | @react-native-ohos/react-native-localize | 0.72 |
| 3.4.2 | @react-native-ohos/react-native-localize | 0.77 |
| 3.6.2 | @react-native-ohos/react-native-localize | 0.82 |
# RN 0.72 版本npminstall@react-native-ohos/react-native-localize@3.1.1-rc.1# RN 0.77 版本npminstall@react-native-ohos/react-native-localize@3.4.2# RN 0.82 版本npminstall@react-native-ohos/react-native-localize@3.6.2# 或者使用 yarnyarnadd@react-native-ohos/react-native-localize2. 验证安装
安装完成后,检查package.json文件:
{"dependencies":{"@react-native-ohos/react-native-localize":"^3.1.1-rc.1"}}🔧 HarmonyOS 平台配置 ⭐
1. 在工程根目录的 oh-package.json5 添加 overrides 字段(看自己的版本是什么,在package.json中)
打开harmony/oh-package.json5,添加以下配置:
{ // ... 其他配置 "overrides": { "@rnoh/react-native-openharmony": "0.72.90" } }2. 引入原生端代码
方式一:通过 HAR 包引入(推荐)
💡提示:HAR 包位于三方库安装路径的
harmony文件夹下。
打开harmony/entry/oh-package.json5,添加以下依赖:
"dependencies": { "@react-native-ohos/react-native-localize": "file:../../node_modules/@react-native-ohos/react-native-localize/harmony/rn_localize.har" }点击右上角的sync按钮,或者在终端执行:
cdharmony/entry ohpminstall3. 配置 CMakeLists 和引入 RNLocalizePackage(仅 0.77 需要)
打开harmony/entry/src/main/cpp/CMakeLists.txt,添加:
project(rnapp)cmake_minimum_required(VERSION3.4.1)set(RNOH_APP_DIR"${CMAKE_CURRENT_SOURCE_DIR}")+set(OH_MODULES"${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")set(RNOH_CPP_DIR"${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")add_subdirectory("${RNOH_CPP_DIR}"./rn)# 添加 RNLocalize 模块+add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-localize/src/main/cpp"./rn_localize)add_library(rnoh_app SHARED"./PackageProvider.cpp""${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp")target_link_libraries(rnoh_app PUBLIC rnoh)# 链接 RNLocalize 库+target_link_libraries(rnoh_app PUBLIC rnoh_localize)打开harmony/entry/src/main/cpp/PackageProvider.cpp,添加:
#include"RNOH/PackageProvider.h"+#include"RNLocalizePackage.h"usingnamespacernoh;std::vector<std::shared_ptr<Package>>PackageProvider::getPackages(Package::Context ctx){return{+std::make_shared<RNLocalizePackage>(ctx),};}4. 在 ArkTs 侧引入 RNLocalizePackage
打开harmony/entry/src/main/ets/RNPackagesFactory.ts,添加:
importtype{RNPackageContext,RNPackage}from'rnoh/ts';+import{RNLocalizePackage}from'@react-native-ohos/react-native-localize/ts';exportfunctioncreateRNPackages(ctx:RNPackageContext):RNPackage[]{return[+newRNLocalizePackage(ctx),];}🚀 同步并运行
点击 DevEco Studio 右上角的sync按钮,或者在终端执行:
cdharmony/entry ohpminstall然后编译、运行即可。
📖 API 详解
getLocales() - 获取用户首选语言列表
返回用户首选的语言环境列表,按优先级排列。这是实现应用多语言支持的核心 API,通过获取用户的语言偏好来决定应用显示的语言版本。
返回值类型:Locale[]
Locale 对象结构:
| 属性 | 类型 | 说明 | 示例值 |
|---|---|---|---|
| languageCode | string | ISO 639-1 语言代码 | “zh”, “en”, “ja” |
| countryCode | string | ISO 3166-1 国家代码 | “CN”, “US”, “JP” |
| languageTag | string | 完整的 BCP 47 语言标签 | “zh-Hans-CN”, “en-US” |
| isRTL | boolean | 是否为从右到左的语言 | false, true |
使用场景:
- 应用启动时确定默认语言
- 多语言内容切换
- 判断是否需要 RTL 布局支持
import * as RNLocalize from 'react-native-localize'; const locales = RNLocalize.getLocales(); // 返回示例: // [ // { languageCode: "zh", countryCode: "CN", languageTag: "zh-Hans-CN", isRTL: false }, // { languageCode: "en", countryCode: "US", languageTag: "en-US", isRTL: false } // ] // 实际应用:根据用户语言偏好加载对应的翻译文件 const primaryLocale = locales[0]; const languageCode = primaryLocale.languageCode; // "zh" // 判断是否需要 RTL 布局 if (primaryLocale.isRTL) { // 应用 RTL 样式,如 flexDirection: 'row-reverse' }getCurrencies() - 获取用户首选货币代码
返回用户设备设置的首选货币代码列表,按优先级排列。这对于电商、金融类应用尤为重要,可以自动显示用户熟悉的货币符号。
返回值类型:string[]
返回值说明:返回 ISO 4217 货币代码数组,如 [“CNY”, “USD”]
使用场景:
- 电商应用自动显示本地货币价格
- 金融应用默认货币选择
- 价格格式化显示
const currencies = RNLocalize.getCurrencies(); // 返回示例:["CNY", "USD"] // 实际应用:根据货币代码格式化价格 const currencySymbols: Record<string, string> = { CNY: '¥', USD: '$', EUR: '€', JPY: '¥', }; const formatPrice = (amount: number, currency: string) => { const symbol = currencySymbols[currency] || currency; return `${symbol}${amount.toFixed(2)}`; }; const userCurrency = currencies[0]; // "CNY" const displayPrice = formatPrice(99.99, userCurrency); // "¥99.99"getCountry() - 获取用户所在国家代码
返回用户设备设置的国家/地区代码,用于判断用户的地理位置偏好。
返回值类型:string
返回值说明:返回 ISO 3166-1 alpha-2 国家代码,如 “CN”、“US”、“JP”
使用场景:
- 内容地区限制判断
- 默认配送地址设置
- 地区特定功能开关
const country = RNLocalize.getCountry(); // 返回示例:"CN" // 实际应用:根据国家显示不同内容 const getAvailableFeatures = (countryCode: string) => { const featuresByCountry: Record<string, string[]> = { CN: ['wechat-pay', 'alipay', 'unionpay'], US: ['apple-pay', 'google-pay', 'paypal'], JP: ['line-pay', 'paypay'], }; return featuresByCountry[countryCode] || ['credit-card']; }; const availablePaymentMethods = getAvailableFeatures(country);getCalendar() - 获取日历格式
返回用户设备设置的日历类型,用于日期相关的显示和计算。
返回值类型:string
常见返回值:
| 返回值 | 说明 | 主要使用地区 |
|---|---|---|
| “gregorian” | 公历(格里高利历) | 全球通用 |
| “chinese” | 农历 | 中国 |
| “japanese” | 日本年号历 | 日本 |
| “islamic” | 伊斯兰历 | 中东地区 |
使用场景:
- 日历应用日期显示
- 节假日判断
- 日期选择器本地化
const calendar = RNLocalize.getCalendar(); // 返回示例:"gregorian" // 实际应用:根据日历类型调整日期显示 const formatDateByCalendar = (date: Date, calendarType: string) => { switch (calendarType) { case 'chinese': // 返回农历日期(需要额外的农历转换库) return convertToLunarDate(date); case 'japanese': // 返回日本年号日期 return formatJapaneseYear(date); default: return date.toLocaleDateString(); } };getTimeZone() - 获取时区信息
返回用户设备设置的时区标识符,用于时间相关的计算和显示。
返回值类型:string
返回值说明:返回 IANA 时区标识符,如 “Asia/Shanghai”、“America/New_York”
使用场景:
- 跨时区时间显示
- 日程安排和提醒
- 服务器时间同步
const timeZone = RNLocalize.getTimeZone(); // 返回示例:"Asia/Shanghai" // 实际应用:将 UTC 时间转换为本地时间 const convertToLocalTime = (utcTime: Date, timeZone: string) => { const options: Intl.DateTimeFormatOptions = { timeZone, year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZoneName: 'short', }; return utcTime.toLocaleString('zh-CN', options); }; const localTime = convertToLocalTime(new Date(), timeZone); // 输出:"2024年1月15日 下午3:30 CST"uses24HourClock() - 判断是否使用24小时制
判断用户设备是否使用 24 小时制时间格式,用于时间显示的本地化。
返回值类型:boolean
返回值说明:true表示使用 24 小时制,false表示使用 12 小时制(AM/PM)
使用场景:
- 时间选择器格式
- 时间显示格式
- 日程时间显示
const uses24Hour = RNLocalize.uses24HourClock(); // 返回示例:true // 实际应用:根据用户偏好格式化时间 const formatTime = (hours: number, minutes: number, use24Hour: boolean) => { if (use24Hour) { return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`; } else { const period = hours >= 12 ? 'PM' : 'AM'; const displayHours = hours % 12 || 12; return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`; } }; const timeString = formatTime(15, 30, uses24Hour); // 24小时制输出:"15:30" // 12小时制输出:"3:30 PM"getNumberFormatSettings() - 获取数字格式设置
返回用户设备的数字格式化设置,包括小数分隔符和千位分隔符。
返回值类型:NumberFormatSettings
返回对象结构:
| 属性 | 类型 | 说明 | 中国示例 | 美国示例 |
|---|---|---|---|---|
| decimalSeparator | string | 小数分隔符 | “.” | “.” |
| groupingSeparator | string | 千位分隔符 | “,” | “,” |
💡提示:不同地区的数字格式差异较大,如欧洲部分地区使用逗号作为小数分隔符。
使用场景:
- 数字输入框格式化
- 金额显示
- 数据报表展示
const settings = RNLocalize.getNumberFormatSettings(); // 返回示例:{ decimalSeparator: ".", groupingSeparator: "," } // 实际应用:根据本地设置格式化数字 const formatNumber = (num: number, settings: typeof settings) => { const parts = num.toFixed(2).split('.'); const intPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, settings.groupingSeparator); return intPart + settings.decimalSeparator + parts[1]; }; // 中国/美国格式 formatNumber(1234567.89, { decimalSeparator: '.', groupingSeparator: ',' }); // 输出:"1,234,567.89" // 欧洲部分国家格式 formatNumber(1234567.89, { decimalSeparator: ',', groupingSeparator: '.' }); // 输出:"1.234.567,89"findBestLanguageTag() - 查找最佳匹配语言
从应用支持的语言列表中,找到与用户偏好最匹配的语言。这是实现应用多语言切换的关键 API。
参数:
languageTags: string[]- 应用支持的语言标签数组
返回值类型:{ languageTag: string; isRTL: boolean } | undefined
匹配规则:
- 首先尝试完全匹配(如 “zh-Hans-CN”)
- 其次尝试语言+脚本匹配(如 “zh-Hans”)
- 最后尝试仅语言匹配(如 “zh”)
使用场景:
- 应用启动时选择最佳语言
- 语言切换回退逻辑
- 多语言内容加载
const bestMatch = RNLocalize.findBestLanguageTag(['en-US', 'en', 'fr', 'zh']); // 返回示例:{ languageTag: "zh", isRTL: false } // 实际应用:选择最佳匹配语言加载翻译 const supportedLanguages = ['en', 'zh', 'ja', 'ko', 'es', 'fr']; const bestLanguage = RNLocalize.findBestLanguageTag(supportedLanguages); if (bestLanguage) { // 加载匹配的语言包 const translations = require(`./locales/${bestLanguage.languageTag}.json`); // 应用翻译 i18n.setTranslations(translations); } else { // 回退到默认语言 const fallbackTranslations = require('./locales/en.json'); i18n.setTranslations(fallbackTranslations); }useLocalize() - Hook 方式获取本地化信息
React Hook 方式获取本地化 API,适合在函数组件中使用。当本地化设置发生变化时,组件会自动重新渲染。
返回值:包含所有本地化方法的对象
使用场景:
- 函数组件中获取本地化信息
- 响应本地化设置变化
- 简化代码结构
import { useLocalize } from 'react-native-localize'; const MyComponent = () => { const { getLocales, getCurrencies, getCountry, getTimeZone } = useLocalize(); // 每次调用都会获取最新值 const locales = getLocales(); const currencies = getCurrencies(); const country = getCountry(); const timeZone = getTimeZone(); return ( <View> <Text>语言: {locales[0]?.languageTag}</Text> <Text>货币: {currencies[0]}</Text> <Text>国家: {country}</Text> <Text>时区: {timeZone}</Text> </View> ); };⚠️注意:与直接调用 API 不同,Hook 方式会在本地化设置变化时触发组件重新渲染。
📋 完整示例
importReact,{useMemo}from"react";import{SafeAreaView,ScrollView,StyleSheet,Text,View,}from"react-native";import*asRNLocalizefrom"react-native-localize";consttranslations:Record<string,Record<string,string>>={en:{welcome:"Welcome",language:"Language",currency:"Currency",timezone:"Timezone"},zh:{welcome:"欢迎",language:"语言",currency:"货币",timezone:"时区"},ja:{welcome:"ようこそ",language:"言語",currency:"通貨",timezone:"タイムゾーン"},};constApp:React.FC=()=>{constlocales=RNLocalize.getLocales();constcurrencies=RNLocalize.getCurrencies();constcountry=RNLocalize.getCountry();consttimeZone=RNLocalize.getTimeZone();constuses24Hour=RNLocalize.uses24HourClock();constnumberSettings=RNLocalize.getNumberFormatSettings();constbestMatch=RNLocalize.findBestLanguageTag(["en","zh","ja"]);constcurrentLang=(bestMatch?.languageTag.split("-")[0]askeyoftypeoftranslations)||"en";constt=translations[currentLang]||translations.en;constformatNumber=(num:number):string=>{constparts=num.toFixed(2).split(".");constintPart=parts[0].replace(/\B(?=(\d{3})+(?!\d))/g,numberSettings.groupingSeparator);returnintPart+numberSettings.decimalSeparator+parts[1];};return(<SafeAreaView style={styles.container}><ScrollView contentContainerStyle={styles.content}><Text style={styles.title}>{t.welcome}</Text><View style={styles.card}><Text style={styles.cardTitle}>本地化信息</Text><View style={styles.row}><Text style={styles.label}>{t.language}:</Text><Text style={styles.value}>{locales[0]?.languageTag}</Text></View><View style={styles.row}><Text style={styles.label}>{t.currency}:</Text><Text style={styles.value}>{currencies[0]}</Text></View><View style={styles.row}><Text style={styles.label}>{t.timezone}:</Text><Text style={styles.value}>{timeZone}</Text></View><View style={styles.row}><Text style={styles.label}>国家:</Text><Text style={styles.value}>{country}</Text></View><View style={styles.row}><Text style={styles.label}>24小时制:</Text><Text style={styles.value}>{uses24Hour?"是":"否"}</Text></View></View><View style={styles.card}><Text style={styles.cardTitle}>数字格式化示例</Text><Text style={styles.formatText}>1234.56→{formatNumber(1234.56)}</Text><Text style={styles.formatText}>9876543.21→{formatNumber(9876543.21)}</Text></View><View style={styles.card}><Text style={styles.cardTitle}>支持的语言列表</Text>{locales.map((locale,index)=>(<View key={index}style={styles.localeRow}><Text style={styles.localeText}>{locale.languageTag}</Text>{locale.isRTL&&<Text style={styles.rtlBadge}>RTL</Text>}</View>))}</View></ScrollView></SafeAreaView>);};conststyles=StyleSheet.create({container:{flex:1,backgroundColor:"#F5F5F5"},content:{padding:16},title:{fontSize:24,fontWeight:"bold",marginBottom:20,color:"#333",textAlign:"center"},card:{backgroundColor:"#FFF",borderRadius:12,padding:16,marginBottom:16},cardTitle:{fontSize:16,fontWeight:"600",marginBottom:12,color:"#333"},row:{flexDirection:"row",justifyContent:"space-between",paddingVertical:8,borderBottomWidth:1,borderBottomColor:"#F0F0F0"},label:{fontSize:14,color:"#666"},value:{fontSize:14,fontWeight:"500",color:"#333"},formatText:{fontSize:14,color:"#007AFF",marginVertical:4},localeRow:{flexDirection:"row",justifyContent:"space-between",alignItems:"center",paddingVertical:8},localeText:{fontSize:14,color:"#333"},rtlBadge:{backgroundColor:"#FF9500",color:"#FFF",paddingHorizontal:8,paddingVertical:2,borderRadius:4,fontSize:12},});exportdefaultApp;⚠️ 遗留问题
- HarmonyOS 侧无法获取温度及长度单位 issue#2
- SSR(Web 端专属)相关特性暂不支持鸿蒙系统 issue#13
- react-native-localize 的 Expo 配置插件暂不支持鸿蒙系统 issue#14
