diff --git a/src/extensibility/ExtensionManagerView.js b/src/extensibility/ExtensionManagerView.js index 59ffaa7c719..cf7396f7315 100644 --- a/src/extensibility/ExtensionManagerView.js +++ b/src/extensibility/ExtensionManagerView.js @@ -236,6 +236,22 @@ define(function (require, exports, module) { context.isCompatible = context.isCompatibleLatest = true; } + // Check if extension metadata contains localized content. + var lang = brackets.getLocale(), + shortLang = lang.split("-")[0]; + if (info.metadata["package-i18n"]) { + [shortLang, lang].forEach(function (locale) { + if (info.metadata["package-i18n"].hasOwnProperty(locale)) { + // only overlay specific properties with the localized values + ["title", "description", "homepage", "keywords"].forEach(function (prop) { + if (info.metadata["package-i18n"][locale].hasOwnProperty(prop)) { + info.metadata[prop] = info.metadata["package-i18n"][locale][prop]; + } + }); + } + }); + } + if (info.metadata.description !== undefined) { info.metadata.shortdescription = StringUtils.truncate(info.metadata.description, 200); } @@ -249,9 +265,6 @@ define(function (require, exports, module) { context.allowInstall = context.isCompatible && !context.isInstalled; if (Array.isArray(info.metadata.i18n) && info.metadata.i18n.length > 0) { - var lang = brackets.getLocale(), - shortLang = lang.split("-")[0]; - context.translated = true; context.translatedLangs = info.metadata.i18n.map(function (value) { diff --git a/src/extensions/samples/LocalizationExample/README.md b/src/extensions/samples/LocalizationExample/README.md index 941199704a5..001e35d0a3d 100644 --- a/src/extensions/samples/LocalizationExample/README.md +++ b/src/extensions/samples/LocalizationExample/README.md @@ -48,7 +48,7 @@ Move this plugin to the extensions\user\ folder to run the plugin. It will add a * main.js – loads the Strings module for the plugin and uses mustache to localize html content -* package.json - add the translation languages as in the example: `"i18n: ["en", "fr" ]` +* package.json - add the translation languages as in the example: `"i18n: ["en", "fr" ]`. Also, add any localized metadata for displayed metadata in the Extension Manager, as in the example: `"fr": { "title": "localized title" }`. * strings.js – uses i18n to load a strings.js file in the nls folder diff --git a/src/extensions/samples/LocalizationExample/package.json b/src/extensions/samples/LocalizationExample/package.json index 83b2020cb02..030295035d3 100644 --- a/src/extensions/samples/LocalizationExample/package.json +++ b/src/extensions/samples/LocalizationExample/package.json @@ -12,5 +12,11 @@ "i18n": [ "en", "fr" - ] + ], + "package-i18n": { + "fr": { + "title": "Localisation Exemple", + "description": "Un guide sur la façon de localiser votre poste." + } + } } diff --git a/test/spec/ExtensionManager-test-files/mockRegistry.json b/test/spec/ExtensionManager-test-files/mockRegistry.json index b1b1054e768..259639f7071 100644 --- a/test/spec/ExtensionManager-test-files/mockRegistry.json +++ b/test/spec/ExtensionManager-test-files/mockRegistry.json @@ -48,7 +48,16 @@ "metadata": { "name": "mock-extension-1", "description": "First mock extension", - "version": "1.0.0" + "title": "Mock extension title", + "version": "1.0.0", + "i18n": [ "en", "fr" ], + "package-i18n": { + "fr": { + "description": "Première prolongation maquette", + "title": "Titre d'extension Mock", + "warnings": "Shouldn't be used" + } + } }, "owner": "github:mockuser", "versions": [ diff --git a/test/spec/ExtensionManager-test.js b/test/spec/ExtensionManager-test.js index 5fec9fc6055..2eece419f6f 100644 --- a/test/spec/ExtensionManager-test.js +++ b/test/spec/ExtensionManager-test.js @@ -58,7 +58,8 @@ define(function (require, exports, module) { mockRegistry; describe("ExtensionManager", function () { - var mockId, mockSettings, origRegistryURL, origExtensionUrl, removedPath; + var mockId, mockSettings, origRegistryURL, origExtensionUrl, removedPath, + view, model, fakeLoadDeferred, modelDisposed; beforeEach(function () { // Use fake URLs for the registry (useful if the registry isn't actually currently @@ -158,6 +159,53 @@ define(function (require, exports, module) { }; } + function setupExtensionManagerViewTests(context) { + context.addMatchers({ + toHaveText: function (expected) { + var notText = this.isNot ? " not" : ""; + this.message = function () { + return "Expected view" + notText + " to contain text " + expected; + }; + return SpecRunnerUtils.findDOMText(this.actual.$el, expected); + }, + toHaveLink: function (expected) { + var notText = this.isNot ? " not" : ""; + this.message = function () { + return "Expected view" + notText + " to contain link " + expected; + }; + return SpecRunnerUtils.findDOMText(this.actual.$el, expected, true); + } + }); + spyOn(InstallExtensionDialog, "installUsingDialog").andCallFake(function (url) { + var id = url.match(/fake-repository\.com\/([^\/]+)/)[1]; + mockLoadExtensions(["user/" + id]); + }); + } + + function cleanupExtensionManagerViewTests() { + if (view) { + view.$el.remove(); + view = null; + } + if (model) { + model.dispose(); + } + } + + // Sets up the view using the normal (mock) ExtensionManager data. + function setupViewWithMockData(ModelClass) { + runs(function () { + view = new ExtensionManagerView(); + model = new ModelClass(); + modelDisposed = false; + waitsForDone(view.initialize(model), "view initializing"); + view.$el.appendTo(document.body); + }); + runs(function () { + spyOn(view.model, "dispose").andCallThrough(); + }); + } + describe("ExtensionManager", function () { it("should download the extension list from the registry", function () { runs(function () { @@ -862,55 +910,15 @@ define(function (require, exports, module) { }); describe("ExtensionManagerView", function () { - var view, model, fakeLoadDeferred, modelDisposed; - - // Sets up the view using the normal (mock) ExtensionManager data. - function setupViewWithMockData(ModelClass) { - runs(function () { - view = new ExtensionManagerView(); - model = new ModelClass(); - modelDisposed = false; - waitsForDone(view.initialize(model), "view initializing"); - view.$el.appendTo(document.body); - }); - runs(function () { - spyOn(view.model, "dispose").andCallThrough(); - }); - } - + beforeEach(function () { - this.addMatchers({ - toHaveText: function (expected) { - var notText = this.isNot ? " not" : ""; - this.message = function () { - return "Expected view" + notText + " to contain text " + expected; - }; - return SpecRunnerUtils.findDOMText(this.actual.$el, expected); - }, - toHaveLink: function (expected) { - var notText = this.isNot ? " not" : ""; - this.message = function () { - return "Expected view" + notText + " to contain link " + expected; - }; - return SpecRunnerUtils.findDOMText(this.actual.$el, expected, true); - } - }); - spyOn(InstallExtensionDialog, "installUsingDialog").andCallFake(function (url) { - var id = url.match(/fake-repository\.com\/([^\/]+)/)[1]; - mockLoadExtensions(["user/" + id]); - }); + setupExtensionManagerViewTests(this); spyOn(brackets, "getLocale").andReturn("en"); }); afterEach(function () { - if (view) { - view.$el.remove(); - view = null; - } - if (model) { - model.dispose(); - } + cleanupExtensionManagerViewTests(); }); describe("when showing registry entries", function () { @@ -1795,5 +1803,32 @@ define(function (require, exports, module) { }); }); }); + + describe("ExtensionManagerView-i18n", function () { + + beforeEach(function () { + setupExtensionManagerViewTests(this); + spyOn(brackets, "getLocale").andReturn("fr"); + }); + + afterEach(function () { + cleanupExtensionManagerViewTests(); + }); + + it("should display localized description", function () { + setupViewWithMockData(ExtensionManagerViewModel.RegistryViewModel); + runs(function () { + _.forEach(mockRegistry, function (item) { + if (item.metadata["package-i18n"] && + item.metadata["package-i18n"].hasOwnProperty("fr") && + item.metadata["package-i18n"].fr.hasOwnProperty("description")) { + expect(view).toHaveText(item.metadata["package-i18n"].fr.description); + expect(view).toHaveText(item.metadata["package-i18n"].fr.title); + expect(view).not.toHaveText(item.metadata["package-i18n"].fr.warnings); + } + }); + }); + }); + }); }); });