处理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数据没有几个是最新的地址信息.....数据太老,导致有些地区不全.所以才想到天气预报官网特定有最新最全的数据.贴出代码,希望能给有相同困惑的朋友. ...
随机推荐
- 认真研究下HTML之id、name、form、submit
#起因 同事希望在提交之后关闭父窗口,但是,始终没有提交请求发出.他的代码大概如下: // <form id='f' action=... // <button onclick='fun( ...
- linux中查看某个端口(port)
查看 27017 端口: netstat -anp | grep 27017 Proto Recv-Q Send-Q Local Address ...
- JUnit4忽略(Ignore)测试实例
这种“忽略”是指方法还没有准备好进行测试,JUnit引擎会绕过(忽略)这个方法. import org.junit.*; /** * JUnit Ignore Test * @author yiiba ...
- Spring JDBC ResultSetExtractor接口示例
org.springframework.jdbc.core.ResultSetExtractor接口是JdbcTemplate的查询方法使用的回调接口.此接口的实现执行从ResultSet提取结果的实 ...
- C#提高-------------------Module的使用
如果没有看<C#反射(一)>.建议先看<C#反射(一)>再看这一篇.上一篇文章发表,有人评论我所写的东西比较基础.其实我也知道我也只不过是在写最基础的语法而已,之所以写它是因为 ...
- RNA_seq GATK 最佳实践
GATK处理DNA 水平的snp 经验比较成熟,而RNA 水平较少,所以可能会存在错误 目前的流程兼顾了假阳性(不是真的snp位点)和假阴性(该位点是snp,却没有检测到):后续会不断改善 G ...
- linux下重要的网络配置文件
linux下重要的网络配置文件:一; /etc/sysconfig/network 文件内容: NETWORKING=yes <= ...
- 启动zookeeper时出现的问题
zkEnv.cmd @echo off REM Licensed to the Apache Software Foundation (ASF) under one or more REM contr ...
- datepicker clone 控件错误
删除id,并删除hasDatepicker //+ - function changeRows(sender,desc){ var tr = $(sender).closest("tr&q ...
- 移植最新Uboot到JZ2440开发板
下载最新版U-boot,建立Source Insight工程,在建立工程的时候注意,去掉一些很明显不属于硬件的分支代码. 例如,arch目录下面的除开arm子目录之外的其他目录,都可 ...