Javascript 具有多语言支持的Durandal(淘汰)应用程序

Javascript 具有多语言支持的Durandal(淘汰)应用程序,javascript,knockout.js,internationalization,requirejs,durandal,Javascript,Knockout.js,Internationalization,Requirejs,Durandal,我正在为我正在开发的应用程序建立多语言支持。在做了一些研究并阅读了SO()之后,我试图以一种“框架友好”的方式来集成它。 我目前所做的工作如下: 已创建。资源模块的格式如下: resources.en-US.js define(function () { return { helloWorlLabelText: "Hello world!" } }); 在app.start上,我使用requirejs获取资源模块,并将所有数据分配给app.resources。在每个模

我正在为我正在开发的应用程序建立多语言支持。在做了一些研究并阅读了SO()之后,我试图以一种“框架友好”的方式来集成它。 我目前所做的工作如下:

已创建。资源模块的格式如下:

resources.en-US.js

define(function () {
   return {
       helloWorlLabelText: "Hello world!"
   }
});
在app.start上,我使用requirejs获取资源模块,并将所有数据分配给app.resources。在每个模块内部,特定资源被分配给可观察对象,并与标签和其他文本相关内容的文本绑定绑定。像这样:

define(function (require) {
   var app = require('durandal/app'),
       router = require('durandal/plugins/router')
   };
   return{
       helloWorldLabelText: ko.observable(app.resources.helloWorldLabelText),

       canDeactivate: function () { 
      }
   }
});

On the view:

<label for="hello-world" data-bind="text: helloWorldLabelText"></label>
定义(功能(需要){
var app=require('durandal/app'),
路由器=require('durandal/plugins/router')
};
返回{
helloWorldLabelText:ko.observable(app.resources.helloWorldLabelText),
canDeactivate:函数(){
}
}
});
关于这一观点:
只需将新模块分配给app.resources即可交换资源

现在的问题是,当语言被更改并且一些视图已经呈现时,以前语言的值仍然存在。所以我最终在activate方法中重新分配了观测值。还尝试将app.resources包装成observable,但也没有成功


我不认为我以最干净的方式结束,也许其他人也有其他方式可以分享。谢谢。

您可以考虑使用一个代码> i18n模块,返回一个具有所有必需的可观察的单体。此外,还有一个init函数,它接受一个i18n对象来初始化/更新它们

define(function (require) {
   var app = require('durandal/app'),
       i18n = require('i18n'),
       router = require('durandal/plugins/router')
   };
   return{

       canDeactivate: function () { 
      }
   }
});

On the view:

<label for="hello-world" data-bind="text: i18n.helloWorldLabelText"></label>
定义(功能(需要){
var app=require('durandal/app'),
i18n=需要('i18n'),
路由器=require('durandal/plugins/router')
};
返回{
canDeactivate:函数(){
}
}
});
关于这一观点:

关于这个主题,SO中的答案给了我很大的启发,因此我提出了自己的i18n模块+绑定的实现,用于Knockout/Durandal

另一个i18n模块的选择是,我更喜欢将翻译存储在数据库中(每个项目都需要输入),而不是文件中。使用该实现,您只需拥有一个后端,该后端必须以键值方式回复包含所有翻译的JSON对象

@雨露
singleton类的好提示对模块非常有帮助

对于那些仍然对最佳实践感到困惑的人,那些觉得缺少一些东西的人,或者那些只是好奇如何更好地实现Durandal、Knockout、RequireJS和客户端web应用程序的人,下面是一个关于可能发生的事情的更有用的概述

这当然是不完整的,但希望这可以扩大一些想法一点

第一,2014年11月更新 我看到,即使一年后,这个答案也会定期被提高投票率。当我进一步开发我们的特定解决方案(在Durandal/AMD/Knockout旁边集成I18)时,我犹豫了几次是否要更新它。然而,我们最终放弃了依赖项目,因为内部困难和对堆栈和堆栈其他部分的“担忧”。因此,这个小小的集成工作也被取消了

话虽如此,我还是希望能够很好地将一般适用的评论与下面的具体评论区分开来,因此我认为它们会继续提供有用的(甚至是非常需要的)观点

如果你还想玩Durandal、Knockout、AMD和任意本地化库(顺便说一句,还有一些新玩家需要评估),我在最后补充了几点我以后的经验

关于单例模式 这里单例模式的一个问题是很难配置每个视图;事实上,除了语言环境(复数形式、上下文、变量、性别)外,翻译还有其他参数,这些参数本身可能特定于某些上下文(例如视图/视图模型)

