1. #1
    Sencha User
    Join Date
    Dec 2011
    Posts
    24
    Vote Rating
    1
    Cine is on a distinguished road

      0  

    Exclamation SDK Very unstable and super slow

    SDK Very unstable and super slow


    I have tried the SDK and found it to be extremely unstable and generally not very user friendly, not to mention horribly slow.

    The "create jsb" seems rather stable, however, it is lacking some crucial features/documentation:
    • It only works when the compile.html (with the startup) file is in the root of the build. It should be possible to hide it away inside a compile dir, so it doesnt mess up
    • It is impossible to control the debug/compress etc settings. E.g. how to build the -dev versions?
    • It is impossible to control the projectname and license text, which means that you have to edit those every time you regen your file, which in any sane build environment is every time.
    The "build" is very unstable:
    • Half the time it will build empty files (only containing the license text) without any warning
    • It will gladly ignore files that are none existing without any warning
    • It requires a very strict input format, but gives no warnings or errors when there are problems. E.g. setting options:null will result in an empty js file. Or setting a path to "xxx" instead of "xxx/" will also result in an empty js file (which is inconsistent with paths to root which is "" rather than "/").
    • It leave temp files in the build dir if anything goes wrong. Those should be in the %temp% folder.
    And it is extremely slow
    • Build of 1 small file (e.g. one of the locale files) without debug or compress will take 15 seconds. It should be near instant.
    • Many builds of small files will take forever. To build all of the locale files into their own compressed files take over 2 minutes.
    • It doesn't check for changed files, so even though the source files have not changed, the output is still generated in its extremely slow manor.

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    36,649
    Vote Rating
    817
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    For performance... it uses PhantomJS which only takes like 2 seconds to startup and I have many classes being built in 5 seconds.

    I will agree about the documentation. It has some CLI help but people these days don't see to like CLI things.

    Stability... I have never had a problem with this. It reliably creates the JSB and builds just fine for me. But I am very explicit when using my requires/uses/etc properties throughout my apps.
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha User
    Join Date
    Dec 2011
    Posts
    24
    Vote Rating
    1
    Cine is on a distinguished road

      0  

    Default


    You can try to build something like this:

    {"projectName":"locales","licenseText":"Copyright(c) 2006-2011 locales","builds":[
    {"name":"af","target":"all.af.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-af.js"}]},
    {"name":"bg","target":"all.bg.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-bg.js"}]},
    {"name":"ca","target":"all.ca.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ca.js"}]},
    {"name":"cs","target":"all.cs.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-cs.js"}]},
    {"name":"da","target":"all.da.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-da.js"}]},
    {"name":"de","target":"all.de.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-de.js"}]},
    {"name":"el-gr","target":"all.el-gr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-el_GR.js"}]},
    {"name":"en","target":"all.en.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-en.js"}]},
    {"name":"en-gb","target":"all.en-gb.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-en_GB.js"}]},
    {"name":"es","target":"all.es.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-es.js"}]},
    {"name":"fa","target":"all.fa.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fa.js"}]},
    {"name":"fi","target":"all.fi.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fi.js"}]},
    {"name":"fr","target":"all.fr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fr.js"}]},
    {"name":"fr-ca","target":"all.fr-ca.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fr_CA.js"}]},
    {"name":"el","target":"all.el.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-gr.js"}]},
    {"name":"he","target":"all.he.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-he.js"}]},
    {"name":"hr","target":"all.hr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-hr.js"}]},
    {"name":"hu","target":"all.hu.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-hu.js"}]},
    {"name":"id","target":"all.id.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-id.js"}]},
    {"name":"it","target":"all.it.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-it.js"}]},
    {"name":"ja","target":"all.ja.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ja.js"}]},
    {"name":"ko","target":"all.ko.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ko.js"}]},
    {"name":"lt","target":"all.lt.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-lt.js"}]},
    {"name":"lv","target":"all.lv.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-lv.js"}]},
    {"name":"mk","target":"all.mk.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-mk.js"}]},
    {"name":"nl","target":"all.nl.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-nl.js"}]},
    {"name":"nb-no","target":"all.nb-no.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-no_NB.js"}]},
    {"name":"nn-no","target":"all.nn-no.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-no_NN.js"}]},
    {"name":"pl","target":"all.pl.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pl.js"}]},
    {"name":"pt","target":"all.pt.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pt.js"}]},
    {"name":"pt-br","target":"all.pt-br.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pt_BR.js"}]},
    {"name":"pt-pt","target":"all.pt-pt.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pt_PT.js"}]},
    {"name":"ro","target":"all.ro.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ro.js"}]},
    {"name":"ru","target":"all.ru.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ru.js"}]},
    {"name":"sk","target":"all.sk.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sk.js"}]},
    {"name":"sl","target":"all.sl.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sl.js"}]},
    {"name":"sr","target":"all.sr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sr.js"}]},
    {"name":"sv-se","target":"all.sv-se.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sv_SE.js"}]},
    {"name":"th","target":"all.th.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-th.js"}]},
    {"name":"tr","target":"all.tr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-tr.js"}]},
    {"name":"vi","target":"all.vi.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-vn.js"}]},
    {"name":"zh-cn","target":"all.zh-cn.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-zh_CN.js"}]},
    {"name":"zh-tw","target":"all.zh-tw.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-zh_TW.js"}]}]}
    On my super fast machine it takes > 90 seconds. Compare that to this tool that I build in less than an hour to do the exact same thing, but it can do it in 8 seconds:

    class Combine{
    public static void CombineAndOptimize(string jsbPath)
    {
    Directory.SetCurrentDirectory(Path.GetDirectoryName(jsbPath));
    var json = File.ReadAllText(jsbPath);
    var serializer = new JavaScriptSerializer();


    var obj = serializer.Deserialize<JBuilder>(json);

    Stopwatch sw = Stopwatch.StartNew();

    List<JavaProcess> process = new List<JavaProcess>();
    foreach (var build in obj.builds)
    {
    var jFiles = build.files.Select(x => new FileInfo((x.path + x.name).Replace('/', '\\'))).ToList();
    var p = GenerateNewFile(jFiles, new FileInfo(build.target.Replace("min", "debug")), new FileInfo(build.target), build.options.debug);
    process.Add(p);
    }

    foreach (var javaProcess in process)
    javaProcess.Finalize();

    Console.WriteLine("Took {0} ms", sw.ElapsedMilliseconds);
    Console.ReadKey();
    }


    private static JavaProcess GenerateNewFile(List<FileInfo> input, FileInfo outputfiDebug, FileInfo outputfiOpt, bool debug)
    {
    if (outputfiDebug.Exists)
    outputfiDebug.Delete();
    foreach (var fi in input)
    {
    string readAllText = File.ReadAllText(fi.FullName);
    if (!debug)
    readAllText = Regex.Replace(readAllText, "//<debug>.*?//</debug>", "",
    RegexOptions.Compiled | RegexOptions.Singleline);
    File.AppendAllText(outputfiDebug.FullName, readAllText, Encoding.UTF8);
    }

    ProcessStartInfo si =
    //new ProcessStartInfo( //google compiler
    // @"c:\Program Files (x86)\Java\jre6\bin\java.exe",
    // "-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\compiler.jar\"" +
    // " --charset=UTF-8 --compilation_level=SIMPLE_OPTIMIZATIONS" +
    // " --js=\"" + outputfiDebug.FullName + "\"" +
    // " --js_output_file=\"" + outputfiOpt.FullName + "tmp\"" +
    // " --summary_detail_level=3 --formatting=pretty_print --jscomp_off=uselessCode"
    // )
    new ProcessStartInfo( //ycompress
    @"c:\Program Files (x86)\Java\jre6\bin\java.exe",
    "-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\ycompressor.jar\"" +
    " --type js --charset UTF-8" +
    " -o \"" + outputfiOpt.FullName + "tmp\"" +
    " \"" + outputfiDebug.FullName + "\""
    )
    {
    CreateNoWindow = true,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true
    };
    var p = Process.Start(si);
    StringBuilder err = new StringBuilder();
    StringBuilder output = new StringBuilder();
    p.OutputDataReceived += (a, b) => output.Append(b.Data);
    p.ErrorDataReceived += (a, b) => err.Append(b.Data);
    p.BeginOutputReadLine();
    p.BeginErrorReadLine();
    return new JavaProcess(p, err, output, outputfiOpt);
    }


    private class JavaProcess
    {
    private readonly Process _process;
    private readonly StringBuilder _err;
    private readonly StringBuilder _output;
    private readonly FileInfo _outputfiOpt;

    public JavaProcess(Process process, StringBuilder err, StringBuilder output, FileInfo outputfiOpt)
    {
    _process = process;
    _err = err;
    _output = output;
    _outputfiOpt = outputfiOpt;
    }

    public void Finalize()
    {
    _process.WaitForExit();
    Console.WriteLine(_err);
    Console.WriteLine(_output);
    _process.Close();
    if (_outputfiOpt.Exists)
    File.Delete(_outputfiOpt.FullName);
    File.Move(_outputfiOpt.FullName + "tmp", _outputfiOpt.FullName);
    Console.WriteLine(_outputfiOpt.FullName + " combined. Optimizing ... ");
    if (_err.Length > 0)
    Console.WriteLine(@"check (c:\Program Files (x86)\Java\jre6\bin\java.exe)");
    }

    }


    internal class JBuilder
    {
    public string projectName;
    public string licenseText;
    public List<JBuild> builds;
    }

    internal class JBuild
    {
    public string name;
    public string target;
    public JOptions options;
    public bool compress;
    public List<JFile> files;
    }

    internal class JOptions
    {
    public bool debug;
    }

    internal class JFile
    {
    public string path;
    public string name;
    }
    }
    btw. That locale dir is so ****ed up, doesn't anyone care about globalization at sencha?
    case "gr": return "el";
    case "no-nb": return "nb-no";
    case "no-nn": return "nn-no";
    case "vn": return "vi";
    case "ukr": return NOSUCHISO;
    case "sr-rs": return NOSUCHISO;
    And isnt there some way to actually keep track of translation status or tooling that the rest of use can use for our translations?

  4. #4
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    36,649
    Vote Rating
    817
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    As soon as you come back to the professionalism world... let me know so I can continue to communicate. Rants get you no where.
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  5. #5
    Sencha User
    Join Date
    Dec 2011
    Posts
    24
    Vote Rating
    1
    Cine is on a distinguished road

      0  

    Default


    And the full source.
    It is a bit specific for my needs, in that it takes the original jsb3 file from the "create jsb" and then locates translated files (in the form of originalfile.js -> originalfile.iso.js) and includes those with the sencha translations. and then builds min and debug version of all.
    Also checks for altered file dates, doing the compile in milliseconds if you didnt change anything.

    Sorry about my ranting, just a bit annoyed having to do it myself, when there is a nice SDK, just lacking some features and working rather slow.

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web.Script.Serialization;

    namespace FixupJsBuild
    {
    class Program
    {
    static void Main(string[] args)
    {
    Stopwatch sw = Stopwatch.StartNew();
    if (args.Length != 1)
    Usage();
    string jsbPath = args[0];
    var jsb = ReadJsb(jsbPath);
    FixupJsbFile(jsb);
    CombineAndOptimize(jsb);

    Console.WriteLine("Took {0} ms", sw.ElapsedMilliseconds);
    }

    private static JBuilder ReadJsb(string jsbPath)
    {
    Directory.SetCurrentDirectory(Path.GetDirectoryName(jsbPath));
    var json = File.ReadAllText(jsbPath);
    var serializer = new JavaScriptSerializer();
    return serializer.Deserialize<JBuilder>(json);
    }

    private static void CombineAndOptimize(JBuilder jsb)
    {

    List<JavaProcess> process = (from build in jsb.builds
    let jFiles = build.files.Select(x => new FileInfo((x.path + x.name).Replace('/', '\\'))).ToList()
    select GenerateNewFile(jFiles, new FileInfo(build.target.Replace("min", "debug")), new FileInfo(build.target), build.options.debug))
    .ToList();

    foreach (var javaProcess in process)
    if (javaProcess != null) javaProcess.Finalize();
    }


    private static DateTime GetLastModified(List<FileInfo> fileInfos)
    {
    DateTime lastModified = DateTime.MinValue;
    foreach (var fi in fileInfos)
    {
    if (!fi.Exists)
    {
    Console.WriteLine("Input file not found : " + fi.FullName);
    Usage();
    }
    if (fi.LastWriteTimeUtc > lastModified)
    lastModified = fi.LastWriteTimeUtc;
    }
    return lastModified;
    }

    private static void Usage()
    {
    Console.WriteLine("Usage: jsbfile");
    Environment.Exit(1);
    }

    private static JavaProcess GenerateNewFile(List<FileInfo> input, FileInfo outputfiDebug, FileInfo outputfiOpt, bool debug)
    {
    DateTime lastModified = GetLastModified(input);

    if (outputfiDebug.Exists && outputfiOpt.Exists &&
    outputfiDebug.LastWriteTimeUtc >= lastModified &&
    outputfiOpt.LastWriteTimeUtc >= lastModified)
    {
    Console.WriteLine("{0} is up to date - not modified", outputfiOpt.FullName);
    return null;
    }
    if (outputfiDebug.Exists)
    outputfiDebug.Delete();
    foreach (var fi in input)
    {
    string readAllText = File.ReadAllText(fi.FullName);
    if (!debug)
    readAllText = Regex.Replace(readAllText, "//<debug>.*?//</debug>", "",
    RegexOptions.Compiled | RegexOptions.Singleline);
    File.AppendAllText(outputfiDebug.FullName, readAllText, Encoding.UTF8);
    }

    ProcessStartInfo si =
    //new ProcessStartInfo( //google compiler
    // @"c:\Program Files (x86)\Java\jre6\bin\java.exe",
    // "-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\compiler.jar\"" +
    // " --charset=UTF-8 --compilation_level=SIMPLE_OPTIMIZATIONS" +
    // " --js=\"" + outputfiDebug.FullName + "\"" +
    // " --js_output_file=\"" + outputfiOpt.FullName + "tmp\"" +
    // " --summary_detail_level=3 --formatting=pretty_print --jscomp_off=uselessCode"
    // )
    new ProcessStartInfo( //ycompress
    @"c:\Program Files (x86)\Java\jre6\bin\java.exe",
    "-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\ycompressor.jar\"" +
    " --type js --charset UTF-8" +
    " -o \"" + outputfiOpt.FullName + "tmp\"" +
    " \"" + outputfiDebug.FullName + "\""
    )
    {
    CreateNoWindow = true,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true
    };
    var p = Process.Start(si);
    StringBuilder err = new StringBuilder();
    StringBuilder output = new StringBuilder();
    p.OutputDataReceived += (a, b) => output.Append(b.Data);
    p.ErrorDataReceived += (a, b) => err.Append(b.Data);
    p.BeginOutputReadLine();
    p.BeginErrorReadLine();
    return new JavaProcess(p, err, output, outputfiOpt);
    }

    private class JavaProcess
    {
    private readonly Process _process;
    private readonly StringBuilder _err;
    private readonly StringBuilder _output;
    private readonly FileInfo _outputfiOpt;

    public JavaProcess(Process process, StringBuilder err, StringBuilder output, FileInfo outputfiOpt)
    {
    _process = process;
    _err = err;
    _output = output;
    _outputfiOpt = outputfiOpt;
    }

    public void Finalize()
    {
    _process.WaitForExit();
    Console.WriteLine(_err);
    Console.WriteLine(_output);
    _process.Close();
    if (_outputfiOpt.Exists)
    File.Delete(_outputfiOpt.FullName);
    File.Move(_outputfiOpt.FullName + "tmp", _outputfiOpt.FullName);
    Console.WriteLine(_outputfiOpt.FullName + " combined. Optimizing ... ");
    if (_err.Length > 0)
    Console.WriteLine(@"check (c:\Program Files (x86)\Java\jre6\bin\java.exe)");
    }

    }

    private static void FixupJsbFile(JBuilder jsb)
    {
    jsb.projectName = "Catglobe";
    jsb.licenseText = "Copyright(c) 2006-2011 Catglobe";

    //remove non existing file, it always wants to add
    jsb.builds = new List<JBuild> {jsb.builds[0]};
    jsb.builds[0].target = "all.min.js";
    jsb.builds[0].options.debug = false;
    //jsb.builds.Add(new JBuild
    // {
    // compress = true,
    // name = "optim",
    // options = new JOptions {debug = false},
    // target = "all.min.js",
    // files = new List<JFile>
    // {
    // new JFile {name = "all.debug.js", path = string.Empty}
    // }
    // });

    var translateableFiles = jsb.builds[0].files.Where(x => !x.path.StartsWith("Scripts/Ext/src")).ToList();
    var pathswithpossibletranslations = translateableFiles.Select(x => x.path).Distinct();
    var translatedFiles = new Dictionary<string, List<JFile>>();

    const string scriptsExtLocale = "Scripts/Ext/locale";
    foreach (var extlocale in Directory.EnumerateFiles(scriptsExtLocale, "*.js"))
    {
    var split = extlocale.Replace(".js", "").Split('-');
    var locale = FindCorrectlocale(split[split.Length - 1]);
    try
    {
    CultureInfo.GetCultureInfo(locale);
    }
    catch (CultureNotFoundException)
    {
    continue;
    }
    translatedFiles[locale] = new List<JFile>
    {
    new JFile
    {
    name = Path.GetFileName(extlocale),
    path = Path.GetDirectoryName(extlocale).Replace('\\', '/') + '/',
    }
    };
    }

    foreach (var pathwithpossibletranslations in pathswithpossibletranslations)
    {
    var filecollection = Directory.EnumerateFiles(pathwithpossibletranslations, "*.js")
    .Select(x => x.ToLowerInvariant()).ToList();

    //we should now have a list of files named something like hest.da.js
    foreach (var file in filecollection)
    {
    var split = file.Split('.');
    if (split.Length < 2) continue;
    var locale = split[split.Length - 2];
    List<JFile> d;
    if (!translatedFiles.TryGetValue(locale, out d))
    continue;
    d.Add(new JFile
    {
    name = Path.GetFileName(file),
    path = Path.GetDirectoryName(file).Replace('\\', '/'),
    });
    }
    }

    foreach (var translatedFile in translatedFiles)
    {
    string locale = translatedFile.Key;
    jsb.builds.Add(new JBuild
    {
    compress = true,
    name = locale,
    target = "all." + locale + ".min.js",
    options = new JOptions(),
    files = translatedFile.Value
    });
    }

    //json = serializer.Serialize(obj);
    //File.WriteAllText(jsbPath, json);
    }

    private static string FindCorrectlocale(string locale)
    {
    string correctlocale = locale.Replace("_", "-").ToLowerInvariant();
    switch (correctlocale)
    {
    case "gr": return "el";
    case "no-nb": return "nb-no";
    case "no-nn": return "nn-no";
    case "vn": return "vi";

    }
    return correctlocale;
    }

    // ReSharper disable InconsistentNaming
    // ReSharper disable NotAccessedField.Local
    // ReSharper disable ClassNeverInstantiated.Local
    private class JBuilder
    {
    public string projectName;
    public string licenseText;
    public List<JBuild> builds;
    }

    private class JBuild
    {
    public string name;
    public string target;
    public JOptions options;
    public bool compress;
    public List<JFile> files;
    }

    private class JOptions
    {
    public bool debug;
    }

    private class JFile
    {
    public string path;
    public string name;
    }
    // ReSharper restore ClassNeverInstantiated.Local
    // ReSharper restore NotAccessedField.Local
    // ReSharper restore InconsistentNaming
    }

    }

Thread Participants: 1