【NC】简析NC6多语言实现

闲着无聊,分析一下多语实现。
一般在使用过程中,我们使用变量的方式来获取数据例如

nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("4111002_0", "04111002-0021")

我们是要通过ID的方式获取字符串的值,返回值可以是中文、繁体中文、英文等根据系统选择的语言来返回对应值。

  • AbstractNCLangRes

首先返回一个处理类,我们在服务器上使用多语处理和在客户端使用多语处理是不一样的所以又引申出两个NCLangResOnserver和NCLangRes

/*** 根据是否服务端、客户端返回该端使用的AbstractNCLangRes的具体实例* @return AbstractNCLangRes */public static synchronized AbstractNCLangRes getNCLangRes(){if (nclangres == null) {try {String sClassname = null;if (RuntimeEnv.getInstance().isRunningInServer()){sClassname = "nc.bs.ml.NCLangResOnserver";}else{sClassname = "nc.ui.ml.NCLangRes";}   Method method = Class.forName(sClassname).getMethod("getInstance",null);nclangres = (AbstractNCLangRes)method.invoke(null,null);} catch (Exception e) {e.printStackTrace();}}return nclangres;}

获取处理类后进行具体的处理方法

/**
根据产品编码,资源ID返回翻译的字符串
* @return String 翻译后的字符串
* @param productCode java.lang.String 产品编码
* @param resId java.lang.String 将要翻译的简体中文字符串的资源ID
*/
public String getStrByID(String  productCode, String  resId){String str = getStringByPath(productCode, null,resId, null);return str;
}

实际上现在用的是适配器的设计模式,进行适配处理,实际上最后处理方法如下

/**
根据产品编码,简体中文,资源ID,搜索路径返回翻译的字符串
* @return String 翻译后的字符串
* @param productCode java.lang.String 产品编码
* @param simpleChinese java.lang.String 将要翻译的简体中文字符串
* @param resId java.lang.String 将要翻译的简体中文字符串的资源ID
* @param path java.lang.String  翻译的资源路径
*/
public String getStringByPath(String  productCode,  String  simpleChinese,  String  resId, String path){if(AbstractAppEnvML.enableAppEnvML()){return getStringByPathInAppEnvML(productCode, simpleChinese, resId,path);}String str = null;try {ITranslator translator = LanguageTranslatorFactor.getInstance().getTranslator(getCurrLanguage());str = getString(translator,productCode, simpleChinese, resId, path);} catch (Exception e) {e.printStackTrace();if(simpleChinese != null)return simpleChinese;elsereturn resId;}return str;
}

这句根据不同的语言获取不同的翻译器,此处很明显,工厂模式

LanguageTranslatorFactor.getInstance().getTranslator(getCurrLanguage());

实际处理多语的方法我们不难发现是不同翻译器里面的方法

