зеркало из https://github.com/microsoft/TACO.git
Updating ResourceManager to allow multiple languages to be specified (for vs-mda-remote)
Adding tests for ResourceManger (which pass!) Added more error checking to typescript compilation (When the error is not to do with a file, it would crash in the error reporting code). Adding in more utility code Added in cordova config parser to utils, along with the start of some file helpers. Fixed up compilation of resources.ts. Protip: ///references have to be before any code, such as "use strict" Added BuildInfo class to util since that will be common across client and server (as well as server components)
This commit is contained in:
Родитель
51b263407a
Коммит
cf44cd6fc3
|
@ -36,7 +36,7 @@ gulp.task("rebuild", ["clean"], function (callback: Function): void {
|
|||
gulp.task("run-stylecop", function (callback: Function): void {
|
||||
if (fs.existsSync(buildConfig.copPath)) {
|
||||
var styleCop = new stylecopUtil.StyleCopUtil();
|
||||
styleCop.runCop(path.join(buildConfig.src, ".."), buildConfig.copPath, callback);
|
||||
styleCop.runCop(buildConfig.src, buildConfig.copPath, callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ gulp.task("clean", function (callback: Function): void {
|
|||
gulp.task("copy", function (callback: Function): void {
|
||||
gulp.src(path.join(buildConfig.src, "/**/package.json")).pipe(gulp.dest(buildConfig.bin));
|
||||
gulp.src(path.join(buildConfig.src, "/**/resources.json")).pipe(gulp.dest(buildConfig.bin));
|
||||
gulp.src(path.join(buildConfig.src, "/**/test/**")).pipe(gulp.dest(buildConfig.bin));
|
||||
});
|
||||
|
||||
/* auto-generate taco-utils.d.ts*/
|
|
@ -44,7 +44,6 @@ export class TypeScriptServices {
|
|||
if (!fs.statSync(currentPath).isDirectory()) {
|
||||
/* push the typescript files */
|
||||
if (path.extname(currentPath) === ".ts" &&
|
||||
!currentPath.match("d.ts$") &&
|
||||
!currentPath.match("gulpfile.ts") &&
|
||||
!currentPath.match("gulpmain.ts")) {
|
||||
result.push(currentPath);
|
||||
|
@ -79,8 +78,12 @@ export class TypeScriptServices {
|
|||
*/
|
||||
private logDiagnosticMessage(diagnostic: ts.Diagnostic): void {
|
||||
var sourceFile = diagnostic.file;
|
||||
var lineAndCharacter = sourceFile.getLineAndCharacterFromPosition(diagnostic.start);
|
||||
console.warn(sourceFile.filename + "(" + lineAndCharacter.line + "," + lineAndCharacter.character + "): " + diagnostic.messageText);
|
||||
if (sourceFile) {
|
||||
var lineAndCharacter = sourceFile.getLineAndCharacterFromPosition(diagnostic.start);
|
||||
console.warn(sourceFile.filename + "(" + lineAndCharacter.line + "," + lineAndCharacter.character + "): " + diagnostic.messageText);
|
||||
} else {
|
||||
console.warn(diagnostic.messageText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,13 @@
|
|||
"version": "0.0.1",
|
||||
"main": "taco-utils.js",
|
||||
"dependencies": {
|
||||
"elementtree": "0.1.6",
|
||||
"unorm": "1.3.3"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"typescript": ">=1.4.1"
|
||||
"typescript": ">=1.4.1",
|
||||
"mocha": "2.0.1",
|
||||
"should": "4.3.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,71 @@
|
|||
/// <reference path="../typings/node.d.ts" />
|
||||
/// <reference path="../typings/elementtree.d.ts"/>
|
||||
/// <reference path="../typings/unorm.d.ts"/>
|
||||
"use strict";
|
||||
|
||||
import et = require ("elementtree");
|
||||
import fs = require ("fs");
|
||||
import path = require ("path");
|
||||
var unorm = require("unorm"); // Note no import: the compiler will remove the require since we don't use the unorm object, we just need it to add String.normalize
|
||||
|
||||
module TacoUtility {
|
||||
// put more classes here, known limitation that classes in external modules CANNOT span multiple files
|
||||
export class ResourcesManager {
|
||||
private static Resources: any = null;
|
||||
private static DefaultLanguage: string = "en";
|
||||
private static Resources: { [key: string]: any; } = null;
|
||||
private static SupportedLanguages: string[] = null;
|
||||
private static DefaultLanguage: string = "en";
|
||||
|
||||
public static init(language: string, resourcesDir?: string): void {
|
||||
public static init(language: string, resourcesDir?: string): void {
|
||||
if (!resourcesDir) {
|
||||
resourcesDir = path.join(__dirname, "..", "resources");
|
||||
}
|
||||
|
||||
var lang = this.bestLanguageMatchOrDefault(language, resourcesDir);
|
||||
this.Resources = this.loadLanguage(lang, resourcesDir);
|
||||
ResourcesManager.Resources = {};
|
||||
ResourcesManager.SupportedLanguages = [];
|
||||
fs.readdirSync(resourcesDir).forEach(function (filename: string): void {
|
||||
try {
|
||||
ResourcesManager.Resources[filename.toLowerCase()] = ResourcesManager.loadLanguage(filename, resourcesDir);
|
||||
ResourcesManager.SupportedLanguages.push(filename.toLowerCase());
|
||||
} catch (e) {
|
||||
// Folder was not a valid resource; ignore it
|
||||
}
|
||||
});
|
||||
|
||||
ResourcesManager.DefaultLanguage = ResourcesManager.bestLanguageMatchOrDefault(language);
|
||||
}
|
||||
|
||||
/* ...optionalArgs is only there for typings, function rest params */
|
||||
// For unit tests
|
||||
public static teardown(): void {
|
||||
ResourcesManager.Resources = null;
|
||||
ResourcesManager.SupportedLanguages = null;
|
||||
}
|
||||
|
||||
/** ...optionalArgs is only there for typings, function rest params */
|
||||
public static getString(id: string, ...optionalArgs: any[]): string {
|
||||
if (!this.Resources) {
|
||||
var args = ResourcesManager.getOptionalArgsArrayFromFunctionCall(arguments, 1);
|
||||
return ResourcesManager.getStringForLanguage(ResourcesManager.DefaultLanguage, id, args);
|
||||
}
|
||||
|
||||
/** ** ...optionalArgs is only there for typings, function rest params** */
|
||||
public static getStringForLanguage(requestOrAcceptLangs: any, id: string, ...optionalArgs: any[]): string {
|
||||
if (!ResourcesManager.Resources) {
|
||||
throw new Error("Resources have not been loaded");
|
||||
}
|
||||
|
||||
var s = this.Resources[id];
|
||||
var lang = ResourcesManager.bestLanguageMatchOrDefault(requestOrAcceptLangs);
|
||||
|
||||
var s = ResourcesManager.Resources[lang][id];
|
||||
if (!s) {
|
||||
return s;
|
||||
} else if (Array.isArray(s)) {
|
||||
// Allow longer resources strings to be specified as a list of strings, which represent multiple lines
|
||||
s = s.join("\n");
|
||||
}
|
||||
|
||||
/*All args passed to current function:
|
||||
you can call getString('foo', 'bar', 'baz') or getString('foo',['bar', 'baz'])
|
||||
and the utility function will extract ['bar', 'baz'] as args in both cases*/
|
||||
var args = this.getOptionalArgsArrayFromFunctionCall(arguments, 1);
|
||||
var args = ResourcesManager.getOptionalArgsArrayFromFunctionCall(arguments, 2);
|
||||
if (args) {
|
||||
for (var i: number = 0; i < args.length; i++) {
|
||||
s = s.replace("{" + i + "}", args[i]);
|
||||
|
@ -43,39 +75,146 @@ module TacoUtility {
|
|||
return s;
|
||||
}
|
||||
|
||||
public static bestLanguageMatchOrDefault(language: string, resourcesDir: string): string {
|
||||
if (!language) {
|
||||
return this.DefaultLanguage;
|
||||
private static bestLanguageMatchOrDefault(requestOrAcceptLangs: any): string {
|
||||
var lang = ResourcesManager.bestLanguageMatch(requestOrAcceptLangs);
|
||||
if (!ResourcesManager.Resources[lang]) {
|
||||
lang = ResourcesManager.DefaultLanguage;
|
||||
}
|
||||
|
||||
var supportedLanguages: string[] = [];
|
||||
fs.readdirSync(resourcesDir).filter(function (c: string): boolean {
|
||||
return fs.existsSync(path.join(resourcesDir, c, "resources.json"));
|
||||
}).forEach(function (l: string): void {
|
||||
supportedLanguages.push(l.toLowerCase());
|
||||
});
|
||||
|
||||
// TODO: remove assumption of case insensitive file system, so this can work on non-windows systems.
|
||||
var lang = language.toLowerCase();
|
||||
if (supportedLanguages.indexOf(lang) !== -1) {
|
||||
// exact match on language, which could include the region (e.g. it-CH), return the match
|
||||
return lang;
|
||||
}
|
||||
|
||||
var primaryLang = lang.split("-")[0];
|
||||
if (supportedLanguages.indexOf(primaryLang) !== -1) {
|
||||
// match on primary language (e.g. it from it-CH).
|
||||
return primaryLang;
|
||||
}
|
||||
|
||||
return this.DefaultLanguage;
|
||||
return lang.toLowerCase();
|
||||
}
|
||||
|
||||
public static loadLanguage(language: string, resourcesDir: string): any {
|
||||
/**
|
||||
* requestOrAcceptLangs can either be:
|
||||
* A string, with format "LangSpec[,LangSpec]*" where LangSpec is "Language[;anything]"
|
||||
* e.g. "pl,fr-FR;q=0.3,en-US;q=0.1" is interpreted as "pl" or "fr-FR" or "en-US". Currently we ignore provided quality (q) values
|
||||
* An array, which we assume is an array of strings representing languages such as "pl" or "fr-FR"
|
||||
* A (express-style) HTTP Request object, with a headers property specifing "accept-language" in a string, as above
|
||||
*
|
||||
* This allows us to handle simple cases of a single string, as well as more complex cases where a client specifies
|
||||
* multiple preferences.
|
||||
*/
|
||||
private static bestLanguageMatch(requestOrAcceptLangs: any): string {
|
||||
if (Array.isArray(requestOrAcceptLangs)) {
|
||||
return ResourcesManager.getBestLanguageFromArray(requestOrAcceptLangs);
|
||||
}
|
||||
|
||||
var langString: string;
|
||||
if (!requestOrAcceptLangs) {
|
||||
return ResourcesManager.DefaultLanguage;
|
||||
} else if (typeof requestOrAcceptLangs === "string") {
|
||||
langString = requestOrAcceptLangs;
|
||||
} else if (requestOrAcceptLangs.headers) {
|
||||
langString = requestOrAcceptLangs.headers["accept-language"] || "";
|
||||
} else {
|
||||
throw new Error("Unsupported type of argument for acceptLangs: " + (typeof requestOrAcceptLangs));
|
||||
}
|
||||
|
||||
return ResourcesManager.getBestLanguageFromArray(langString.split(",").map(function (l: string): string { return l.split(";")[0]; }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of languages, we try to find an exact match if we can, and we fall back to a primary language otherwise.
|
||||
* e.g. "fr-CA" will use "fr-CA" resources if present, but will fall back to "fr" resources if they are available
|
||||
*/
|
||||
private static getBestLanguageFromArray(acceptLangs: string[]): string {
|
||||
var primaryLanguageMatch: string = null;
|
||||
for (var i: number = 0; i < acceptLangs.length; ++i) {
|
||||
var lang: string = acceptLangs[i].toLowerCase();
|
||||
var primaryLang = lang.split("-")[0];
|
||||
if (ResourcesManager.SupportedLanguages.indexOf(lang) !== -1) {
|
||||
// Exact match on full language header, which could include the region
|
||||
return lang;
|
||||
}
|
||||
|
||||
if (ResourcesManager.SupportedLanguages.indexOf(primaryLang) !== -1) {
|
||||
// Match on primary language (e.g. it from it-CH). We may find a better match later, so continue looking.
|
||||
primaryLanguageMatch = primaryLang;
|
||||
}
|
||||
}
|
||||
|
||||
return primaryLanguageMatch;
|
||||
}
|
||||
|
||||
private static loadLanguage(language: string, resourcesDir: string): any {
|
||||
var resourcesPath = path.join(resourcesDir, language, "resources.json");
|
||||
return require(resourcesPath);
|
||||
}
|
||||
|
||||
private static getOptionalArgsArrayFromFunctionCall(functionArguments: IArguments, startFrom: number): any[] {
|
||||
if (functionArguments.length <= startFrom) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(functionArguments[startFrom])) {
|
||||
return functionArguments[startFrom];
|
||||
}
|
||||
|
||||
return Array.prototype.slice.apply(functionArguments, [startFrom]);
|
||||
}
|
||||
}
|
||||
|
||||
export class CordovaConfig {
|
||||
/** CordovaConfig is a class for parsing the config.xml file for Cordova projects */
|
||||
private _doc: et.ElementTree;
|
||||
|
||||
constructor(configXmlPath: string) {
|
||||
var contents = UtilHelper.readFileContentsSync(configXmlPath, "utf-8");
|
||||
this._doc = new et.ElementTree(et.XML(contents));
|
||||
}
|
||||
|
||||
public id(): string {
|
||||
return this._doc.getroot().attrib["id"];
|
||||
}
|
||||
|
||||
public name(): string {
|
||||
var el = this._doc.find("name");
|
||||
return el && el.text && el.text.trim().normalize();
|
||||
}
|
||||
|
||||
public version(): string {
|
||||
var el = this._doc.getroot();
|
||||
return el && el.attrib && el.attrib["version"];
|
||||
}
|
||||
|
||||
public preferences(): { [key: string]: string } {
|
||||
var data: { [key: string]: string } = {};
|
||||
var preferences = this._doc.findall("preference");
|
||||
preferences.forEach(function (preference: et.XMLElement): void {
|
||||
data[preference.attrib["name"]] = preference.attrib["value"];
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export class UtilHelper {
|
||||
private static InvalidAppNameChars = {
|
||||
34: "\"",
|
||||
36: "$",
|
||||
38: "&",
|
||||
39: "/",
|
||||
60: "<",
|
||||
92: "\\"
|
||||
};
|
||||
/** Converts an untyped argument into a boolean in a "sensible" way, treating only the string "true" as true rather than any non-empty string * */
|
||||
public static argToBool(input: any): boolean {
|
||||
if (typeof input === "string") {
|
||||
return input.toLowerCase() === "true";
|
||||
}
|
||||
|
||||
return !!input;
|
||||
}
|
||||
|
||||
public static readFileContentsSync(filename: string, encoding?: string): string {
|
||||
var contents = fs.readFileSync(filename, (encoding || "utf-8"));
|
||||
if (contents) {
|
||||
contents = contents.replace(/^\uFEFF/, ""); // Windows is the BOM
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
public static getOptionalArgsArrayFromFunctionCall(functionArguments: IArguments, startFrom: number): any[] {
|
||||
if (functionArguments.length <= startFrom) {
|
||||
return null;
|
||||
|
@ -88,6 +227,86 @@ module TacoUtility {
|
|||
return Array.prototype.slice.apply(functionArguments, [startFrom]);
|
||||
}
|
||||
}
|
||||
|
||||
export class BuildInfo {
|
||||
public status: string;
|
||||
/**
|
||||
* BuildInfo holds relevant information about a particular build, including its identifier, what kind of build was requested, and the status of the build
|
||||
* This information is passed around both within the remote build server, and back to the client
|
||||
*/
|
||||
public changeList: {
|
||||
deletedFiles: string[];
|
||||
changedFiles: string[];
|
||||
addedPlugins: string[];
|
||||
deletedPlugins: string[];
|
||||
deletedFilesIos: string[];
|
||||
changedFilesIos: string[];
|
||||
addedPluginsIos: string[];
|
||||
deletedPluginsIos: string[];
|
||||
};
|
||||
public statusTime: Date;
|
||||
public cordovaVersion: string;
|
||||
public buildCommand: string;
|
||||
public configuration: string;
|
||||
public options: any;
|
||||
public buildDir: string;
|
||||
public buildLang: string;
|
||||
public buildPlatform: string;
|
||||
public submissionTime: Date;
|
||||
public buildNumber: number;
|
||||
|
||||
public messageId: string;
|
||||
public messageArgs: any[];
|
||||
public message: string;
|
||||
|
||||
public tgzFilePath: string;
|
||||
public appDir: string;
|
||||
public appName: string;
|
||||
|
||||
public webDebugProxyPort: number;
|
||||
|
||||
constructor(params: { buildNumber?: number; status?: string; cordovaVersion?: string; buildCommand?: string; configuration?: string; options?: any; buildDir?: string; buildLang?: string; buildPlatform?: string }) {
|
||||
this.buildNumber = params.buildNumber;
|
||||
this.status = params.status;
|
||||
this.cordovaVersion = params.cordovaVersion;
|
||||
this.buildCommand = params.buildCommand;
|
||||
this.configuration = params.configuration;
|
||||
this.options = params.options;
|
||||
this.buildDir = params.buildDir;
|
||||
this.buildLang = params.buildLang;
|
||||
this.buildPlatform = params.buildPlatform;
|
||||
this.submissionTime = new Date();
|
||||
this.changeList = null;
|
||||
}
|
||||
|
||||
public static createNewBuildInfoFromDataObject(buildInfoData: any): BuildInfo {
|
||||
var bi: any = new BuildInfo(buildInfoData);
|
||||
Object.keys(buildInfoData).forEach(function (k: string): void {
|
||||
bi[k] = buildInfoData[k];
|
||||
});
|
||||
return bi;
|
||||
}
|
||||
|
||||
public updateStatus(status: string, messageId?: string, ...messageArgs: any[]): void {
|
||||
this.status = status;
|
||||
this.messageId = messageId;
|
||||
if (arguments.length > 2) {
|
||||
this.messageArgs = UtilHelper.getOptionalArgsArrayFromFunctionCall(arguments, 2);
|
||||
}
|
||||
|
||||
this.statusTime = new Date();
|
||||
}
|
||||
|
||||
public localize(req: any): BuildInfo {
|
||||
if (this.messageId) {
|
||||
this.message = ResourcesManager.getStringForLanguage(req, this.messageId, this.messageArgs);
|
||||
} else {
|
||||
this.message = ResourcesManager.getStringForLanguage(req, "Build-" + this.status);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export = TacoUtility;
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* ******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
"use strict";
|
||||
/// <reference path="../../typings/should.d.ts"/>
|
||||
/// <reference path="../../typings/mocha.d.ts"/>
|
||||
import should = require ("should");
|
||||
import mocha = require ("mocha");
|
||||
|
||||
import fs = require ("fs");
|
||||
import path = require ("path");
|
||||
|
||||
import util = require ("../taco-utils");
|
||||
|
||||
/// <disable code="SA1301" justification="CordovaConfig is a class" />
|
||||
var CordovaConfig = util.CordovaConfig;
|
||||
/// <enable code="SA1301" />
|
||||
|
||||
describe("CordovaConfig", function (): void {
|
||||
it("should correctly parse config files", function (): void {
|
||||
var cfg = new CordovaConfig(path.join(__dirname, "resources", "config.xml"));
|
||||
cfg.id().should.equal("org.foo.bar");
|
||||
cfg.name().should.equal("FooBar");
|
||||
cfg.version().should.equal("0.9.2");
|
||||
});
|
||||
|
||||
it("should correctly normalize unicode display names", function (): void {
|
||||
var cfg = new CordovaConfig(path.join(__dirname, "resources", "config_unicode.xml"));
|
||||
cfg.name().should.equal("隣兀﨩"); // Note that this is NOT identical to the name in config_unicode, it is the normalized form.
|
||||
});
|
||||
});
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* ******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
/// <reference path="../../typings/node.d.ts"/>
|
||||
/// <reference path="../../typings/should.d.ts"/>
|
||||
/// <reference path="../../typings/mocha.d.ts"/>
|
||||
"use strict";
|
||||
import path = require ("path");
|
||||
import should = require ("should");
|
||||
import mocha = require ("mocha");
|
||||
|
||||
import util = require ("../taco-utils");
|
||||
|
||||
var resources = util.ResourcesManager;
|
||||
|
||||
describe("resources", function (): void {
|
||||
before(function (): void {
|
||||
resources.init("en", path.join(__dirname, "resources"));
|
||||
});
|
||||
after(function (): void {
|
||||
resources.teardown();
|
||||
});
|
||||
|
||||
it("should use the default language by default", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/en/resources.json"));
|
||||
var actual = resources.getString("SimpleMessage");
|
||||
var expected = expectedResources["SimpleMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
|
||||
it("should correctly return strings for a primary language", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/en/resources.json"));
|
||||
var actual = resources.getStringForLanguage("en", "SimpleMessage");
|
||||
var expected = expectedResources["SimpleMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
|
||||
it("should correctly return strings for a non-english primary language", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/it/resources.json"));
|
||||
var actual = resources.getStringForLanguage("it", "SimpleMessage");
|
||||
var expected = expectedResources["SimpleMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
|
||||
it("should correctly substitute arguments in resource strings", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/en/resources.json"));
|
||||
var actual = resources.getStringForLanguage("en", "MessageWithArgs", "Billy", "Fish");
|
||||
var expected = expectedResources["MessageWithArgs"];
|
||||
var actualBackToExpected = actual.replace("Billy", "{0}").replace("Fish", "{1}");
|
||||
actualBackToExpected.should.equal(expected);
|
||||
|
||||
var actualWithArrayArgs = resources.getStringForLanguage("en", "MessageWithArgs", ["Billy", "Fish"]);
|
||||
actualWithArrayArgs.should.equal(actual);
|
||||
});
|
||||
|
||||
it("should find the most specific region for a language", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/it-ch/resources.json"));
|
||||
var actual = resources.getStringForLanguage("it-ch", "SimpleMessage");
|
||||
var expected = expectedResources["SimpleMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
|
||||
it("should fall back to more general languages if a more specific one is not available", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/it/resources.json"));
|
||||
var actual = resources.getStringForLanguage("it-DE", "SimpleMessage");
|
||||
var expected = expectedResources["SimpleMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
|
||||
it("should fall back to english as a default if the language is unknown", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/en/resources.json"));
|
||||
var actual = resources.getStringForLanguage("hy-Latn-IT-arevela", "SimpleMessage");
|
||||
var expected = expectedResources["SimpleMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
|
||||
it("should return undefined for bad resource identifiers", function (): void {
|
||||
var actual = resources.getStringForLanguage("en", "NoResourceDefinedForThis");
|
||||
/// <disable code="SA9017" justification="We want to capture any changes in behavior, and currently it returns undefined" />
|
||||
should(actual).be.equal(undefined);
|
||||
/// <enable code="SA9017" />
|
||||
});
|
||||
|
||||
it("should handle unicode in resource strings", function (): void {
|
||||
var expectedResources = require(path.join(__dirname, "/resources/gb18030/resources.json"));
|
||||
var actual = resources.getStringForLanguage("gb18030", "UnicodeMessage");
|
||||
var expected = expectedResources["UnicodeMessage"];
|
||||
actual.should.equal(expected);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="org.foo.bar" version="0.9.2" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:vs="http://schemas.microsoft.com/appx/2014/htmlapps" xmlns:gap="http://phonegap.com/ns/1.0">
|
||||
<name>FooBar</name>
|
||||
<description>
|
||||
A sample Apache Cordova application that responds to the deviceready event.
|
||||
</description>
|
||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<!--<vs:features>
|
||||
<vs:feature>org.apache.cordova.splashscreen</vs:feature>
|
||||
</vs:features>-->
|
||||
</widget>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="org.foo.bar" version="0.9.2" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:vs="http://schemas.microsoft.com/appx/2014/htmlapps" xmlns:gap="http://phonegap.com/ns/1.0">
|
||||
<name>隣兀﨩</name>
|
||||
<description>
|
||||
A sample Apache Cordova application that responds to the deviceready event.
|
||||
</description>
|
||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<!--<vs:features>
|
||||
<vs:feature>org.apache.cordova.splashscreen</vs:feature>
|
||||
</vs:features>-->
|
||||
</widget>
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"SimpleMessage": "Hello",
|
||||
"MessageWithArgs": "Hello {0} {1}"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"UnicodeMessage": "啊齄丂狛狜隣郎隣兀﨩ˊ▇█〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€龹龻㐀㒣㕴㕵㙉㙊䵯䵰䶴䶵𠀀𠀁𠀂𠀃𪛑𪛒𪛓𪛔𪛕𪛖"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"SimpleMessage": "Ciao it-CH",
|
||||
"MessageWithArgs": "Ciao {0} {1} it-CH"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"SimpleMessage": "Ciao",
|
||||
"MessageWithArgs": "Ciao {0} {1}"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Barebones typing for elementtree, added as-needed
|
||||
|
||||
declare module "elementtree" {
|
||||
export class ElementTree {
|
||||
constructor(xml: XMLElement);
|
||||
|
||||
getroot(): XMLElement
|
||||
find(name: string): XMLElement;
|
||||
findall(name: string): XMLElement[];
|
||||
}
|
||||
|
||||
export class XMLElement {
|
||||
attrib: { [key: string]: string };
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function XML(data: string): XMLElement;
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// Type definitions for mocha 1.17.1
|
||||
// Project: http://visionmedia.github.io/mocha/
|
||||
// Definitions by: Kazi Manzur Rashid <https://github.com/kazimanzurrashid/>, otiai10 <https://github.com/otiai10>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
interface Mocha {
|
||||
// Setup mocha with the given setting options.
|
||||
setup(options: MochaSetupOptions): Mocha;
|
||||
|
||||
//Run tests and invoke `fn()` when complete.
|
||||
run(callback?: () => void): void;
|
||||
|
||||
// Set reporter as function
|
||||
reporter(reporter: () => void): Mocha;
|
||||
|
||||
// Set reporter, defaults to "dot"
|
||||
reporter(reporter: string): Mocha;
|
||||
|
||||
// Enable growl support.
|
||||
growl(): Mocha
|
||||
}
|
||||
|
||||
interface MochaSetupOptions {
|
||||
//milliseconds to wait before considering a test slow
|
||||
slow?: number;
|
||||
|
||||
// timeout in milliseconds
|
||||
timeout?: number;
|
||||
|
||||
// ui name "bdd", "tdd", "exports" etc
|
||||
ui?: string;
|
||||
|
||||
//array of accepted globals
|
||||
globals?: any[];
|
||||
|
||||
// reporter instance (function or string), defaults to `mocha.reporters.Dot`
|
||||
reporter?: any;
|
||||
|
||||
// bail on the first test failure
|
||||
bail?: Boolean;
|
||||
|
||||
// ignore global leaks
|
||||
ignoreLeaks?: Boolean;
|
||||
|
||||
// grep string or regexp to filter tests with
|
||||
grep?: any;
|
||||
}
|
||||
|
||||
interface MochaDone {
|
||||
(error?: Error): void;
|
||||
}
|
||||
|
||||
declare var mocha: Mocha;
|
||||
|
||||
declare var describe: {
|
||||
(description: string, spec: () => void): void;
|
||||
only(description: string, spec: () => void): void;
|
||||
skip(description: string, spec: () => void): void;
|
||||
timeout(ms: number): void;
|
||||
}
|
||||
|
||||
// alias for `describe`
|
||||
declare var context: {
|
||||
(contextTitle: string, spec: () => void): void;
|
||||
only(contextTitle: string, spec: () => void): void;
|
||||
skip(contextTitle: string, spec: () => void): void;
|
||||
timeout(ms: number): void;
|
||||
}
|
||||
|
||||
declare var it: {
|
||||
(expectation: string, assertion?: () => void): void;
|
||||
(expectation: string, assertion?: (done: MochaDone) => void): void;
|
||||
only(expectation: string, assertion?: () => void): void;
|
||||
only(expectation: string, assertion?: (done: MochaDone) => void): void;
|
||||
skip(expectation: string, assertion?: () => void): void;
|
||||
skip(expectation: string, assertion?: (done: MochaDone) => void): void;
|
||||
timeout(ms: number): void;
|
||||
};
|
||||
|
||||
declare function before(action: () => void): void;
|
||||
|
||||
declare function before(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function setup(action: () => void): void;
|
||||
|
||||
declare function setup(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function after(action: () => void): void;
|
||||
|
||||
declare function after(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function teardown(action: () => void): void;
|
||||
|
||||
declare function teardown(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function beforeEach(action: () => void): void;
|
||||
|
||||
declare function beforeEach(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function suiteSetup(action: () => void): void;
|
||||
|
||||
declare function suiteSetup(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function afterEach(action: () => void): void;
|
||||
|
||||
declare function afterEach(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function suiteTeardown(action: () => void): void;
|
||||
|
||||
declare function suiteTeardown(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare module "mocha" {
|
||||
export = undefined;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
// Type definitions for should.js 3.1.2
|
||||
// Project: https://github.com/visionmedia/should.js
|
||||
// Definitions by: Alex Varju <https://github.com/varju/>, Maxime LUCE <https://github.com/SomaticIT/>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
interface Object {
|
||||
should: ShouldAssertion;
|
||||
}
|
||||
|
||||
interface ShouldAssertion {
|
||||
// basic grammar
|
||||
a: ShouldAssertion;
|
||||
an: ShouldAssertion;
|
||||
and: ShouldAssertion;
|
||||
be: ShouldAssertion;
|
||||
have: ShouldAssertion;
|
||||
with: ShouldAssertion;
|
||||
of: ShouldAssertion;
|
||||
not: ShouldAssertion;
|
||||
|
||||
// validators
|
||||
arguments: ShouldAssertion;
|
||||
empty: ShouldAssertion;
|
||||
ok: ShouldAssertion;
|
||||
true: ShouldAssertion;
|
||||
false: ShouldAssertion;
|
||||
NaN: ShouldAssertion;
|
||||
Infinity: ShouldAssertion;
|
||||
Array: ShouldAssertion;
|
||||
Object: ShouldAssertion;
|
||||
String: ShouldAssertion;
|
||||
Boolean: ShouldAssertion;
|
||||
Number: ShouldAssertion;
|
||||
Error: ShouldAssertion;
|
||||
Function: ShouldAssertion;
|
||||
eql(expected: any, description?: string): ShouldAssertion;
|
||||
equal(expected: any, description?: string): ShouldAssertion;
|
||||
within(start: number, finish: number, description?: string): ShouldAssertion;
|
||||
approximately(value: number, delta: number, description?: string): ShouldAssertion;
|
||||
type(expected: any, description?: string): ShouldAssertion;
|
||||
instanceof(constructor: Function, description?: string): ShouldAssertion;
|
||||
above(n: number, description?: string): ShouldAssertion;
|
||||
below(n: number, description?: string): ShouldAssertion;
|
||||
match(other: {}, description?: string): ShouldAssertion;
|
||||
match(other: (val: any) => any, description?: string): ShouldAssertion;
|
||||
match(regexp: RegExp, description?: string): ShouldAssertion;
|
||||
match(other: any, description?: string): ShouldAssertion;
|
||||
matchEach(other: {}, description?: string): ShouldAssertion;
|
||||
matchEach(other: (val: any) => any, description?: string): ShouldAssertion;
|
||||
matchEach(regexp: RegExp, description?: string): ShouldAssertion;
|
||||
matchEach(other: any, description?: string): ShouldAssertion;
|
||||
length(n: number, description?: string): ShouldAssertion;
|
||||
property(name: string, description?: string): ShouldAssertion;
|
||||
property(name: string, val: any, description?: string): ShouldAssertion;
|
||||
properties(names: string[]): ShouldAssertion;
|
||||
properties(name: string): ShouldAssertion;
|
||||
properties(descriptor: any): ShouldAssertion;
|
||||
properties(...properties: string[]): ShouldAssertion;
|
||||
ownProperty(name: string, description?: string): ShouldAssertion;
|
||||
contain(obj: any): ShouldAssertion;
|
||||
containEql(obj: any): ShouldAssertion;
|
||||
containDeep(obj: any): ShouldAssertion;
|
||||
keys(...allKeys: string[]): ShouldAssertion;
|
||||
keys(allKeys: string[]): ShouldAssertion;
|
||||
header(field: string, val?: string): ShouldAssertion;
|
||||
status(code: number): ShouldAssertion;
|
||||
json: ShouldAssertion;
|
||||
html: ShouldAssertion;
|
||||
startWith(expected: string, message?: any): ShouldAssertion;
|
||||
endWith(expected: string, message?: any): ShouldAssertion;
|
||||
throw(message?: any): ShouldAssertion;
|
||||
|
||||
// deprecated
|
||||
include(obj: any, description?: string): ShouldAssertion;
|
||||
includeEql(obj: any[], description?: string): ShouldAssertion;
|
||||
|
||||
// aliases
|
||||
exactly(expected: any, description?: string): ShouldAssertion;
|
||||
instanceOf(constructor: Function, description?: string): ShouldAssertion;
|
||||
throwError(message?: any): ShouldAssertion;
|
||||
lengthOf(n: number, description?: string): ShouldAssertion;
|
||||
key(key: string): ShouldAssertion;
|
||||
haveOwnProperty(name: string, description?: string): ShouldAssertion;
|
||||
greaterThan(n: number, description?: string): ShouldAssertion;
|
||||
lessThan(n: number, description?: string): ShouldAssertion;
|
||||
}
|
||||
|
||||
interface ShouldInternal {
|
||||
// should.js's extras
|
||||
exist(actual: any, msg?: string): void;
|
||||
exists(actual: any, msg?: string): void;
|
||||
not: ShouldInternal;
|
||||
}
|
||||
|
||||
interface Internal extends ShouldInternal {
|
||||
(obj: any): ShouldAssertion;
|
||||
|
||||
// node.js's assert functions
|
||||
fail(actual: any, expected: any, message: string, operator: string): void;
|
||||
assert(value: any, message: string): void;
|
||||
ok(value: any, message?: string): void;
|
||||
equal(actual: any, expected: any, message?: string): void;
|
||||
notEqual(actual: any, expected: any, message?: string): void;
|
||||
deepEqual(actual: any, expected: any, message?: string): void;
|
||||
notDeepEqual(actual: any, expected: any, message?: string): void;
|
||||
strictEqual(actual: any, expected: any, message?: string): void;
|
||||
notStrictEqual(actual: any, expected: any, message?: string): void;
|
||||
throws(block: any, error?: any, message?: string): void;
|
||||
doesNotThrow(block: any, message?: string): void;
|
||||
ifError(value: any): void;
|
||||
inspect(value: any, obj: any): any;
|
||||
}
|
||||
|
||||
declare var should: Internal;
|
||||
declare var Should: Internal;
|
||||
interface Window {
|
||||
Should: Internal;
|
||||
}
|
||||
|
||||
declare module "should" {
|
||||
export = should;
|
||||
}
|
|
@ -1,14 +1,103 @@
|
|||
/// <reference path="../typings/node.d.ts" />
|
||||
/// <reference path="../typings/elementtree.d.ts" />
|
||||
/// <reference path="../typings/unorm.d.ts" />
|
||||
declare module TacoUtility {
|
||||
class ResourcesManager {
|
||||
private static Resources;
|
||||
private static SupportedLanguages;
|
||||
private static DefaultLanguage;
|
||||
static init(language: string, resourcesDir?: string): void;
|
||||
static teardown(): void;
|
||||
/** ...optionalArgs is only there for typings, function rest params */
|
||||
static getString(id: string, ...optionalArgs: any[]): string;
|
||||
static bestLanguageMatchOrDefault(language: string, resourcesDir: string): string;
|
||||
static loadLanguage(language: string, resourcesDir: string): any;
|
||||
/** ** ...optionalArgs is only there for typings, function rest params** */
|
||||
static getStringForLanguage(requestOrAcceptLangs: any, id: string, ...optionalArgs: any[]): string;
|
||||
private static bestLanguageMatchOrDefault(requestOrAcceptLangs);
|
||||
/**
|
||||
* requestOrAcceptLangs can either be:
|
||||
* A string, with format "LangSpec[,LangSpec]*" where LangSpec is "Language[;anything]"
|
||||
* e.g. "pl,fr-FR;q=0.3,en-US;q=0.1" is interpreted as "pl" or "fr-FR" or "en-US". Currently we ignore provided quality (q) values
|
||||
* An array, which we assume is an array of strings representing languages such as "pl" or "fr-FR"
|
||||
* A (express-style) HTTP Request object, with a headers property specifing "accept-language" in a string, as above
|
||||
*
|
||||
* This allows us to handle simple cases of a single string, as well as more complex cases where a client specifies
|
||||
* multiple preferences.
|
||||
*/
|
||||
private static bestLanguageMatch(requestOrAcceptLangs);
|
||||
/**
|
||||
* Given a list of languages, we try to find an exact match if we can, and we fall back to a primary language otherwise.
|
||||
* e.g. "fr-CA" will use "fr-CA" resources if present, but will fall back to "fr" resources if they are available
|
||||
*/
|
||||
private static getBestLanguageFromArray(acceptLangs);
|
||||
private static loadLanguage(language, resourcesDir);
|
||||
private static getOptionalArgsArrayFromFunctionCall(functionArguments, startFrom);
|
||||
}
|
||||
class CordovaConfig {
|
||||
/** CordovaConfig is a class for parsing the config.xml file for Cordova projects */
|
||||
private _doc;
|
||||
constructor(configXmlPath: string);
|
||||
id(): string;
|
||||
name(): string;
|
||||
version(): string;
|
||||
preferences(): {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
class UtilHelper {
|
||||
private static InvalidAppNameChars;
|
||||
/** Converts an untyped argument into a boolean in a "sensible" way, treating only the string "true" as true rather than any non-empty string * */
|
||||
static argToBool(input: any): boolean;
|
||||
static readFileContentsSync(filename: string, encoding?: string): string;
|
||||
static getOptionalArgsArrayFromFunctionCall(functionArguments: IArguments, startFrom: number): any[];
|
||||
}
|
||||
class BuildInfo {
|
||||
status: string;
|
||||
/**
|
||||
* BuildInfo holds relevant information about a particular build, including its identifier, what kind of build was requested, and the status of the build
|
||||
* This information is passed around both within the remote build server, and back to the client
|
||||
*/
|
||||
changeList: {
|
||||
deletedFiles: string[];
|
||||
changedFiles: string[];
|
||||
addedPlugins: string[];
|
||||
deletedPlugins: string[];
|
||||
deletedFilesIos: string[];
|
||||
changedFilesIos: string[];
|
||||
addedPluginsIos: string[];
|
||||
deletedPluginsIos: string[];
|
||||
};
|
||||
statusTime: Date;
|
||||
cordovaVersion: string;
|
||||
buildCommand: string;
|
||||
configuration: string;
|
||||
options: any;
|
||||
buildDir: string;
|
||||
buildLang: string;
|
||||
buildPlatform: string;
|
||||
submissionTime: Date;
|
||||
buildNumber: number;
|
||||
messageId: string;
|
||||
messageArgs: any[];
|
||||
message: string;
|
||||
tgzFilePath: string;
|
||||
appDir: string;
|
||||
appName: string;
|
||||
webDebugProxyPort: number;
|
||||
constructor(params: {
|
||||
buildNumber?: number;
|
||||
status?: string;
|
||||
cordovaVersion?: string;
|
||||
buildCommand?: string;
|
||||
configuration?: string;
|
||||
options?: any;
|
||||
buildDir?: string;
|
||||
buildLang?: string;
|
||||
buildPlatform?: string;
|
||||
});
|
||||
static createNewBuildInfoFromDataObject(buildInfoData: any): BuildInfo;
|
||||
updateStatus(status: string, messageId?: string, ...messageArgs: any[]): void;
|
||||
localize(req: any): BuildInfo;
|
||||
}
|
||||
}
|
||||
declare module "taco-utils"{
|
||||
export = TacoUtility;
|
|
@ -0,0 +1,12 @@
|
|||
// Barebones typing for unorm, added as-needed
|
||||
|
||||
interface Object {
|
||||
normalize: (form?: string) => string;
|
||||
}
|
||||
|
||||
declare module "unorm" {
|
||||
export function nfc(str: string): string;
|
||||
export function nfd(str: string): string;
|
||||
export function nfkc(str: string): string;
|
||||
export function nfkd(str: string): string;
|
||||
}
|
Загрузка…
Ссылка в новой задаче