顺便说一句,重要的是不要自己做这件事,而是依赖本地化库/框架(它可能会变得非常复杂)。有很多

你仍然可以使用单亲家庭,但无论哪种方式,你都只完成了一半

在…上 in探索的一个解决方案是创建一个KO绑定处理程序。可以想象这个处理程序从视图中获取这些参数,然后使用任何本地化库作为后端。通常,您需要在viewmodel(或其他地方)中以编程方式更改这些参数,这意味着您仍然需要公开JSAPI

如果仍然要公开这样的API,那么可以使用它来填充视图模型并完全跳过绑定处理程序。但是,对于那些可以直接从视图中配置的字符串,它们仍然是一个很好的快捷方式。提供这两种方法是一件好事,但如果没有JSAPI,您可能无法做到这一点

当前Javascript API,文档重新加载 大多数本地化库和框架都很老套,其中许多都希望在用户更改区域设置时重新加载整个页面,有时甚至在翻译参数因各种原因更改时也是如此。不要这样做,它违背了客户端web应用程序所代表的一切。(如今,SPA似乎是一个很酷的术语。)

主要原因是,否则您将需要跟踪每次区域设置更改时需要重新传输的每个DOM元素,以及每次参数更改时需要重新传输的元素。手动执行此操作非常繁琐

幸运的是,这正是像knockout这样的数据绑定非常容易做到的。事实上,我刚才提到的问题应该提醒您KO计算的可观测值和KO
data bind
属性试图解决的问题

关于RequireJS 该插件既使用单例模式,又希望您重新加载文档。不适合与Durandal一起使用

define(function (require) {
    var ko = require('knockout');
    var i18next = require('i18next');
    var locale = require('locale');

    return function (key, opts) {
        return ko.computed(function () {
            locale();
            var unwrapped = {};
            if (opts) {
                for (var optName in opts) {
                    if (opts.hasOwnProperty(optName)) {
                        var opt = opts[optName];
                        unwrapped[optName] = ko.isObservable(opt) ? opt() : opt;
                    }
                }
            }
            return i18next.t(key, unwrapped);
        });
    }
});
define(function (require) { return require('knockout').observable('en'); });
var ko = require('knockout');
var i18next = require('i18next');
var locale = require('locale');

i18next.init({
    lng: locale(),
    getAsync: false,
    resGetPath: 'app/locale/__ns__-__lng__.json',
});

locale.subscribe(function (value) {
    i18next.setLng(value, function () {});
});
var _ = require('translate');

var n_foo = ko.observable(42);
var greeting = _('greeting');
var foo = _('foo', { count: n_foo });
var locale = require('locale');

locale('en_US');
n_foo(1);
...
define(function (require) {
    'use strict';

    var ko = require('knockout');
    var i18next = require('i18next-actual');
    var locale = require('locale');
    var namespaces = require('tran-namespaces');
    var Mutex = require('komutex');

    var mutex = new Mutex();

    mutex.lock(function (unlock) {
        i18next.init({
            lng: locale(),
            getAsync: true,
            fallbackLng: 'en',
            resGetPath: 'app/locale/__lng__/__ns__.json',
            ns: {
                namespaces: namespaces,
                defaultNs: namespaces && namespaces[0],
            },
        }, unlock);
    });

    locale.subscribe(function (value) {
        mutex.lock(function (unlock) {
            i18next.setLng(value, unlock);
        });
    });

    var origFn = i18next.t;
    i18next.t = i18next.translate = function (key, opts) {
        return ko.computed(function () {
            return mutex.tryLockAuto(function () {
                locale();
                return origFn(key, opts && ko.toJS(opts));
            });
        });
    };

    return i18next;
});
requirejs.config({
    paths: {
        'i18next-actual': 'path/to/real/i18next.amd-x.y.z',
        'i18next': 'path/to/wrapper/above',
    }
});
<p>
  {{ 'home.label' | i18n }}
</p>
<h2 title="{{ 'home.title' | i18n }}">
  {{ 'home.label' | i18n }}
</h2>
<h2>
  {{ 'home.welcome' | i18n:name }}
  <!-- Passing the 'name' observable, will be embedded in text string -->
</h2>
{
  "home": {
    "label": "Home Page",
    "title": "Type your name…"
    "welcome": "Hello {{0}}!",
  }
}
{
  "home": {
    "label": "家",
    "title": "输入你的名字……",
    "welcome": "{{0}}您好!",
  }
}