Flutter三方库适配OpenHarmony【flutter_libphonenumber】——57个国家格式化规则的数据结构设计
本文深入分析了Flutter三方库flutter_libphonenumber适配OpenHarmony时的国家数据组织方式。该库采用硬编码方式存储57个国家的电话号码数据,覆盖全球90%以上人口。数据按洲际分布(亚洲19国、欧洲22国等),包含各国区号、手机/固话格式、正则表达式等核心字段。重点解析了大中华区、东亚等地区的电话规则差异,展示了如何通过CountryData类实现高效格式化。这种设
前言
欢迎来到 Flutter三方库适配OpenHarmony 系列文章!本系列围绕 flutter_libphonenumber 这个 电话号码处理库 的鸿蒙平台适配,进行全面深入的技术分享。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


上一篇我们全面分析了 PhoneNumberUtil.ets 核心类的整体设计。本篇将深入其内部的 数据层——initCountryData() 方法中硬编码的 57 个国家数据,分析每个国家的数据如何组织、各字段的设计考量、以及不同洲际国家的格式化规则差异。
数据是格式化的基础。理解了这 57 个国家的数据结构,就理解了整个格式化系统的"原料"。
一、数据存储方式
1.1 initCountryData() 方法
所有国家数据通过 initCountryData() 方法在 PhoneNumberUtil 构造时加载:
private initCountryData(): void {
// 亚洲
this.countryDataMap.set('CN', new CountryData(
'China', '86', '13123456789', '1012345678',
'1[3-9]\\d{9}', '\\d{2,4}\\d{7,8}', '0'));
this.countryDataMap.set('JP', new CountryData(
'Japan', '81', '9012345678', '312345678',
'[789]0\\d{8}', '[1-9]\\d{8}', '0'));
// ... 共 57 个国家
}
1.2 为什么选择硬编码
| 方案 | 优点 | 缺点 |
|---|---|---|
| 硬编码(当前方案) | 零依赖、加载快、无解析开销 | 更新需改代码 |
| JSON 文件 | 易于更新 | 需要文件读取和解析 |
| Protobuf(Android方案) | 紧凑高效 | 需要 protobuf 解析器 |
| 数据库 | 支持查询 | 过于重量级 |
选择硬编码的原因:电话号码规则变化极少,57 个国家的数据量不大(约 400 个字符串值),硬编码是最简单直接的方案。
1.3 数据存储结构
countryDataMap: Map<string, CountryData>
│
├── 'CN' → CountryData('China', '86', ...)
├── 'US' → CountryData('United States', '1', ...)
├── 'GB' → CountryData('United Kingdom', '44', ...)
├── 'JP' → CountryData('Japan', '81', ...)
└── ... (共 57 个)
Map 的 key 是 ISO 3166-1 alpha-2 国家代码,value 是 CountryData 实例。
二、57 个国家的洲际分布
2.1 分布统计
| 洲 | 国家数 | 占比 | 覆盖的主要国家 |
|---|---|---|---|
| 亚洲 | 19 | 33% | CN, JP, KR, IN, SG, HK, TW, … |
| 欧洲 | 22 | 39% | GB, DE, FR, IT, ES, RU, … |
| 北美洲 | 3 | 5% | US, CA, MX |
| 南美洲 | 6 | 11% | BR, AR, CL, CO, PE, VE |
| 大洋洲 | 2 | 4% | AU, NZ |
| 非洲 | 5 | 9% | ZA, EG, NG, KE, MA |
| 合计 | 57 | 100% |
2.2 选择标准
这 57 个国家的选择基于以下标准:
- 人口覆盖 — 覆盖全球 90%+ 人口
- 经济体量 — 包含 G20 全部成员国
- 电话流量 — 覆盖国际通话量最大的国家
- 区域代表性 — 每个洲至少有代表性国家
- 华人聚集 — 包含 CN、HK、MO、TW、SG、MY 等华人聚集地区
三、亚洲国家数据(19 国)
3.1 大中华区(4 国/地区)
// 中国大陆
this.countryDataMap.set('CN', new CountryData(
'China', '86',
'13123456789', // 手机:1开头11位
'1012345678', // 固话:区号+号码
'1[3-9]\\d{9}', // 手机正则
'\\d{2,4}\\d{7,8}', // 固话正则
'0' // 国内前缀
));
// 香港
this.countryDataMap.set('HK', new CountryData(
'Hong Kong', '852',
'51234567', // 手机:5/6/7/8/9开头8位
'21234567', // 固话:2/3开头8位
'[5-9]\\d{7}',
'[2-3]\\d{7}',
'' // 无国内前缀
));
// 澳门
this.countryDataMap.set('MO', new CountryData(
'Macao', '853',
'66123456', // 手机:6开头8位
'28123456', // 固话:28开头8位
'6\\d{7}',
'28\\d{6}',
''
));
// 台湾
this.countryDataMap.set('TW', new CountryData(
'Taiwan', '886',
'912345678', // 手机:9开头9位
'212345678', // 固话:2-8开头
'9\\d{8}',
'[2-8]\\d{7,8}',
'0'
));
大中华区的特点:
| 地区 | 区号 | 手机位数 | 固话位数 | 国内前缀 |
|---|---|---|---|---|
| 中国大陆 | 86 | 11 | 10-12 | 0 |
| 香港 | 852 | 8 | 8 | 无 |
| 澳门 | 853 | 8 | 8 | 无 |
| 台湾 | 886 | 9 | 8-9 | 0 |
3.2 东亚(2 国)
// 日本
this.countryDataMap.set('JP', new CountryData(
'Japan', '81',
'9012345678', // 手机:090/080/070
'312345678', // 固话:03(东京)/06(大阪)
'[789]0\\d{8}',
'[1-9]\\d{8}',
'0' // 国内前缀0
));
// 韩国
this.countryDataMap.set('KR', new CountryData(
'South Korea', '82',
'1012345678', // 手机:010开头
'212345678', // 固话:02(首尔)
'1[0-9]\\d{7,8}',
'[2-6]\\d{7,8}',
'0'
));
3.3 南亚与东南亚(8 国)
| 国家 | 区号 | 手机示例 | 固话示例 | 前缀 |
|---|---|---|---|---|
| 印度 IN | 91 | 8123456789 |
1123456789 |
0 |
| 新加坡 SG | 65 | 81234567 |
61234567 |
无 |
| 马来西亚 MY | 60 | 123456789 |
312345678 |
0 |
| 泰国 TH | 66 | 812345678 |
21234567 |
0 |
| 越南 VN | 84 | 912345678 |
2101234567 |
0 |
| 菲律宾 PH | 63 | 9051234567 |
21234567 |
0 |
| 印尼 ID | 62 | 812345678 |
211234567 |
0 |
| 巴基斯坦 PK | 92 | 3012345678 |
2112345678 |
0 |
3.4 中东(5 国)
| 国家 | 区号 | 手机示例 | 固话示例 | 前缀 |
|---|---|---|---|---|
| 孟加拉 BD | 880 | 1812345678 |
27111234 |
0 |
| 阿联酋 AE | 971 | 501234567 |
22345678 |
0 |
| 沙特 SA | 966 | 512345678 |
112345678 |
0 |
| 以色列 IL | 972 | 502345678 |
21234567 |
0 |
| 土耳其 TR | 90 | 5012345678 |
2123456789 |
0 |
四、欧洲国家数据(22 国)
4.1 西欧主要国家(6 国)
// 英国
this.countryDataMap.set('GB', new CountryData(
'United Kingdom', '44',
'7400123456', // 手机:7开头10位
'1212345678', // 固话:区号+号码
'7[\\d]{9}',
'[1-9]\\d{9}',
'0'
));
// 德国
this.countryDataMap.set('DE', new CountryData(
'Germany', '49',
'15123456789', // 手机:015x/016x/017x
'30123456', // 固话:030(柏林)
'1[5-7]\\d{8,9}',
'[2-9]\\d{6,10}', // 固话长度可变!
'0'
));
// 法国
this.countryDataMap.set('FR', new CountryData(
'France', '33',
'612345678', // 手机:06开头9位
'123456789', // 固话:01开头9位
'6\\d{8}',
'[1-5]\\d{8}',
'0'
));
// 意大利
this.countryDataMap.set('IT', new CountryData(
'Italy', '39',
'3123456789', // 手机:3开头10位
'0212345678', // 固话:含区号
'3\\d{9}',
'0\\d{9,10}',
'' // 无前缀(区号是号码一部分)
));
// 西班牙
this.countryDataMap.set('ES', new CountryData(
'Spain', '34',
'612345678', // 手机:6/7开头9位
'912345678', // 固话:9开头9位
'[67]\\d{8}',
'[89]\\d{8}',
''
));
// 葡萄牙
this.countryDataMap.set('PT', new CountryData(
'Portugal', '351',
'912345678', // 手机:9开头9位
'212345678', // 固话:2开头9位
'9\\d{8}',
'2\\d{8}',
''
));
欧洲国家的复杂性:
| 特点 | 涉及国家 | 说明 |
|---|---|---|
| 固话长度可变 | DE, AT | 德国固话 7-11 位不等 |
| 无国内前缀 | IT, ES, PT, PL, DK, NO, GR, CZ | 直接拨号 |
| 区号是号码一部分 | IT | 意大利区号不可省略 |
| 两位数前缀 | HU | 匈牙利前缀为 ‘06’ |
| 特殊前缀 | RU | 俄罗斯前缀为 ‘8’ |
4.2 北欧(4 国)
| 国家 | 区号 | 手机示例 | 固话示例 | 前缀 |
|---|---|---|---|---|
| 瑞典 SE | 46 | 701234567 |
812345678 |
0 |
| 挪威 NO | 47 | 40612345 |
21234567 |
无 |
| 丹麦 DK | 45 | 32123456 |
32123456 |
无 |
| 芬兰 FI | 358 | 412345678 |
131234567 |
0 |
丹麦特殊性:丹麦的手机号和固话号码格式完全相同(都是 8 位),无法通过格式区分。
4.3 中东欧(6 国)
| 国家 | 区号 | 手机示例 | 固话示例 | 前缀 |
|---|---|---|---|---|
| 波兰 PL | 48 | 512345678 |
123456789 |
无 |
| 捷克 CZ | 420 | 601234567 |
212345678 |
无 |
| 希腊 GR | 30 | 6912345678 |
2123456789 |
无 |
| 匈牙利 HU | 36 | 201234567 |
12345678 |
06 |
| 罗马尼亚 RO | 40 | 712345678 |
212345678 |
0 |
| 爱尔兰 IE | 353 | 850123456 |
12345678 |
0 |
4.4 东欧(2 国)
// 俄罗斯
this.countryDataMap.set('RU', new CountryData(
'Russia', '7',
'9123456789', // 手机:9开头10位
'4951234567', // 固话:495(莫斯科)
'9\\d{9}',
'[3-9]\\d{9}',
'8' // 特殊前缀 '8'
));
// 乌克兰
this.countryDataMap.set('UA', new CountryData(
'Ukraine', '380',
'501234567', // 手机:50开头9位
'441234567', // 固话:44(基辅)
'5\\d{8}',
'4\\d{8}',
'0'
));
俄罗斯特殊性:俄罗斯的国内长途前缀是
'8'(而非常见的'0'),且与哈萨克斯坦共享+7区号。
五、美洲国家数据(9 国)
5.1 北美洲(3 国)
// 美国
this.countryDataMap.set('US', new CountryData(
'United States', '1',
'2015550123', // 10位,手机固话格式相同
'2015550123',
'[2-9]\\d{9}',
'[2-9]\\d{9}',
''
));
// 加拿大
this.countryDataMap.set('CA', new CountryData(
'Canada', '1', // 与美国共享 +1
'5062345678',
'5062345678',
'[2-9]\\d{9}',
'[2-9]\\d{9}',
''
));
// 墨西哥
this.countryDataMap.set('MX', new CountryData(
'Mexico', '52',
'12221234567', // 手机:1+区号+号码
'2221234567', // 固话:区号+号码
'1\\d{10}',
'[2-9]\\d{9}',
''
));
NANP(北美编号计划)的特点:
| 特点 | 说明 |
|---|---|
| 共享区号 | 美国和加拿大都使用 +1 |
| 统一格式 | 都是 10 位:(NPA) NXX-XXXX |
| 手机=固话 | 格式完全相同,无法区分 |
| 无前缀 | 直接拨 10 位号码 |
5.2 南美洲(6 国)
| 国家 | 区号 | 手机示例 | 固话示例 | 前缀 |
|---|---|---|---|---|
| 巴西 BR | 55 | 11912345678 |
1123456789 |
0 |
| 阿根廷 AR | 54 | 91123456789 |
1123456789 |
0 |
| 智利 CL | 56 | 961234567 |
221234567 |
无 |
| 哥伦比亚 CO | 57 | 3101234567 |
12345678 |
无 |
| 秘鲁 PE | 51 | 912345678 |
12345678 |
0 |
| 委内瑞拉 VE | 58 | 4121234567 |
2121234567 |
0 |
巴西特殊性:巴西手机号在 2012 年后增加了第 9 位数字,变成 11 位(区号2位 + 9 + 8位号码),是南美最复杂的号码体系。
六、大洋洲与非洲国家数据(7 国)
6.1 大洋洲(2 国)
// 澳大利亚
this.countryDataMap.set('AU', new CountryData(
'Australia', '61',
'412345678', // 手机:04xx
'212345678', // 固话:02(悉尼)/03(墨尔本)
'4\\d{8}',
'[2-9]\\d{8}',
'0'
));
// 新西兰
this.countryDataMap.set('NZ', new CountryData(
'New Zealand', '64',
'211234567', // 手机:021/022/027
'91234567', // 固话:09(奥克兰)
'2\\d{8}',
'[3-9]\\d{7}',
'0'
));
6.2 非洲(5 国)
| 国家 | 区号 | 手机示例 | 固话示例 | 前缀 |
|---|---|---|---|---|
| 南非 ZA | 27 | 711234567 |
101234567 |
0 |
| 埃及 EG | 20 | 1001234567 |
21234567 |
0 |
| 尼日利亚 NG | 234 | 8021234567 |
12345678 |
0 |
| 肯尼亚 KE | 254 | 712345678 |
202123456 |
0 |
| 摩洛哥 MA | 212 | 650123456 |
520123456 |
0 |
七、nationalPrefix 的全局分析
7.1 按前缀分类
| 前缀值 | 国家数 | 国家列表 |
|---|---|---|
'0' |
33 | CN, TW, JP, KR, IN, MY, TH, VN, PH, ID, PK, BD, AE, SA, IL, TR, GB, DE, FR, NL, BE, AT, CH, SE, FI, RU→8, UA, RO, IE, BR, AR, PE, VE, AU, NZ, ZA, EG, NG, KE, MA |
''(空) |
21 | HK, MO, SG, US, CA, MX, IT, ES, PT, NO, DK, PL, CZ, GR, CL, CO |
'8' |
1 | RU |
'06' |
1 | HU |
注意:上表中俄罗斯的前缀是
'8',不是'0'。这是因为俄罗斯的国内长途拨号使用8而非0。
7.2 前缀对格式化的影响
nationalPrefix 直接影响 formatNational() 的输出:
CN: formatNational('13123456789')
→ prefix='0' → formatChineseNumber()
→ '131 2345 6789'(中国手机号不加0前缀)
JP: formatNational('9012345678')
→ prefix='0' → formatJapaneseNumber()
→ '090-1234-5678'(日本加0前缀)
US: formatNational('2015550123')
→ prefix='' → formatNANPNumber()
→ '(201) 555-0123'(美国无前缀)
八、区号的特殊情况
8.1 共享区号
| 区号 | 共享国家 | 区分方式 |
|---|---|---|
1 |
US, CA | 通过 Area Code 区分 |
7 |
RU(本库中仅 RU) | 哈萨克斯坦未收录 |
8.2 区号长度分布
| 长度 | 区号 | 国家数 |
|---|---|---|
| 1 位 | 1, 7 | 3 (US, CA, RU) |
| 2 位 | 20, 27, 30, 31, 32, 33, 34, 36, 39, 40, 41, 43, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 81, 82, 84, 86, 90, 91, 92 | 39 |
| 3 位 | 212, 234, 254, 351, 353, 358, 380, 420, 852, 853, 880, 886, 966, 971, 972 | 15 |
8.3 parseInternationalNumber 的匹配顺序
由于区号长度不同,parseInternationalNumber() 从 3 位到 1 位 依次尝试:
输入: '+8613123456789'
尝试 3 位: '861' → 无匹配 ❌
尝试 2 位: '86' → 匹配 CN ✅
→ countryCode=86, nationalNumber='13123456789'
输入: '+85251234567'
尝试 3 位: '852' → 匹配 HK ✅
→ countryCode=852, nationalNumber='51234567'
从长到短 的匹配顺序确保了 3 位区号(如 852)不会被误匹配为 2 位区号(如 85)。
九、正则表达式的设计
9.1 手机号正则示例
| 国家 | 正则 | 含义 |
|---|---|---|
| CN | 1[3-9]\\d{9} |
1开头,第2位3-9,后跟9位数字 |
| US | [2-9]\\d{9} |
首位2-9,共10位 |
| GB | 7[\\d]{9} |
7开头,共10位 |
| JP | [789]0\\d{8} |
7/8/9开头+0,共10位 |
| DE | 1[5-7]\\d{8,9} |
15/16/17开头,10-11位 |
| FR | 6\\d{8} |
6开头,共9位 |
9.2 正则的用途
当前实现中,正则表达式字段(mobilePattern 和 fixedLinePattern)主要用于 文档和参考,实际的号码类型判断在 getNumberType() 中通过 硬编码规则 实现。这是一个有意的设计简化:
// 实际判断方式(硬编码)
if (region === 'CN') {
if (number.length === 11 && number.charAt(0) === '1') {
return 'mobile';
}
return 'fixedLine';
}
// 而非使用正则
// if (new RegExp(data.mobilePattern).test(number)) { ... }
设计考量:硬编码判断比正则匹配更快、更可控。对于 10 个主要国家,硬编码规则足够精确;对于其余国家,使用默认的长度判断。
十、Mask 的生成过程
10.1 从 CountryData 到 RegionInfo
getAllRegionInfo() 方法将原始的 CountryData(7 字段)转换为包含格式化信息的 RegionInfo(10 字段)。这个过程涉及 示例号码的格式化 和 Mask 的推导:
getAllRegionInfo(): Map<string, RegionInfo> {
let result = new Map<string, RegionInfo>();
this.countryDataMap.forEach(
(data: CountryData, region: string) => {
let mobileExample =
this.getExampleNumberForMobile(region);
let fixedLineExample =
this.getExampleNumberForFixedLine(region);
if (mobileExample !== null
&& fixedLineExample !== null) {
let info = new RegionInfo();
info.countryName = data.name;
info.phoneCode = data.code;
// 格式化示例号码
info.exampleNumberMobileNational =
this.formatNational(mobileExample, region);
info.exampleNumberFixedLineNational =
this.formatNational(fixedLineExample, region);
info.exampleNumberMobileInternational =
this.formatInternational(mobileExample);
info.exampleNumberFixedLineInternational =
this.formatInternational(fixedLineExample);
// 推导 Mask(数字替换为 0)
info.phoneMaskMobileNational =
this.maskNumber(
info.exampleNumberMobileNational);
info.phoneMaskFixedLineNational =
this.maskNumber(
info.exampleNumberFixedLineNational);
info.phoneMaskMobileInternational =
this.maskNumber(
info.exampleNumberMobileInternational);
info.phoneMaskFixedLineInternational =
this.maskNumber(
info.exampleNumberFixedLineInternational);
result.set(region, info);
}
}
);
return result;
}
10.2 maskNumber() 的实现
maskNumber(phoneNumber: string): string {
return phoneNumber.replace(/\d/g, '0');
}
这个方法极其简单——将所有数字替换为 0,保留其他字符(+、空格、连字符、括号等):
输入: '+86 131 2345 6789'
输出: '+00 000 0000 0000'
输入: '(201) 555-0123'
输出: '(000) 000-0000'
输入: '090-1234-5678'
输出: '000-0000-0000'
10.3 Mask 生成的完整链路
以中国手机号为例:
CountryData:
mobileExample = '13123456789'(纯数字)
code = '86'
步骤 1: getExampleNumberForMobile('CN')
→ PhoneNumber(countryCode=86, nationalNumber='13123456789')
步骤 2: formatNational(phoneNumber, 'CN')
→ applyNationalFormat('13123456789', 'CN', '0')
→ formatChineseNumber('13123456789')
→ '131 2345 6789'
步骤 3: formatInternational(phoneNumber)
→ formatWithSpaces('13123456789')
→ '+86 131 2345 6789'
步骤 4: maskNumber('131 2345 6789')
→ '000 0000 0000'
步骤 5: maskNumber('+86 131 2345 6789')
→ '+00 000 0000 0000'
10.4 10 国专用格式化对 Mask 的影响
由于 10 个国家有专用的 formatNational() 函数,它们的 National Mask 会反映各国独特的格式:
| 国家 | formatNational 结果 | National Mask |
|---|---|---|
| CN | 131 2345 6789 |
000 0000 0000 |
| US | (201) 555-0123 |
(000) 000-0000 |
| GB | 07400 123456 |
00000 000000 |
| JP | 090-1234-5678 |
000-0000-0000 |
| DE | 0151 2345 6789 |
0000 0000 0000 |
| FR | 06 12 34 56 78 |
00 00 00 00 00 |
| AU | 0412 345 678 |
0000 000 000 |
| BR | (11) 91234-5678 |
(00) 00000-0000 |
| IN | 81234 56789 |
00000 00000 |
| RU | 8 912 345-67-89 |
0 000 000-00-00 |
而其余 47 个国家使用通用的 formatWithSpaces(),Mask 格式统一为空格分隔:
通用格式: 000 0000 0000(按 3-4-N 分组)
十一、数据完整性验证
11.1 每个国家必须提供的 7 个字段
| 字段 | 是否可空 | 验证规则 |
|---|---|---|
name |
否 | 非空字符串 |
code |
否 | 1-3 位数字字符串 |
mobileExample |
否 | 纯数字,7-15 位 |
fixedLineExample |
否 | 纯数字,7-15 位 |
mobilePattern |
否 | 有效正则表达式 |
fixedLinePattern |
否 | 有效正则表达式 |
nationalPrefix |
是 | 空字符串或 1-2 位字符 |
11.2 getAllRegionInfo 的隐式验证
getAllRegionInfo() 方法中有一个隐式的数据完整性检查:
if (mobileEx !== null && fixedEx !== null) {
// 只有手机和固话示例都存在时才生成 RegionInfo
result.set(region, info);
}
如果某个国家的 mobileExample 或 fixedLineExample 为空,该国家会被 静默跳过,不会出现在最终的区域信息中。
十二、数据从 ArkTS 到 Dart 的完整流转
12.1 流转路径
ArkTS: initCountryData()
│ 57 × CountryData (7字段)
│
↓ getAllRegionInfo()
│ 57 × RegionInfo (10字段)
│
↓ handleGetAllSupportedRegions()
│ Map → Record → Record (两层转换)
│
↓ MethodChannel 传输
│ 二进制序列化
│
↓ Dart: getAllSupportedRegions()
│ Map<String, CountryWithPhoneCode>
│
↓ CountryManager().loadCountries()
│ List<CountryWithPhoneCode>
│
↓ 应用层使用
formatNumberSync() / TextFormatter / UI
12.2 字段数量变化
| 阶段 | 每国字段数 | 总字段数 |
|---|---|---|
| CountryData | 7 | 399 |
| RegionInfo | 10 | 570 |
| CountryWithPhoneCode | 11 | 627 |
字段数从 7 增长到 11,是因为:RegionInfo 增加了 National/International 两种格式的示例和 Mask(+3 字段),CountryWithPhoneCode 又增加了
countryCode(Map key 变为字段,+1 字段)。
十三、与 Android/iOS 数据方案的对比
13.1 三平台数据来源对比
| 对比项 | Android | iOS | 鸿蒙 |
|---|---|---|---|
| 数据来源 | Google libphonenumber | PhoneNumberKit | 硬编码 |
| 数据格式 | Protobuf 元数据 | 内置 JSON | ArkTS Map |
| 国家数量 | 200+ | 200+ | 57 |
| 数据大小 | ~1.5MB | ~800KB | ~15KB |
| 加载方式 | 运行时解析 | 运行时解析 | 编译时确定 |
| 更新方式 | 升级库版本 | 升级库版本 | 修改源码 |
13.2 鸿蒙方案的优劣分析
优势:
| 优势 | 说明 |
|---|---|
| 零依赖 | 不需要第三方号码处理库 |
| 加载快 | 无需解析 Protobuf/JSON |
| 体积小 | 15KB vs 1.5MB |
| 可控性强 | 每个国家的规则都可以精确调整 |
| 调试方便 | 所有数据一目了然 |
劣势:
| 劣势 | 说明 |
|---|---|
| 覆盖不全 | 57 国 vs 200+ 国 |
| 更新成本 | 需要手动修改代码 |
| 精度有限 | 正则和格式化规则是简化版 |
| 维护负担 | 号码规则变化时需要人工跟踪 |
13.3 57 国覆盖率分析
虽然只有 57 个国家,但覆盖了全球绝大部分电话流量:
| 指标 | 覆盖率 |
|---|---|
| 全球人口 | ~90% |
| G20 成员国 | 100% |
| 国际通话量 | ~95% |
| 华人聚集地区 | 100% |
| 主要经济体 | ~95% |
对于大多数应用场景,57 个国家的覆盖已经足够。如果需要支持更多国家,可以通过
init(overrides: {...})在 Dart 侧补充。
十四、扩展新国家的步骤
如果需要新增一个国家(如斯里兰卡 LK),只需在 initCountryData() 中添加一行:
this.countryDataMap.set('LK', new CountryData(
'Sri Lanka', // 国家名称
'94', // 区号
'712345678', // 手机示例
'112345678', // 固话示例
'7\\d{8}', // 手机正则
'1\\d{8}', // 固话正则
'0' // 国内前缀
));
如果该国需要专用的国内格式化,还需要:
- 在
applyNationalFormat()中添加else if (region === 'LK')分支 - 实现
formatSriLankanNumber()方法 - 在
AsYouTypeFormatter.formatPartialNational()中添加对应分支 - 实现
formatPartialSriLankan()方法
对于大多数国家,通用的
formatWithSpaces()已经足够,不需要专用格式化函数。
总结
本文深入分析了 57 个国家格式化规则的数据结构设计。关键要点回顾:
- 57 个国家数据通过 硬编码 方式存储在
initCountryData()中,每个国家 7 个字段,共 399 个值 - 国家分布覆盖 6 大洲:亚洲 19 国、欧洲 22 国、北美 3 国、南美 6 国、大洋洲 2 国、非洲 5 国
nationalPrefix是格式化的关键参数,大部分国家使用'0',美国等无前缀,俄罗斯使用'8',匈牙利使用'06'- 区号长度从 1 位到 3 位不等,
parseInternationalNumber()通过 从长到短 的匹配策略确保正确解析 - 正则表达式字段主要用于文档参考,实际类型判断使用 硬编码规则
- 数据从 ArkTS 的 7 字段
CountryData经过格式化处理,最终变成 Dart 侧的 11 字段CountryWithPhoneCode
下一篇我们将深入分析 format() 异步格式化的完整调用链路,从 Dart 调用到 MethodChannel 传输到 ArkTS 处理再到结果回传的全链路。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony 适配仓库:gitcode.com/oh-flutter/flutter_libphonenumber
- 开源鸿蒙跨平台社区:openharmonycrossplatform.csdn.net
- ISO 3166-1 alpha-2 国家代码:wikipedia.org - ISO 3166-1 alpha-2
- E.164 国际电话号码标准:itu.int - E.164
- ITU-T 国际电话区号分配:itu.int - E.164 Country Codes
- NANP 北美编号计划:nationalnanpa.com
- Google libphonenumber:github.com/google/libphonenumber
- ArkTS 语言文档:developer.huawei.com - ArkTS
- Flutter-OHOS 项目:gitee.com/openharmony-sig/flutter_flutter
- plugin_platform_interface:pub.dev/packages/plugin_platform_interface
更多推荐



所有评论(0)