В настоящее время я использую файлы .resx для управления ресурсами на стороне сервера для .NET.
приложение, с которым я работаю, также позволяет разработчикам встраивать JavaScript в различные обработчики событий для проверки на стороне клиента и т. д. Как мне лучше всего локализовать сообщения и строки JavaScript?
В идеале я хотел бы хранить строки в файлах .resx, чтобы сохранить их вместе с остальными локализованными ресурсами.
Я открыт для предложений.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я бы использовал обозначение объекта / массива:
var phrases = {};
phrases['fatalError'] ='On no!';
Затем вы можете просто поменять местами файл JS или использовать вызов Ajax для переопределения списка фраз.
Расширение ответа diodeus.myopenid.com: пусть ваш код выпишет файл, содержащий массив JS со всеми необходимыми строками, а затем загрузите соответствующий файл / скрипт перед другим кодом JS.
Базовый объект JavaScript представляет собой ассоциативный массив, поэтому его можно легко использовать для хранения пар ключ / значение. Итак, используя JSON, вы можете создать объект для каждой строки, которая будет локализована следующим образом:
var localizedStrings = {
confirmMessage:{
'en/US':'Are you sure?',
'fr/FR':'Est-ce que vous êtes certain?',
...
},
...
}
Тогда вы можете получить локальную версию каждой строки следующим образом:
var locale='en/US';
var confirm=localizedStrings['confirmMessage'][locale];
Проблема с этим подходом в том, что вы загружаете все строки для всех языков. Однако вполне вероятно, что сервер будет знать либо через подсказки браузера, либо через настройки пользователя, какой язык нужен. Было бы лучше отправить файл на одном языке.
Поскольку я пишу объектно-ориентированный JS, каждый объект определяет английские строки по умолчанию, которые он использует в прототипе. Если мне нужен другой язык, я просто загружаю другой файл и изменяю строки прототипа. Таким образом, мне не нужно загружать сразу все строки для одного языка (они загружаются динамически). Недостатком является то, что вам может потребоваться повторить некоторые строки для разных объектов.
просто инвертирование порядка 'localizedStrings [culture] [key]' решает проблему загрузки нескольких культур ... это также может позволить использовать 'нейтральную' культуру в качестве прототипа для всех остальных (обеспечивая плавный откат, когда культура не не определяю конкретный ключ)
Я использую Т4 для генерации в точности кода, предложенного выше.
Вдохновленный SproutCore Вы можете установить свойства струны:
'Hello'.fr = 'Bonjour';
'Hello'.es = 'Hola';
а затем просто укажите правильную локализацию в зависимости от вашей локали:
var locale = 'en';
alert( message[locale] );
С помощью вспомогательной сборки (вместо файла resx) вы можете перечислить все строки на сервере, на котором вы знаете язык, таким образом создавая объект Javascript только со строками для правильного языка.
У нас работает что-то вроде этого (код VB.NET):
Dim rm As New ResourceManager([resource name], [your assembly])
Dim rs As ResourceSet =
rm.GetResourceSet(Thread.CurrentThread.CurrentCulture, True, True)
For Each kvp As DictionaryEntry In rs
[Write out kvp.Key and kvp.Value]
Next
Однако, к сожалению, мы еще не нашли способ сделать это для файлов .resx.
JSGettext отлично справляется с этой задачей - динамическая загрузка файлов GNU Gettext .po с использованием практически любого языка в серверной части. Google для "Динамической локализации Javascript с помощью Gettext и PHP", чтобы найти пошаговое руководство для JSGettext с PHP (я бы опубликовал ссылку, но этот глупый сайт не позволяет мне, вздох ...)
Редактировать: это должен быть ссылкой
MSDN способ сделать это, в основном:
You create a separate script file for each supported language and culture. In each script file, you include an object in JSON format that contains the localized resources values for that language and culture.
Я не могу сказать вам лучшего решения вашего вопроса, но ИМХО, это худший способ сделать это. По крайней мере, теперь вы знаете, как этого НЕ делать.
Просто потому что это РС, это плохо? Неудачная попытка показаться смешной! Это наиболее распространенный способ. Если это наихудший способ, нужно хотя бы объяснить почему!
@Juan Попытка быть смешным? Я предполагаю, что способ локализации своих строк в js - это в точности способ MSDN, также известный как «CPP - копирование и вставка программирования», и я не мог терпеть, когда кто-то его критиковал. «Самый распространенный способ» - это нарушить принцип DRY в соответствии с вашим утверждением, но вы, вероятно, не воспринимаете это как запах кода.
Как это нарушение DRY? Даже если ваш аргумент верен, ответ все равно слабый, поскольку он ничего не объясняет.
ИМХО, это одно из самых СУХИХ решений. Строки находятся только в одном файле, в одном месте, и на этот файл ссылается везде, где требуется отображать строки.
Пользовательский интерфейс jQuery также использует «способ MSDN», над которым вы насмехаетесь. Мне нравится, что все, что делает Microsoft, для некоторых людей «неправильно».
Проблема в том, что они защищают повторение логики (дословно) для каждого отдельного языка, который вы хотите поддерживать. Они также предлагают смешивать ваши строковые ресурсы с вашим кодом. Тоже не лучшая идея. Упомянутый выше Jquery.i18n этого не делает. Они отделяют данные (строковые ресурсы) от кода и аналогичным образом хранят 1 копию вашей логики, опять же отдельно от данных.
Я считаю, что они рекомендуют вам включить сценарий, отдельный файл для каждого языка .. с одним экспортом (который представляет собой объект с парами ключ / значение) для конкретного языка .. и включить его отдельно от вашего кода / логики .. именно это и использует jQuery.i18n.
Ребята, ребята, расслабьтесь, способ, предложенный в MSDN, не так хорош, потому что, если вы действительно используете файл .resx, вам нужно будет повторить его в JS. пока способ DRYies: stackoverflow.com/a/14782225/1930376
Я сделал следующее, чтобы локализовать JavaScript для мобильного приложения с HTML5:
1. Создал набор файлов ресурсов для каждого языка, назвав их как «en.js» для английского языка. Каждый из них содержал разные строки приложения, как показано ниже:
var localString = {
appName: "your app name",
message1: "blah blah"
};
2. С помощью Lazyload загрузил соответствующий файл ресурсов в зависимости от языка локали приложения: https://github.com/rgrove/lazyload
3. передайте языковой код через строку запроса (поскольку я запускаю html-файл с Android с помощью PhoneGap)
4.Затем я написал следующий код для динамической загрузки нужного файла ресурсов:
var lang = getQueryString("language");
localization(lang);
function localization(languageCode) {
try {
var defaultLang = "en";
var resourcesFolder = "values/";
if (!languageCode || languageCode.length == 0)
languageCode = defaultLang;
// var LOCALIZATION = null;
LazyLoad.js(resourcesFolder + languageCode + ".js", function() {
if ( typeof LOCALIZATION == 'undefined') {
LazyLoad.js(resourcesFolder + defaultLang + ".js", function() {
for(var propertyName in LOCALIZATION) {
$("#" + propertyName).html(LOCALIZATION[propertyName]);
}
});
} else {
for(var propertyName in LOCALIZATION) {
$("#" + propertyName).html(LOCALIZATION[propertyName]);
}
}
});
} catch (e) {
errorEvent(e);
}
}
function getQueryString(name)
{
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regexS = "[\?&]" + name + "=([^]*)";
var regex = new RegExp(regexS);
var results = regex.exec(window.location.href);
if (results == null)
return "";
else
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
5. Из файла html я ссылаюсь на строки следующим образом:
span id = "appName"
Можете ли вы объяснить, что именно делает этот блок кода: if (typeof LOCALIZATION == 'undefined') {LazyLoad.js (resourcesFolder + defaultLang + ".js", function () {for (var propertyName in LOCALIZATION) {$ ( "#" + propertyName) .html (LOCALIZATION [propertyName]);}}); } else {для (var propertyName в LOCALIZATION) {$ ("#" + propertyName) .html (LOCALIZATION [propertyName]); }}
Есть библиотека для локализации приложений JavaScript: https://github.com/wikimedia/jquery.i18n
Он может выполнять замену параметров, поддерживает пол (умная обработка), число (умная обработка множественного числа, включая языки, которые имеют более одной формы множественного числа) и пользовательские правила грамматики, которые необходимы некоторым языкам.
Строки хранятся в файлах JSON.
Единственное требование - jQuery.
После того, как я много погуглил и не был удовлетворен большинством представленных решений, я только что нашел удивительное / универсальное решение, которое использует Шаблоны T4. Полный текст сообщения Йохен ван Вайлик вы можете прочитать здесь:
Использование T4 для локализации ресурсов JavaScript на основе файлов .resx
Основные преимущества:
Недостатки:
The shortcomings of this solution are of course that the size of the .js file might become quite large. However, since it's cached by the browser, we don't consider this a problem for our application. However - this caching can also result in the browser not finding the resource called from code.
Как это работает?
По сути, он определил шаблон T4, который указывает на ваши файлы .resx. С помощью некоторого кода C# он просматривает каждую строку ресурса и добавляет ее в свойства чистого значения ключа JavaScript, которые затем выводятся в одном файле JavaScript с именем Resources.js (вы можете настроить имена, если хотите).
Шаблон Т4 [измените соответственно, чтобы указать на расположение ваших файлов .resx]
<#@ template language = "C#" debug = "false" hostspecific = "true"#>
<#@ assembly name = "System.Windows.Forms" #>
<#@ import namespace = "System.Resources" #>
<#@ import namespace = "System.Collections" #>
<#@ import namespace = "System.IO" #>
<#@ output extension = ".js"#>
<#
var path = Path.GetDirectoryName(Host.TemplateFile) + "/../App_GlobalResources/";
var resourceNames = new string[1]
{
"Common"
};
#>
/**
* Resources
* ---------
* This file is auto-generated by a tool
* 2012 Jochen van Wylick
**/
var Resources = {
<# foreach (var name in resourceNames) { #>
<#=name #>: {},
<# } #>
};
<# foreach (var name in resourceNames) {
var nlFile = Host.ResolvePath(path + name + ".nl.resx" );
var enFile = Host.ResolvePath(path + name + ".resx" );
ResXResourceSet nlResxSet = new ResXResourceSet(nlFile);
ResXResourceSet enResxSet = new ResXResourceSet(enFile);
#>
<# foreach (DictionaryEntry item in nlResxSet) { #>
Resources.<#=name#>.<#=item.Key.ToString()#> = {
'nl-NL': '<#= ("" + item.Value).Replace("\r\n", string.Empty).Replace("'","\'")#>',
'en-GB': '<#= ("" + enResxSet.GetString(item.Key.ToString())).Replace("\r\n", string.Empty).Replace("'","\'")#>'
};
<# } #>
<# } #>
На стороне формы / просмотра
Чтобы получить правильный перевод, добавьте это в свой мастер, если вы используете WebForms:
<script type = "text/javascript">
var locale = '<%= System.Threading.Thread.CurrentThread.CurrentCulture.Name %>';
</script>
<script type = "text/javascript" src = "/Scripts/Resources.js"></script>
Если вы используете ASP.NET MVC (как и я), вы можете сделать это:
<script type = "text/javascript">
// Setting Locale that will be used by JavaScript translations
var locale = $("meta[name='accept-language']").attr("content");
</script>
<script type = "text/javascript" src = "/Scripts/Resources.js"></script>
Помощник MetaAcceptLanguage, который я получил из этой замечательной публикации Скотта Хансельмана:
Глобализация, интернационализация и локализация в ASP.NET MVC 3, JavaScript и jQuery - Часть 1
public static IHtmlString MetaAcceptLanguage<T>(this HtmlHelper<T> html)
{
var acceptLanguage =
HttpUtility.HtmlAttributeEncode(
Thread.CurrentThread.CurrentUICulture.ToString());
return new HtmlString(
String.Format("<meta name=\"{0}\" content=\"{1}\">", "accept-language",
acceptLanguage));
}
Используй это
var msg = Resources.Common.Greeting[locale];
alert(msg);
Мы используем MVC и просто создали действие контроллера для возврата локализованной строки. Мы поддерживаем культуру пользователя в сеансе и устанавливаем культуру потока перед любым вызовом для получения языковой строки, AJAX или иным образом. Это означает, что мы всегда возвращаем локализованную строку.
Я признаю, что это не самый эффективный метод, но получение локализованной строки в javascript требуется редко, поскольку большая часть локализации выполняется в наших частичных представлениях.
Global.asax.cs
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
{
// Set the current thread's culture
var culture = (CultureInfo)Session["CultureInfo"];
if (culture != null)
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
}
}
}
Действие контроллера
public string GetString(string key)
{
return Language.ResourceManager.GetString(key);
}
Javascript
/*
Retrieve a localized language string given a lookup key.
Example use:
var str = language.getString('MyString');
*/
var language = new function () {
this.getString = function (key) {
var retVal = '';
$.ajax({
url: rootUrl + 'Language/GetString?key=' + key,
async: false,
success: function (results) {
retVal = results;
}
});
return retVal;
}
};
Что ж, я думаю, вы можете это рассмотреть. Англо-испанский пример:
Напишите 2 сценария Js, например:
en-GB.js
lang = {
date_message: 'The start date is incorrect',
...
};
es-ES.js
lang = {
date_message: 'Fecha de inicio incorrecta',
...
};
Сторона сервера - код позади:
Protected Overrides Sub InitializeCulture()
Dim sLang As String
sLang = "es-ES"
Me.Culture = sLang
Me.UICulture = sLang
Page.ClientScript.RegisterClientScriptInclude(sLang & ".js", "../Scripts/" & sLang & ".js")
MyBase.InitializeCulture()
End Sub
Вы знаете, где sLang может быть "en-GB", в зависимости от выбора текущего пользователя ...
Javascript вызывает:
alert (lang.date_message);
И это работает, я думаю, очень просто.
Обратите внимание, что это неверно. phrases будет массивом, но вы просто устанавливаете одно из его свойств для строки (например, phrases.fatalError = 'Oh no'; эквивалентно вашей второй строке). Вместо этого используйте объектные литералы для чистоты.