1

我正在用 durandal 开发一个 SPA。我依赖于 ASP.NET Web API 和 ASP.NET MVC。对于本地化需求,我已经按照 durandal v2 文档中的说明设置了 i18n 模块,即使我一直想在服务器端管理大部分全球化,这也能正常工作:我的意思是在 ASP.Net mvc (*.cshtml) 视图。

但是,由于视图是按需加载的,我怎样才能以某种方式自定义视图(html/cshtml)异步加载调用以请求视图的特定语言(例如提供 url 参数以指定视图语言) .

我已经开始寻找加载视图(html/cshtml)的位置,但到目前为止还没有找到位置)。你能给我任何解释/技巧吗?(是由 durandal、ko、require..?)

谢谢!

编辑:我开始探索 durandal 代码。我刚刚明白它正在使用 require.js,即使是通过 text.js 插件加载的视图/html文件。所以现在我的目标是挂钩该过程,以便将请求的页面文化添加到调用中(url 请求参数或 http 标头)。关于如何以最简单的方式实现这一目标的任何想法?(通过编写等效的 text.js 或者只是通过在 viewEngine.js 中破解 convertViewIdToRequirePath ?)

4

3 回答 3

1

简单的解决方案

因为我真的不喜欢它,所以有可能从 url 参数检测 i18next 的语言。在 i18next 选项中,您需要添加以下条目:

detectLngQS: 'lang'

多亏了它您可以使用?lang=en-USurl 参数设置页面语言。

更好的(恕我直言)解决方案

在我的应用程序中,我曾经创建处理 Durandal 本地化功能的本地化服务模块。在App/services我确实使用以下代码创建localization.ts文件(对不起,TypeScript,但我真的更喜欢它而不是 JS):

/// <reference path='../../Scripts/typings/durandal/durandal.d.ts' />
/// <reference path='../../Scripts/typings/i18next/i18next.d.ts' />

//#region Imports
import app = require('durandal/app');

//#endregion
 //#region Public Members

export var Languages: KnockoutObservableArray<ILanguage> = ko.observableArray();

export var Init = (callback: (t: (key: string, options?: any) => string) => void) =>
{
    var pl: ILanguage =
        {
            Id: ko.observable('pl'),
            Name: ko.observable('Polski'),
            Icon: ko.observable('Content/images/pl_flag.png'),
            IsActive: ko.observable(true)
        };

    var en: ILanguage =
        {
            Id: ko.observable('en'),
            Name: ko.observable('English'),
            Icon: ko.observable('Content/images/en_flag.png'),
            IsActive: ko.observable(false)
        };

    var de: ILanguage =
        {
            Id: ko.observable('de'),
            Name: ko.observable('German'),
            Icon: ko.observable('Content/images/de_flag.png'),
            IsActive: ko.observable(false)
        };

    Languages.push(pl);
    Languages.push(en);
    Languages.push(de);

    var option: I18nextOptions =
        {
            preload: ['pl', 'en', 'de'],
            lng: 'pl',
            fallbackLng: 'pl',
            ns: 'app',
            debug: true
        };

    $.i18n.init(option, callback);
    app.trigger('Localization:Init');
};

export var T = (key: string) =>
{
    return $.i18n.t(key);
};

export var Lng = () =>
{
    return $.i18n.lng();
};

export var SetLng = (lng: ILanguage) =>
{
    for (var l in Languages())
    {
        var value = Languages()[l];
        if (value.Id != lng.Id)
        {
            value.IsActive(false);
        }
        else
        {
            value.IsActive(true);
        }
    }
    $.i18n.setLng(lng.Id());
    $('*').i18n();
    app.trigger('Localization:SetLng');
};


//#endregion


//#region Local Interfaces

export interface ILanguage
{
    Id: KnockoutObservable<string>;
    Name: KnockoutObservable<string>;
    Icon: KnockoutObservable<string>;
    IsActive: KnockoutObservable<boolean>;
}

//#endregion 

main.js在回调的文件中调用初始化函数app.start()

app.start().then(function () {
        toastr.options.positionClass = 'toast-bottom-right';
        toastr.options.backgroundpositionClass = 'toast-bottom-right';
        viewLocator.useConvention();
        localization.Init(function () {
            binder.binding = function (obj, view) {
                $(view).i18n();
            };
            app.setRoot('viewmodels/shell', 'entrance');
        });
    });

值得注意的是,Init 函数是一些复杂的本地化初始化(例如从数据库获取本地化选项)的好地方。

对你来说最重要的是SetLng。根据您的架构和想法,它可以在许多地方调用,但由于它在模块中,因此可以在任何其他模块中调用(仅由 require.js 引用),所以它很棒。我通常在引导导航栏中创建本地化按钮,这让我可以随时更改语言。

编辑

据我所知,您仍然可以将 i18next 用于 MVC 视图 - 只需设置 i18next 绑定。如果您想根据身份验证设置语言,最好的选择可能是使用 18next cookie。在您的登录 MVC 操作中,您需要添加类似于以下内容的内容:

var languageCode = GetLanguageCode(username)
var userCookie = new HttpCookie("i18next", languageCode );
userCookie.Expires.AddDays(365);
HttpContext.Response.Cookies.Add(userCookie);

只需确保 i18next 选项中的默认设置为英语 - 将为匿名用户选择此语言。登录后语言将根据您的逻辑设置,选择将保存在 cookie 中

编辑 2

它应该只是 text.js 文件中的微小变化 - 可能您应该在 273 之前只添加两三行。 text.js 行(和一个辅助函数)

首先,我们需要帮助函数来获取给定 cookie 的值( document.cookie 返回所有 cookie 名称和连接的值,因此我们需要以某种方式解析它)

function getCookie(cname)
{
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) 
  {
  var c = ca[i].trim();
  if (c.indexOf(name)==0) return c.substring(name.length,c.length);
  }
return "";
}

现在你需要修改你的 text.js 文件来设置 i18next cookie。273.之前的代码行:

var cookie = getCookie('i18next');
if ( cookie != '') xhr.setRequestHeader("Cookie", "i18next=" + cookie);
于 2014-01-14T11:53:25.223 回答
0

好的,这就是我的做法!(目前我只是为请求的文化添加了一个 url 参数。)

因此,在客户端,在 main.js 中(在 app.start 之前),我重写了 viewEngine 的“convertViewIdToRequirePath”方法来添加参数:

//overriding the default viewEngine viewId conversion rule to add language parameter
viewEngine.convertViewIdToRequirePath = function (viewId) {
    return this.viewPlugin + '!' + viewId + this.viewExtension + '?lang=' + i18n.lng();
};

然后在服务器端,我添加了一个 Application_AcquireRequestState 方法来管理参数检测,然后设置线程的当前文化。

PS:感谢@Krzysztof 的帮助!

于 2014-01-23T15:59:39.840 回答
0

我无法导入 i18next 的一个原因是 i18next.d.ts 文件没有

declare module "i18next" {
    export = i18next;
}

在最后。它只有

 declare var i18next: I18nextStatic;

因此,当我尝试使用它导入我的 TypeScript 文件时,import i18next = require('i18next');它会出错:

无法加载外部模块模块不能别名为非模块类型

我不知道为什么肯定类型的项目以这种方式组成 d.ts 文件。而且我不知道 declare module 是否是一个好的解决方案。

有人请帮忙,我在这里也发布了一个问题:

尝试在 TypeScript 中导入 i18next 时无法加载外部模块

于 2014-08-05T08:17:34.427 回答