/**
根据具体的翻译器,产品编码,简体中文,资源ID,资源路径返回翻译的字符串
* @return String 翻译后的字符串
* @param translator ITranslator 具体的翻译器
* @param productCode java.lang.String 产品编码
* @param simpleChinese java.lang.String 将要翻译的简体中文字符串
* @param resId java.lang.String 将要翻译的简体中文字符串的资源ID
* @param path java.lang.String  翻译的资源路径
*/
protected String getString(ITranslator translator,String  productCode,  String  simpleChinese,  String  resId, String path){String str = null;try {if (resId == null){Exception e = new Exception("resid can't be null");Logger.error(e.getMessage(), e);return null;
//          productCode = IProductCode.PRODUCTCODE_COMMON;//对于id为null,目前规定都要把资源放在COMMON中
//          //对于resID为null的,自动插入到本地文件中为了资源上传到数据库中(这个只是在测试环境中才能用)
//          resWrite.writeToFile4Test(StringUtil.filterString(simpleChinese));}
//      //增加一个分类类型来定义用户自定义的资源,分类类型为usercustom
//      try{//          str = translator.getString(IProductCode.PRODUCTCODE_USERCUSTOM, simpleChinese, resId);
//      }catch(java.util.MissingResourceException e){
            System.out.println("查找资源失败:"+simpleChinese+"/"+resId+"/"+productCode);
//      }//if (productCode != null)try{str = translator.getString(productCode, simpleChinese, resId);}catch(java.util.MissingResourceException e){
//              System.out.println("查找资源失败:"+simpleChinese+"/"+resId+"/"+productCode);}if (str == null&&path != null){String[] searchPath = splitSearchPath(path);for (int i = 0; i < searchPath.length; i++){try{str = translator.getString(searchPath[i], simpleChinese, resId);}catch(java.util.MissingResourceException e){
//                  System.out.println("查找资源失败:"+simpleChinese+"/"+resId+"/"+searchPath[i]);}if (str != null)break;}}if (str == null&&( productCode == null || !productCode.equals(IProductCode.PRODUCTCODE_COMMON))) {//productCode为COMMON时,就不用再去找了try{str = translator.getString(IProductCode.PRODUCTCODE_COMMON, simpleChinese, resId);}catch(java.util.MissingResourceException e){
//              System.out.println("查找资源失败:"+simpleChinese+"/"+resId+"/"+IProductCode.PRODUCTCODE_COMMON);}}if (str == null){
//          System.out.println("查找资源失败:"+productCode+"/"+simpleChinese+"/"+resId+"/"+path);str = simpleChinese;}if (str == null && resId != null) {str = resId;}} catch (Exception e) {e.printStackTrace();}return str;
}

我们就简单找一个simplechinese来看一下

/**
* 创建日期:(2004-9-9 16:24:39)
* @return java.lang.String
* @param productCode java.lang.String
* @param simpleChinese java.lang.String
* @param resourceId java.lang.String
*/
public String getString(String productCode, String simpleChinese, String resourceId) {
String str = null;
if(resourceId != null){
MLMessageResource mr = LangResourceBoundleCache.getInstance().getMessageResource(productCode, getLanguage());
if(mr != null){
str = mr.getString(resourceId);
}

}   return str;

}

具体的处理是先获取资源,获取资源的方法如下

public MLMessageResource getMessageResource(String langClass, Language lang) {langClass = langClass.toLowerCase();String langCode = lang.getCode();String resKey = getResKey(langClass, langCode); MLMessageResource mesResource = (MLMessageResource) getResourceBoundleCache().get(resKey);if(mesResource == null){synchronized (LangResourceBoundleCache.class) {mesResource = (MLMessageResource) getResourceBoundleCache().get(resKey);if (mesResource == null) {try {mesResource = new MLMessageResource(lang,langClass);getResourceBoundleCache().put(resKey, mesResource);} catch (Exception e) {}}}}return mesResource;}

其中getResKey这个方法很重要为什么?来看一下

/*** 根据产品编码和语言编码构造资源缓存用的键值, 同时这个字符串也是资源的路径*/private String getResKey(String productCode, String langCode) {StringBuffer sb = new StringBuffer("lang/");sb.append(langCode).append("/").append(productCode);// .append(".")// .append(productCode).append("res");return sb.toString();}

这个是找到对应的目录文件/lang的文件夹下,然后在缓存中(此处为HashMap)获取资源,如果没有就缓存到HashMap中去,供下次同模块的多语调用

回到主线上来,多语资源返回后getString(String resid) 这个字段返回了真正处理后的值,那我们看一下

public String getString(String resid){String retr = cachMap.get(resid);if(retr == null){retr = getStringByType(MLMessageResourceBundle.SPEC_RES_PREFIX_CUSTOMER, resid);if(retr == null){retr = getStringByType(MLMessageResourceBundle.SPEC_RES_PREFIX_PARTNER, resid);}if(retr == null){retr = getStringByType(MLMessageResourceBundle.SPEC_RES_PREFIX_HY, resid);}if(retr == null){retr = getStringByType(null, resid);}if(retr == null){retr = getStringByType(MLMessageResourceBundle.SPEC_RES_PREFIX_PLATFORM, resid);}if(retr != null){cachMap.put(resid, retr);}}return retr;}

这个时候,根据了开发维度做了处理,客户化开发、伙伴开发、行业开发、和平台开发,可以看出,客户化开发最新加载所以维度权重也就不言而喻了。我们主要看一下水平开发维度上的处理

private String getStringByType(String rbType, String resid){String retr = null;MLMessageResourceBundle rb = null;String hyCode = getHYCode();if(hyCode != null && hyCode.trim().length() > 0){do {rb = getMessageResourceBundle(rbType, hyCode);try{retr = rb.getString(resid);}catch(MissingResourceException e){}if(hyCode.length() > 1){hyCode = hyCode.substring(0, hyCode.length() - 1);}else{break;}} while (retr == null);}if(retr == null){rb = getMessageResourceBundle(rbType, "");try{retr = rb.getString(resid);}catch(MissingResourceException e){}}return retr;}

这里面MLMessageResourceBundle起到了作用,我们分析一下
发现了

public class MLMessageResourceBundle extends ResourceBundle implements Serializable

里面有一个构造方法

@SuppressWarnings("unchecked")public MLMessageResourceBundle(String langClass, Language lang ,String typePrefix)  {// 形同lang.englis.1002langClass = langClass.toLowerCase();boolean isSysStandard = typePrefix == null || typePrefix.trim().length() == 0;String langCode = lang.getCode();String charsetName = lang.getCharsetName();String resDir = getResKey(langClass, langCode);UTFProperties properties = new UTFProperties(charsetName);ArrayList<String> al = getFilesAL(langCode, langClass, resDir);// (ArrayList)getLangResBoundleFiles(langCode).get(resDir);int count = al == null ? 0 : al.size();ClassLoader cl = getClass().getClassLoader();for (int i = 0; i < count; i++) {String name = al.get(i);if(!validateFile(name, typePrefix, isSysStandard)){continue;}//InputStream in = null;try {in = cl.getResourceAsStream(name);if (in != null) {in = new BufferedInputStream(in);properties.load(in);}else{Logger.error("multi language resource load failed:"+name);}} catch (Exception e) {Logger.error("multi language resource load failed for exception :" + name+"  exception msg is "+e.getMessage());Logger.error(e.getMessage(),e);} finally {if (in != null){try {in.close();} catch (Exception e2) {}}}}lookup = new HashMap<String, String>(properties);}

这个时候就已经很明显了
langcode表示语言,langclass表示模块名称,至于resDir就是多余的存放目录文件

getFilesAL(langCode, langClass, resDir);

把对应的properties文件读取出来进行加载,这是在NC6客户端启动时对所有模块做的工作,所以模块太多启动时会比较慢,因为涉及到了太多文件读取。

我们最后发现,支持多语并不是指的多语翻译,是将预置好的编码放到properties文件中,需要的时候读取。这一整套思想还是很值得学习的。

至于为什么要区分server和client,是为解决广域网问题,区分前后台,客户端启用缓存机制。

【NC】简析NC6多语言实现相关推荐

  1. 简析 Java语言的过载与重载

    复习一下 :  过载 (override)     重载 (overload)  在代码实践中 理清概念. package sub;/*** 简析 过载与重载* User: yiminghe* Dat ...

  2. java 进阶笔记线程与并发之ForkJoinPool简析

    简介 ForkJoinPool是一个线程池,支持特有的的ForkJoinTask,对于ForkJoinTask任务,通过特定的for与join方法可以优化调度策略,提高效率. 使用 通常,我们继承使用 ...

  3. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  4. Python源码学习:内建类型简析并简析int对象

    Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> 上一篇文章中已经大致分析了下,Python的启动执行流程,现在我们分析一下Pytho ...

  5. Python源码学习:启动流程简析

    Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> Python简介: python主要是动态语言,虽然Python语言也有编译,生成中 ...

  6. java jdbc 教程_java JDBC系列教程之JDBC类的简析与JDBC的基础操作

    什么是JDBC? 概念:JAVA Database Connectivity Javas数据库连接,Java语言操作数据库接口,然后由各个数据库厂商去实现这个接口,提供数据库驱动java包,我们可以使 ...

  7. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  8. 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析

     # 写在前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章 ...

  9. java 常见 错误_JAVA几个常见错误简析

    JAVA几个常见错误简析 Java看起来设计得很像C++,但是为了使语言小和容易熟悉,设计者们把C++语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的.下面就来和小编一起看看JAVA几个常见 ...

最新文章

  1. Tomcat 与 Resin PK大战
  2. 数据挖掘十大经典算法之——AdaBoost 算法
  3. Python自动发送邮件-smtplib和email库
  4. linux swap分区与内存,虚拟内存和swap分区的关系
  5. Interesting Finds: 2008.03.24
  6. MFC读取配置文件GetPrivateProfileString
  7. 终端执行php,PHP命令行执行PHP脚本的注意事项总结
  8. php 文章浏览量 缓存,WordPress缓存文章浏览量访问不自动增加怎么办?WordPress缓存导致文章阅读数点赞数不更新...
  9. 页面之间传递参数得几种方法
  10. swift网络数据请求方法
  11. vue-cli初始化一个项目
  12. Python引用复制,参数传递,弱引用与垃圾回收
  13. iPhone屏幕尺寸、分辨率及适配
  14. 股市中的马太效应带给我们什么股票道理?
  15. dither技术的原理及应用
  16. 逻辑思维能力选择题30道
  17. 零基础学习Hadoop
  18. 数据可视化方法:数据图表展示
  19. linux ps2键盘驱动,Linux下USB模拟ps2鼠标驱动
  20. VC++QQ群,刚刚建立,欢迎加入,共同进步

热门文章

  1. python 经验正交函数(EOF)与旋转经验正交函数(REOF)
  2. Wireshark, Sniffer and Omnipeek 三款网络分析工具的比较
  3. 星历表ASC2EPH修改且汉化
  4. html网页底部弹窗,【HTML】底部弹窗插件代码
  5. [错误解决] Libreoffice转换不成功,直接不做任何操作
  6. python GUI demo(tkinter)
  7. macos使用ffpmeg批量mp4转mp3
  8. opencv图像处理之在手机上实现背景虚化
  9. Linux学习之路(1):初学Linux
  10. Label mx条码标签打印软件生成跳号的流水号