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

Posted

技术标签:

【中文标题】Xpages:我们如何实现客户端 JS 代码的本地化?【英文标题】:Xpages: how can we implement localization for client side JS code? 【发布时间】:2016-04-19 11:57:21 【问题描述】:

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

现在我必须添加一些客户端脚本,并且还需要使用一些本地化字符串。我不得不承认,我不知道这是最好的方法。

主要问题是:

    我能否以某种方式使用现有的文件资源/资源包? 如果是这样:如何从我的脚本代码中引用这些资源? 如果不是:组织和引用我自己的文件资源的最佳方式是什么? 最后:如果我必须自己做这一切:我如何找出用户首选的语言环境?如果设置了 cookie(见上文),这很容易,但如果我需要参考浏览器的偏好和/或如果用户不允许使用 cookie,该怎么办?

编辑:我考虑过使用dojo i18n way,但我不知道如何实现这样的自定义插件。

【问题讨论】:

我认为最好的方式是Java方式。在 CSJS 中,您还可以插入 ssjs 以从支持的 bean 中获取标签,这可能是我的博客可以提供的帮助。 elstarit.nl/2016/03/08/… 感谢@FrankvanderLinden 的评论。似乎是维护这些资源的一种非常有用的方法,我将在接下来的几天内更彻底地阅读它 如果你需要帮助,请告诉我,你有我的博客 ;-) 我使用以下方法:在我的 CSJS 库中,我使用类似 EL 的符号来标记可翻译部分,例如 var text="#TRLT.TextToTranslate";。我没有直接引用 html 头中的库,而是引用了一个行为类似于 javascript 资源 (Content-Type=text/javascript) 的帮助程序 xpage (js.xsp)。此 XPage 加载所有 CSJS 库,将它们连接起来,并用我也用于服务器端翻译的字典中的字符串替换可翻译部分。我可以发布一些代码,但我想你的问题有更简单的解决方案...... 这听起来很有趣。如果你不介意的话,我很乐意看到一些例子 【参考方案1】:

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

    是的,但是如果您直接从 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>
    

    您需要将 1. 中的 XPage 作为客户端 JavaScript 库包含在您的页面上。我从 1 开始在 XPage 中创建了一个名为 translations 的全局 JavaScript 变量。因此您可以将其称为 translations.key,其中 key 引用属性文件中的一个变量。

    无需回答...

    在答案 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>
    

【讨论】:

好东西!关于内部属性格式的另一个问题:您是使用 "key": "value" 中的 JSON 格式,还是引擎本身创建和维护的 XSP 标准格式,如 "key=value"? 没关系,我刚刚发现:您正在将标准的“key=value”格式转换为 json 对象,对吧? 正确。您可以使用标准的key=value 格式。【参考方案2】:

我使用以下方法:我的所有客户端 JavaScript (CSJS) 库都存储为数据库设计中的文件资源(资源 > 文件)。在库的代码中,我使用 EL-like notation 来标记可翻译的部分,例如:

var text="#TRLT.TextToTranslate";

每当我想在 XPage 上使用这些库时,我不会直接在 XPage 的资源中引用它们,而是添加对“Helper-XPage”(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:我不得不修改这里提供的代码,因为原始版本对我的通用框架有一些依赖,以及一些额外的缓存和错误处理。因此我不能保证没有错别字,但原则上它应该可以工作。

【讨论】:

非常感谢;很难决定我应该接受两个相似答案中的哪一个;我从上面的@MarkLeusink 中选择了一个,因为它稍微简单一些 @LotharMueller:Mark Leusink 的解决方案实现起来要简单得多,也更适合您想要实现的目标。我不使用这种方法的原因是我的翻译词典相当大(>5Mb),因为它们还包含 HTML 模板。在 HTML 页面上加载这么多内容会导致用户第一次访问该页面时加载时间很长。如果您还忘记设置缓存控制标头,甚至每次用户加载页面时都会发生这种情况。当然我可以为 CSJS 翻译创建一个额外的字典,但这使得它更难维护

以上是关于Xpages:我们如何实现客户端 JS 代码的本地化?的主要内容,如果未能解决你的问题,请参考以下文章

Xpages 无法弹出 dojo 工具提示对话框

我的 IBM Domino xPages 应用程序中的 dojo.js 导致某些 JS 库无法工作

如何将 html 文件加载到 XPages 中的 Dojo 对话框中

XPages:如何计算分页视图面板行数

如何更改xpages中dojo货币文本框的特征?

10. 如何在XPages里实现典型的Notes权限控制?