]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/messages.jsm
36fead2c19abd57265800d202bf0e6bfbb85ae4d
[dactyl.git] / common / modules / messages.jsm
1 // Copyright (c) 2011-2013 Kris Maglione <maglione.k@gmail.com>
2 //
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
5 "use strict";
6
7 defineModule("messages", {
8     exports: ["Messages", "messages", "_"],
9     require: ["services", "util"]
10 });
11
12 var Messages = Module("messages", {
13
14     init: function init(name = "messages") {
15         let self = this;
16         this.name = name;
17
18         this._ = Class("_", String, {
19             init: function _(message) {
20                 this.args = arguments;
21             },
22             instance: {},
23             message: Class.Memoize(function () {
24                 let message = this.args[0];
25
26                 if (this.args.length > 1) {
27                     let args = Array.slice(this.args, 1);
28                     return self.format(message + "-" + args.length, args, null) || self.format(message, args);
29                 }
30                 return self.get(message);
31             }),
32             valueOf: function valueOf() this.message,
33             toString: function toString() this.message
34         });
35     },
36
37     cleanup: function cleanup() {
38         services.stringBundle.flushBundles();
39     },
40
41     bundles: Class.Memoize(function ()
42         array.uniq([JSMLoader.getTarget("dactyl://locale/" + this.name + ".properties"),
43                     JSMLoader.getTarget("dactyl://locale-local/" + this.name + ".properties"),
44                     "resource://dactyl-locale/en-US/" + this.name + ".properties",
45                     "resource://dactyl-locale-local/en-US/" + this.name + ".properties"],
46                    true)
47              .map(services.stringBundle.createBundle)
48              .filter(function (bundle) { try { bundle.getSimpleEnumeration(); return true; } catch (e) { return false; } })),
49
50     iterate: function () {
51         let seen = {};
52         for (let bundle in values(this.bundles))
53             for (let { key, value } in iter(bundle.getSimpleEnumeration(), Ci.nsIPropertyElement))
54                 if (!Set.add(seen, key))
55                     yield [key, value];
56     },
57
58     get: function get(value, default_) {
59         for (let bundle in values(this.bundles))
60             try {
61                 let res = bundle.GetStringFromName(value);
62                 if (res.slice(0, 2) == "+ ")
63                     return res.slice(2).replace(/\s+/g, " ");
64                 return res;
65             }
66             catch (e) {}
67
68         // Report error so tests fail, but don't throw
69         if (arguments.length < 2) // Do *not* localize these strings
70             util.reportError(Error("Invalid locale string: " + value));
71         return arguments.length > 1 ? default_ : value;
72     },
73
74     format: function format(value, args, default_) {
75         for (let bundle in values(this.bundles))
76             try {
77                 let res = bundle.formatStringFromName(value, args, args.length);
78                 if (res.slice(0, 2) == "+ ")
79                     return res.slice(2).replace(/\s+/g, " ");
80                 return res;
81             }
82             catch (e) {}
83
84         // Report error so tests fail, but don't throw
85         if (arguments.length < 3) // Do *not* localize these strings
86             util.reportError(Error("Invalid locale string: " + value));
87         return arguments.length > 2 ? default_ : value;
88     },
89
90     /**
91      * Exports known localizable strings to a properties file.
92      *
93      * @param {string|nsIFile} {file} The file to which to export
94      *      the strings.
95      */
96     export: function export_(file) {
97         let { Buffer, commands, hints, io, mappings, modes, options, sanitizer } = overlay.activeModules;
98         file = io.File(file);
99
100         function properties(base, iter_, prop = "description") iter(function _properties() {
101             function key(...args) [base, obj.identifier || obj.name].concat(args).join(".").replace(/[\\:=]/g, "\\$&");
102
103             for (var obj in iter_) {
104                 if (!obj.hive || obj.hive.name !== "user") {
105                     yield key(prop) + " = " + obj[prop];
106
107                     if (iter_.values)
108                         for (let [k, v] in isArray(obj.values) ? array.iterValues(obj.values) : iter(obj.values))
109                             yield key("values", k) + " = " + v;
110
111                     for (let opt in values(obj.options))
112                         yield key("options", opt.names[0]) + " = " + opt.description;
113
114                     if (obj.deprecated)
115                         yield key("deprecated") + " = " + obj.deprecated;
116                 }
117             }
118         }()).toArray();
119
120         file.write(
121             array(commands.allHives.map(h => properties("command", h)))
122                           .concat(modes.all.map(m =>
123                               properties("map", values(mappings.builtin.getStack(m)
124                                                                .filter(map => map.modes[0] == m)))))
125                           .concat(properties("mode", values(modes.all.filter(m => !m.hidden))))
126                           .concat(properties("option", options))
127                           .concat(properties("hintmode", values(hints.modes), "prompt"))
128                           .concat(properties("pageinfo", values(Buffer.pageInfo), "title"))
129                           .concat(properties("sanitizeitem", values(sanitizer.itemMap)))
130                 .flatten().uniq().join("\n"));
131     }
132 }, {
133     Localized: Class("Localized", Class.Property, {
134         init: function init(prop, obj) {
135             let _prop = "unlocalized_" + prop;
136             if (this.initialized) {
137                 /*
138                 if (config.locale === "en-US")
139                     return { configurable: true, enumerable: true, value: this.default, writable: true };
140                 */
141
142                 if (!Set.has(obj, "localizedProperties"))
143                     obj.localizedProperties = { __proto__: obj.localizedProperties };
144                 obj.localizedProperties[prop] = true;
145
146                 obj[_prop] = this.default;
147                 return {
148                     get: function get() {
149                         let value = this[_prop];
150
151                         function getter(key, default_) function getter() messages.get([name, key].join("."), default_);
152
153                         if (value != null) {
154                             var name = [this.constructor.className.toLowerCase(),
155                                         this.identifier || this.name,
156                                         prop].join(".");
157
158                             if (!isObject(value))
159                                 value = messages.get(name, value);
160                             else if (isArray(value))
161                                 // Deprecated
162                                 iter(value).forEach(function ([k, v]) {
163                                     if (isArray(v))
164                                         memoize(v, 1, getter(v[0], v[1]));
165                                     else
166                                         memoize(value, k, getter(k, v));
167                                 });
168                             else
169                                 iter(value).forEach(function ([k, v]) {
170                                     memoize(value, k, () => messages.get([name, k].join("."), v));
171                                 });
172                         }
173
174                         return Class.replaceProperty(this, prop, value);
175                     },
176
177                     set: function set(val) this[_prop] = val
178                 };
179             }
180             this.default = prop;
181             this.initialized = true;
182         }
183     })
184 }, {
185     javascript: function initJavascript(dactyl, modules, window) {
186         let { JavaScript } = modules;
187
188         JavaScript.setCompleter([this._, this.get, this.format], [
189             context => messages.iterate()
190         ]);
191
192         JavaScript.setCompleter([this.export],
193             [function (context, obj, args) {
194                 context.quote[2] = "";
195                 modules.completion.file(context, true);
196             }]);
197     }
198 });
199
200 var { _ } = messages;
201
202 endModule();
203
204 // catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
205
206 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: