Xpages:我们如何实现客户端JS代码的本地化?

时间:2016-04-19 11:57:22

标签: javascript dojo internationalization xpages

我目前正在开发一个多语言的Xpages驱动的应用程序,使用标准方法通过内部.property资源和resourceBundles本地化静态和动态字符串。在应用程序内,用户可以选择自己喜欢的语言,这个决定当前存储在用户配置文件中;我还计划将这些决定存储为浏览器cookie。如果没有用户定义的首选项,则浏览器的默认语言将驱动应用程序中使用的语言环境。这适用于所有服务器端元素。

现在我必须添加一些客户端脚本,我还需要使用一些本地化字符串。我不得不承认我没有线索,这是最好的办法。

主要问题是:

  1. 我可以以某种方式使用现有的文件资源/资源包吗?
  2. 如果是这样的话:如何从我的脚本代码中引用这些资源?
  3. 如果没有:组织和引用我自己的文件资源的最佳方法是什么?
  4. 最后:如果我必须自己完成所有操作:如何找出用户首选的区域设置?如果设置了cookie,那么这很容易(见上文),但如果我需要引用浏览器的偏好和/或用户是否禁止使用cookie,该怎么办?
  5. 编辑:我考虑过dojo i18n way,但我不知道如何实现这样的自定义插件。

2 个答案:

答案 0 :(得分:3)

另一种方法是直接在浏览器中读取资源包,并在需要它的JavaScript中引用它。所以回答你的主要问题:

  1. 是的,但是如果直接从NSF加载它,它还不是真正可用的。我建议创建一个XPage来输出它作为JSON对象:

    <?xml version="1.0" encoding="UTF-8"?>
    <xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">
    
    <xp:this.resources>
        <xp:bundle
        src="/labels_en.properties"
        var="translations">
        </xp:bundle>
    </xp:this.resources>
    
    <xp:this.afterRenderResponse><![CDATA[#{javascript:
    try {
    
     var externalContext = facesContext.getExternalContext();
     var writer = facesContext.getResponseWriter();
     var response = externalContext.getResponse();
    
     response.setContentType("application/json"); 
    
     var jsonOutput = {};
     var keys = translations.keySet();
    
     for (var key in keys) {
       jsonOutput[key] = translations[key];
     }
    
     writer.write(  "var translations = " + toJson(jsonOutput)  ) ;
     writer.endDocument();
    
    } catch (e) {
        print(e);
    }
    }]]>
        </xp:this.afterRenderResponse>
    
    </xp:view>
    
  2. 您需要在页面中包含来自1.的XPage作为客户端JavaScript库。我在XPage中创建了一个名为translations的全局JavaScript变量,因此您可以将其称为translations.key,其中key引用属性文件中的变量。

  3. 无需回答......
  4. 在回答1的XPage中,您可以根据用户的语言环境(浏览器语言设置)加载相应的资源包:

    <xp:this.resources>
        <xp:bundle var="translations">
        <xp:this.src><![CDATA[#{javascript:
    var language = "en";        //default language
    
    switch (context.getLocaleString() ) {
    case "nl":
        language = "nl";
        break;
    }
    
    return "/labels_" + language + ".properties";}]]></xp:this.src>
        </xp:bundle>
    </xp:this.resources>
    

答案 1 :(得分:2)

我使用以下方法:我的所有客户端JavaScript(CSJS)库都作为文件资源存储在数据库设计中(Resources&gt; Files)。在库的代码中,我使用类似EL的符号来标记可翻译的部分,例如:

var text="#{TRLT.TextToTranslate}";

每当我想在XPage上使用这些库时,我都不会在XPage的资源中直接引用它们,而是添加对&#34; Helper-XPage&#34;的引用。 (js.xsp)改为:

<xp:this.resources>
    <xp:headTag tagName="script">
        <xp:this.attributes>
            <xp:parameter name="src" value="js.xsp?lng=#{sessionScope.language}&amp;v=#{applicationScope.versionApp}"/>
            <xp:parameter name="type" value="text/javascript"/>
        </xp:this.attributes>
    </xp:headTag>
</xp:this.resources>

js.xsp的行为类似于JavaScript资源:我在XPage上设置了rendered="false",并在 beforeRenderResponse 事件中手动创建了Content-Type="text/javascript"的响应。调用XPage时,会根据我通常用于服务器端翻译的字典读取所有CSJS库,连接它们并填写所有翻译

以下是js.xsp的beforeRenderResponse事件的代码:

importPackage(java.io);
importPackage(java.lang);
importPackage(java.util.regex);
importPackage(javax.servlet.http);
importPackage(org.apache.commons.io); // AFAIK this package is not installed by default (but generally very helpful)

var i,arr,lng,js,libs,c,s,m,bfr,dct,response,ec,is,os;

//---------------------------- initialize main variables
ec=facesContext.getExternalContext(); // the external context

lng=(param.lng || "en");  // the language can be provided as url parameter, otherwise use a default

dct=(applicationScope["TRLT_"+lng] || {}); // in my case, the dictionaries (HashMaps) containing the translations are stored in the applicationScope, but of course they can be loaded from anywhere (resource bundles etc.)

libs=(param.libs ? fromJson(param.libs) : ["mylib1.js","mylib2.js"]) // the library names can be provided as url parameter, otherwise use a default

//---------------------------- concatenate all libraries
js=new StringBuilder();
for (i=0;i<libs.length;i++) {
    if (s=IOUtils.toString(ec.getResourceAsStream(libs[i]),"UTF-8")) js.append("\n\n"+s);
}
js=js.toString();

//---------------------------- search for and replace translateable parts
m=Pattern.compile("[#$]\\{TRLT\\.([^}]+)\\}").matcher(js);
bfr=new StringBuffer();
c=0;
while (m.find() && c<1e6) {
    c++;
    s=m.group(1);
    m.appendReplacement(bfr,dct[s] || s || "");
}
m.appendTail(bfr);
js=bfr.toString();

//---------------------------- create the response and finalize
response=ec.getResponse();
response.setHeader("Cache-Control","max-age="+(60*60*24*365).toFixed(0)); // its important to set the expiration "a bit" into the future to prevent the browser from reloading the js.xsp everytime you reference it on another XPage; in order to force the browser to update the XPage, use versioning (see url parameter "v" in the headTag definition above)
response.setDateHeader("Expires",new Date().getTime()+(1000*60*60*24*365));
response.setHeader("Content-Type","text/javascript; name=\"libs.js\"");
response.setHeader("Content-Disposition","inline; filename=\"libs.js\"");

is=new ByteArrayInputStream(js.getBytes("UTF-8"));
os=response.getOutputStream();
IOUtils.copy(is,os);
is.close();
os.close();
facesContext.responseComplete();
return;

PS:我必须修改此处提供的代码,因为原始版本对我的一般框架有一些依赖性以及一些额外的缓存和错误处理。因此,我不能保证没有拼写错误,但原则上它应该有效。