处理i18n国际电话区号的代码实践
本文转载至 http://adad184.com/2015/08/18/practice-in-i18n-dialling-code/
前言
上周在忙产品的国际化(i18n)的问题 其中一个很重要的地方就是电话号码的国际化(我们以电话号码为主账号) 电话号码有个很重要的部分就是区号

上图是我们产品的区号选择 除了常规的电话号码之外 后面还有一个区号 代表这个电话号码所属的是哪个国家和地区 关于区号的概念 可以看一下维基百科
看到这里 可能有人奇怪 这有什么难的? 不就是按照列表来展示吗? 这样有几个问题
- 由于是支持多语言 那么不同的语言环境的系统 显示出来的国家名称是不一样的 比如“中国” 简体中文是“中国” 英文是“China” 韩文是“중화인민공화국” 其在各个语言中的显示排序都是不一样的
- 如果根据不同国家和语言来维护一张这样的表 工作量太大 一般的公司估计做不来
所以这个工作我们就会放到本地来做 不过iOS已经帮我们做了一部分工作了 我们可以根据国家代码来获取某个国家或在当前区域中的本地化名称
1 |
//获取当前locale |
我们简单测试一下
1 |
NSArray *countryArray = [NSLocale ISOCountryCodes]; |
结果
1 |
zh_CN AD 安道尔 |
已经介绍完iOS帮我们做的一部分工作了 那么另一部分就得我们自己来了
我们需要有一张 地区->区号 的列表 不过这个也简单 网上一抓一大把 我也是网上找的 文件内容如下(diallingcode.json)
1 |
[ |
维护这样一张表就很简单了我们可以存在本地 也可以放在服务器(“name”字段其实是不必须的 只是为了好看)
研究
我们暂时先把代码放一放 来看一看其他产品是怎么做的
这个是微信的
微信的问题还是挺多的
- 左边是中文环境 按拼音分组是分对了 但是文字排序却出错了 “阿”开头的国家并没有排列在一起
- 右边是法语环境 这些衍生拉丁字母 并没有正确的归类
这个是Twitter的
Twitter在中文环境下还是挺奇怪的 但是却没有犯微信第二个错误
Facebook的呢? 人家的工程师比较聪明(懒) 压根就不支持索引
接下来我们会解决出现的这几个问题
代码
先简历一个Modal 用来表示国家相关的信息
1 |
@interface MMCountry : NSObject @property (nonatomic, strong) NSString *name; //国家名(本地化后的版本) |
然后我们要把区号从配置文件中读取出来 并以区号为key 建立索引
1 |
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"diallingcode" ofType:@"json"]]; |
接着获取这些国家的本地话名称
1 |
NSLocale *locale = [NSLocale currentLocale]; |
这里要注意的是 把字母拉丁文化 解决了微信的第二个问题 使非基本拉丁字母也可以按照基本拉丁字母来排序 其函数如下
1 |
- (NSString*)latinize:(NSString*)str |
这里有两步
- 先将文字 转成拉丁字母(kCFStringTransformToLatin)
- 再将拉丁字母去掉变音符(kCFStringTransformStripDiacritics)
这里是微信犯的第一个错误 也就是没有正确归类的错误 因为微信在第一步的时候只针对汉字进行了处理 其他字符则没有处理 导致第二步没有得到正确的基本拉丁字符(kCFStringTransformMandarinLatin 参见注释掉的代码)
我们来测试一下这两步会造成得到效果 还是之前的例子
1 |
NSArray *countryArray = [NSLocale ISOCountryCodes]; |
结果
1 |
zh_CN AD 安道尔 | an dao er |
可以到看 系统会根据不同国家和不同语言的特点 将同一个国家的不同表达形式转化成不同的拉丁字母
接下来 我们把获取过的数据根据’A’-‘Z’进行归类
1 |
NSMutableDictionary *dicSort = [@{} mutableCopy];
for ( MMCountry *c in dicCountry.allValues )
|
最后 将每个归类下面的数据 排序重新整理
1 |
for ( NSString *key in dicSort.allKeys ) |
这样dicSort就是我们最终得到的结果集
这里是微信犯的第二个错误 微信的排序是按照latin来排序的(见注释掉的代码) 所以导致了相同汉字的国家排不到一起的情况 正确的方式是用
localizedStandardCompare来排序 这也是iOS已为我们提供好了的本地化比较函数
看看之前的图中 挑三个国家出来 比如:阿尔巴尼亚 爱尔兰 阿鲁巴 他们的拼音是 aerbabiya aierlan aluba 如果按照拼音排序的话 这样的排序就是正确的
我们来看看最终的效果
是不是比微信的更好?
讨论
虽然代码是写完了 但是问题并没有结果 有一个关键的问题就是 为什么我们要按照’A’-‘Z’来索引排序呢? 比如Twitter在日文和韩文环境下是这样的
其实按照不同国家的语言特点来进行对应的索引 应该才是最优的解决办法(PS:看到Twitter在中文环境下的糟糕结果 我也不确定其在日文和韩文下的结果是否是正确的(¯﹃¯)
当然 如果真要这样做 其实改动量也不大 只要在索引的那块稍微修改一下就行了
小结
文中的demo可以在这里找到
正如讨论中说的一样 本文所讨论的方案 并不是最终的解决方案 如果需要更好的体验的话 还要深入研究各国的文化才行 所以 国际化并不单纯是个技术问题 更是个社会工程啊~~~~
处理i18n国际电话区号的代码实践的更多相关文章
- 支持中英文和国旗的android国家代码/国际电话区号选择器
最近在做app登录的时候,因为需要支持国外手机号注册和登录,所以就涉及到国际电话区号的选择.在github上面找了一下,国家名称基本都是只有英文版本,而手动的去把中文一个个加上实在是一件费时费力的事情 ...
- 国际电话区号SQL
CREATE TABLE `phone_prefix` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `country` varchar(30) N ...
- 国际电话号码的区号mysql数据表
-- phpMyAdmin SQL Dump-- version 3.5.2-- http://www.phpmyadmin.net---- Host: localhost-- Generation ...
- 分享,iOS国家手机区号代码.plist
APP注册需要手机号码的时候,如果有在其他国家的时候需要填写手机区号 一份有国家名字和区号的plist 参照微信注册的时候 格式是 <Array> <Array> <Ar ...
- 生活常用类API调用的代码示例合集:邮编查询、今日热门新闻查询、区号查询等
以下示例代码适用于 www.apishop.net 网站下的API,使用本文提及的接口调用代码示例前,您需要先申请相应的API服务. 邮编查询:通过邮编查询地名:通过地名查询邮编 今日热门新闻查询:提 ...
- Android 国际区号注册手机号编码 以及常用城市列表
附上 国际区号编码:我是定义到arrays.xml里面了 <?xml version="1.0" encoding="utf-8"?> <re ...
- [原创]JAVA号码工具类:实现手机固话号码判断与区号截取
工具类说明 该工具类主要是用于判断号码的类型,如果是手机类型,则返回号码前7位,便于后面继续判断号码归属地:如果是固话类型,则截取固话的区号,同样也是为了后面判断号码的归属地. 在获取到这些信息之后, ...
- HtmlAgilityPack解析全国区号页面到XML
需求:完成一个城市和区号的xml配置文件 处理思路:通过HtmlAgilityPack解析一个区号页面,生产xml文件 页面:http://www.hljboli.gov.cn/html/code.h ...
- java利用爬虫技术抓取(省、市(区号\邮编)、县)数据
近期项目须要用到 城市的地址信息,但从网上下载的xml数据没有几个是最新的地址信息.....数据太老,导致有些地区不全.所以才想到天气预报官网特定有最新最全的数据.贴出代码,希望能给有相同困惑的朋友. ...
随机推荐
- C艹函数与结构体
传递指针 代码: #include <iostream> #include <cmath> struct polar{ double distance; double angl ...
- Oracle 数据泵使用详解
数据泵使用EXPDP和IMPDP时应该注意的事项: EXP和IMP是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用. EXPDP和IMPDP是服务端的工具程序,他们只能在ORACLE服务端 ...
- java 5.0引入的新特性-枚举
概念 首先,枚举并不是一种新技术,而是一种基础数据类型.它隶属于两种基础类型中的值类型,如下: 2. 为什么要有枚举 枚举在真正的开发中是非常常用的,它的作用很简单也很纯粹:它定义了一种规范,就是要 ...
- e793. 监听JSpinner数据变化
// Create a nummber spinner JSpinner spinner = new JSpinner(); // Add the listener spinner.addChange ...
- 使用selenium遇到java.lang.NoSuchMethodError: org.apache.xpath.XPathContext,排查
初试selenium webdriver,运行小程序,抛如下错误: java.lang.NoSuchMethodError: org.apache.xpath.XPathContext.<i ...
- 如何获取模拟器安装的app的位置
你可以死记下地址格式, 但是一旦不同的xcode和模拟器版本改变变了地址, 又得记, 从活动管理器里其实是可以直接查看的: Launch the app in the simulator Open A ...
- pytest集成Allure Report
https://blog.csdn.net/liuchunming033/article/details/79624474#commentBox https://blog.csdn.net/lihua ...
- 如何使用 URLOpenStream 函数
URLOpenStream 和 URLDownloadToFile 类似, 都是下载文件的 COM 函数; 前者是下载到 IStream 流, 后者是直接下载到指定路径; 不如后者使用方便. 它们都声 ...
- 代码记录——phase16,block36(修正后)
加入边缘判定,<2则加2. if (x_upleft<2) x_upleft=x_upleft+2; if (y_upleft<2) y_upleft=y_upleft+2; HRE ...
- 【转】细谈Redis和Memcached的区别
Redis的作者Salvatore Sanfilippo曾经对这两种基于内存的数据存储系统进行过比较: Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支 ...