Skip to content

Commit bb8256d

Browse files
authored
feat: Make translation system key based and add annotation type to gutter icon aria labels (ajaxorg#5524)
Changing the API we have for UI string translations from being based on the default value of the string to a more abstract key.
1 parent 507ae2f commit bb8256d

19 files changed

+276
-151
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ lib/ace/mode/xml/*
99
lib/ace/mode/xquery/*
1010
lib/ace/mode/xquery.js
1111
lib/ace/mode/yaml/*
12+
src/lib/default_english_messages.js
1213
**/test/asyncjs/*
1314
**/es5-shim.js
1415
**/vim*.js

Makefile.dryice.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -654,28 +654,34 @@ function extractCss(callback) {
654654
}
655655

656656
function extractNls() {
657-
var allMessages = {};
657+
var defaultData = require(__dirname + "/src/lib/default_english_messages").defaultEnglishMessages;
658+
658659
searchFiles(__dirname + "/src", function(path) {
659660
if (/_test/.test(path)) return;
660661
var text = fs.readFileSync(path, "utf8");
661-
var matches = text.match(/nls\s*\(\s*("([^"\\]|\\.)+"|'([^'\\]|\\.)+')/g);
662+
var matches = text.match(/nls\s*\(\s*("([^"\\]|\\.)+"|'([^'\\]|\\.)+'),\s*("([^"\\]|\\.)+"|'([^'\\]|\\.)+')/g);
662663
matches && matches.forEach(function(m) {
663-
var eng = m.replace(/^nls\s*\(\s*["']|["']$/g, "");
664-
allMessages[eng] = "";
664+
var match = m.match(/("([^"\\]|\\.)+"|'([^'\\]|\\.)+)/g);
665+
var key = match[0].replace(/["']|["']$/g, "");
666+
var defaultString = match[1].replace(/["']|["']$/g, "");
667+
668+
// If the key not yet in the default file, add it:
669+
if (defaultData[key] !== undefined) return;
670+
defaultData[key] = defaultString;
665671
});
666672
});
673+
fs.writeFileSync(__dirname + "/src/lib/default_english_messages.js", "var defaultEnglishMessages = " + JSON.stringify(defaultData, null, 4) + "\n\nexports.defaultEnglishMessages = defaultEnglishMessages;", "utf8");
667674

668675
fs.readdirSync(__dirname + "/translations").forEach(function(x) {
669676
if (!/\.json$/.test(x)) return;
670677
var path = __dirname + "/translations/" + x;
671678
var existingStr = fs.readFileSync(path, "utf8");
672679
var existing = JSON.parse(existingStr);
673680

674-
var newData = {$id: existing.$id};
675-
for (var i in allMessages) {
676-
newData[i] = existing[i] || "";
681+
for (var i in defaultData) {
682+
existing[i] = existing[i] || "";
677683
}
678-
fs.writeFileSync(path, JSON.stringify(newData, null, 4), "utf8");
684+
fs.writeFileSync(path, JSON.stringify(existing, null, 4), "utf8");
679685
console.log("Saved " + x);
680686
});
681687
}

src/autocomplete.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class Autocomplete {
121121
}
122122

123123
static get completionsForLoading() { return [{
124-
caption: config.nls("Loading..."),
124+
caption: config.nls("autocomplete.loading", "Loading..."),
125125
value: ""
126126
}];
127127
}

src/autocomplete/popup.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ class AcePopup {
6262

6363
// Set aria attributes for the popup
6464
popup.renderer.$textLayer.element.setAttribute("role", popupAriaRole);
65-
popup.renderer.$textLayer.element.setAttribute("aria-roledescription", nls("Autocomplete suggestions"));
66-
popup.renderer.$textLayer.element.setAttribute("aria-label", nls("Autocomplete suggestions"));
65+
popup.renderer.$textLayer.element.setAttribute("aria-roledescription", nls("autocomplete.popup.aria-roledescription", "Autocomplete suggestions"));
66+
popup.renderer.$textLayer.element.setAttribute("aria-label", nls("autocomplete.popup.aria-label", "Autocomplete suggestions"));
6767
popup.renderer.textarea.setAttribute("aria-hidden", "true");
6868

6969
popup.setOption("displayIndentGuides", false);
@@ -152,7 +152,7 @@ class AcePopup {
152152
t.element.setAttribute("aria-activedescendant", ariaId);
153153
el.setAttribute("aria-activedescendant", ariaId);
154154
selected.setAttribute("role", optionAriaRole);
155-
selected.setAttribute("aria-roledescription", nls("item"));
155+
selected.setAttribute("aria-roledescription", nls("autocomplete.popup.item.aria-roledescription", "item"));
156156
selected.setAttribute("aria-label", popup.getData(row).caption || popup.getData(row).value);
157157
selected.setAttribute("aria-setsize", popup.data.length);
158158
selected.setAttribute("aria-posinset", row + 1);

src/config_test.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,16 @@ module.exports = {
5252
"test: nls": function() {
5353
var nls = config.nls;
5454
config.setMessages({
55-
foo: "hello world of $1"
55+
foo: "hello world of $1",
56+
test_key: "hello world for test key"
5657
});
57-
assert.equal(nls("bar $1"), "bar $1");
58-
assert.equal(nls("bar"), "bar");
59-
assert.equal(nls("foo"), "hello world of $1");
60-
assert.equal(nls("foo", {1: "goo"}), "hello world of goo");
61-
assert.equal(nls("$0B is $1$$", [0.11, 22]), "0.11B is 22$");
58+
assert.equal(nls("untranslated_key","bar $1"), "bar $1");
59+
assert.equal(nls("untranslated_key", "bar"), "bar");
60+
assert.equal(nls("untranslated_key_but_translated_default_string", "foo"), "hello world of $1");
61+
assert.equal(nls("untranslated_key_but_translated_default_string", "foo", {1: "goo"}), "hello world of goo");
62+
assert.equal(nls("untranslated_key", "$0B is $1$$", [0.11, 22]), "0.11B is 22$");
63+
assert.equal(nls("untranslated_key_but_translated_default_string", "foo", {1: "goo"}), "hello world of goo");
64+
assert.equal(nls("test_key", "this text should not appear"), "hello world for test key");
6265
},
6366
"test: define options" : function() {
6467
var o = {};

src/editor.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -2944,10 +2944,10 @@ config.defineOptions(Editor.prototype, "editor", {
29442944
this.textInput.setNumberOfExtraLines(useragent.isWin ? 3 : 0);
29452945
this.renderer.scroller.setAttribute("tabindex", 0);
29462946
this.renderer.scroller.setAttribute("role", "group");
2947-
this.renderer.scroller.setAttribute("aria-roledescription", nls("editor"));
2947+
this.renderer.scroller.setAttribute("aria-roledescription", nls("editor.scroller.aria-roledescription", "editor"));
29482948
this.renderer.scroller.classList.add(this.renderer.keyboardFocusClassName);
29492949
this.renderer.scroller.setAttribute("aria-label",
2950-
nls("Editor content, press Enter to start editing, press Escape to exit")
2950+
nls("editor.scroller.aria-label", "Editor content, press Enter to start editing, press Escape to exit")
29512951
);
29522952

29532953
this.renderer.scroller.addEventListener("keyup", focusOnEnterKeyup.bind(this));
@@ -2956,9 +2956,9 @@ config.defineOptions(Editor.prototype, "editor", {
29562956
this.renderer.$gutter.setAttribute("tabindex", 0);
29572957
this.renderer.$gutter.setAttribute("aria-hidden", false);
29582958
this.renderer.$gutter.setAttribute("role", "group");
2959-
this.renderer.$gutter.setAttribute("aria-roledescription", nls("editor"));
2959+
this.renderer.$gutter.setAttribute("aria-roledescription", nls("editor.gutter.aria-roledescription", "editor"));
29602960
this.renderer.$gutter.setAttribute("aria-label",
2961-
nls("Editor gutter, press Enter to interact with controls using arrow keys, press Escape to exit")
2961+
nls("editor.gutter.aria-label", "Editor gutter, press Enter to interact with controls using arrow keys, press Escape to exit")
29622962
);
29632963
this.renderer.$gutter.classList.add(this.renderer.keyboardFocusClassName);
29642964

src/ext/error_marker.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ exports.showErrorMarker = function(editor, dir) {
9898
return;
9999
} else {
100100
gutterAnno = {
101-
text: [nls("Looks good!")],
101+
text: [nls("error-marker.good-state", "Looks good!")],
102102
className: "ace_ok"
103103
};
104104
}

src/ext/prompt.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -441,13 +441,13 @@ prompt.commands = function(editor, callback) {
441441
otherCommands = getFilteredCompletions(otherCommands, prefix);
442442

443443
if (recentlyUsedCommands.length && otherCommands.length) {
444-
recentlyUsedCommands[0].message = nls("Recently used");
445-
otherCommands[0].message = nls("Other commands");
444+
recentlyUsedCommands[0].message = nls("prompt.recently-used", "Recently used");
445+
otherCommands[0].message = nls("prompt.other-commands", "Other commands");
446446
}
447447

448448
var completions = recentlyUsedCommands.concat(otherCommands);
449449
return completions.length > 0 ? completions : [{
450-
value: nls("No matching commands"),
450+
value: nls("prompt.no-matching-commands", "No matching commands"),
451451
error: 1
452452
}];
453453
}

src/ext/searchbox.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,24 @@ class SearchBox {
2727
dom.buildDom(["div", {class:"ace_search right"},
2828
["span", {action: "hide", class: "ace_searchbtn_close"}],
2929
["div", {class: "ace_search_form"},
30-
["input", {class: "ace_search_field", placeholder: nls("Search for"), spellcheck: "false"}],
30+
["input", {class: "ace_search_field", placeholder: nls("search-box.find.placeholder", "Search for"), spellcheck: "false"}],
3131
["span", {action: "findPrev", class: "ace_searchbtn prev"}, "\u200b"],
3232
["span", {action: "findNext", class: "ace_searchbtn next"}, "\u200b"],
33-
["span", {action: "findAll", class: "ace_searchbtn", title: "Alt-Enter"}, nls("All")]
33+
["span", {action: "findAll", class: "ace_searchbtn", title: "Alt-Enter"}, nls("search-box.find-all.text", "All")]
3434
],
3535
["div", {class: "ace_replace_form"},
36-
["input", {class: "ace_search_field", placeholder: nls("Replace with"), spellcheck: "false"}],
37-
["span", {action: "replaceAndFindNext", class: "ace_searchbtn"}, nls("Replace")],
38-
["span", {action: "replaceAll", class: "ace_searchbtn"}, nls("All")]
36+
["input", {class: "ace_search_field", placeholder: nls("search-box.replace.placeholder", "Replace with"), spellcheck: "false"}],
37+
["span", {action: "replaceAndFindNext", class: "ace_searchbtn"}, nls("search-box.replace-next.text", "Replace")],
38+
["span", {action: "replaceAll", class: "ace_searchbtn"}, nls("search-box.replace-all.text", "All")]
3939
],
4040
["div", {class: "ace_search_options"},
41-
["span", {action: "toggleReplace", class: "ace_button", title: nls("Toggle Replace mode"),
41+
["span", {action: "toggleReplace", class: "ace_button", title: nls("search-box.toggle-replace.title", "Toggle Replace mode"),
4242
style: "float:left;margin-top:-2px;padding:0 5px;"}, "+"],
4343
["span", {class: "ace_search_counter"}],
44-
["span", {action: "toggleRegexpMode", class: "ace_button", title: nls("RegExp Search")}, ".*"],
45-
["span", {action: "toggleCaseSensitive", class: "ace_button", title: nls("CaseSensitive Search")}, "Aa"],
46-
["span", {action: "toggleWholeWords", class: "ace_button", title: nls("Whole Word Search")}, "\\b"],
47-
["span", {action: "searchInSelection", class: "ace_button", title: nls("Search In Selection")}, "S"]
44+
["span", {action: "toggleRegexpMode", class: "ace_button", title: nls("search-box.toggle-regexp.title", "RegExp Search")}, ".*"],
45+
["span", {action: "toggleCaseSensitive", class: "ace_button", title: nls("search-box.toggle-case.title", "CaseSensitive Search")}, "Aa"],
46+
["span", {action: "toggleWholeWords", class: "ace_button", title: nls("search-box.toggle-whole-word.title", "Whole Word Search")}, "\\b"],
47+
["span", {action: "searchInSelection", class: "ace_button", title: nls("search-box.toggle-in-selection.title", "Search In Selection")}, "S"]
4848
]
4949
], div);
5050
/**@type {any}*/
@@ -234,7 +234,7 @@ class SearchBox {
234234
}
235235
}
236236
}
237-
this.searchCounter.textContent = nls("$0 of $1", [before , (all > MAX_COUNT ? MAX_COUNT + "+" : all)]);
237+
this.searchCounter.textContent = nls("search-box.search-counter", "$0 of $1", [before , (all > MAX_COUNT ? MAX_COUNT + "+" : all)]);
238238
}
239239
findNext() {
240240
this.find(true, false);

src/keyboard/textinput.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ TextInput= function(parentNode, host) {
8787
text.setAttribute("role", options.role);
8888
}
8989
if (options.setLabel) {
90-
text.setAttribute("aria-roledescription", nls("editor"));
90+
text.setAttribute("aria-roledescription", nls("text-input.aria-roledescription", "editor"));
9191
if(host.session) {
9292
var row = host.session.selection.cursor.row;
93-
text.setAttribute("aria-label", nls("Cursor at row $0", [row + 1]));
93+
text.setAttribute("aria-label", nls("text-input.aria-label", "Cursor at row $0", [row + 1]));
9494
}
9595
}
9696
};

src/layer/gutter.js

+31-7
Original file line numberDiff line numberDiff line change
@@ -409,21 +409,21 @@ class Gutter{
409409

410410
// getFoldWidgetRange is optional to be implemented by fold modes, if not available we fall-back.
411411
if (foldRange)
412-
foldWidget.setAttribute("aria-label", nls("Toggle code folding, rows $0 through $1", [foldRange.start.row + 1, foldRange.end.row + 1]));
412+
foldWidget.setAttribute("aria-label", nls("gutter.code-folding.range.aria-label", "Toggle code folding, rows $0 through $1", [foldRange.start.row + 1, foldRange.end.row + 1]));
413413
else {
414414
if (fold)
415-
foldWidget.setAttribute("aria-label", nls("Toggle code folding, rows $0 through $1", [fold.start.row + 1, fold.end.row + 1]));
415+
foldWidget.setAttribute("aria-label", nls("gutter.code-folding.closed.aria-label", "Toggle code folding, rows $0 through $1", [fold.start.row + 1, fold.end.row + 1]));
416416
else
417-
foldWidget.setAttribute("aria-label", nls("Toggle code folding, row $0", [row + 1]));
417+
foldWidget.setAttribute("aria-label", nls("gutter.code-folding.open.aria-label", "Toggle code folding, row $0", [row + 1]));
418418
}
419419

420420
if (isClosedFold) {
421421
foldWidget.setAttribute("aria-expanded", "false");
422-
foldWidget.setAttribute("title", nls("Unfold code"));
422+
foldWidget.setAttribute("title", nls("gutter.code-folding.closed.title", "Unfold code"));
423423
}
424424
else {
425425
foldWidget.setAttribute("aria-expanded", "true");
426-
foldWidget.setAttribute("title", nls("Fold code"));
426+
foldWidget.setAttribute("title", nls("gutter.code-folding.open.title", "Fold code"));
427427
}
428428
} else {
429429
if (foldWidget) {
@@ -442,7 +442,17 @@ class Gutter{
442442
dom.setStyle(annotationIconNode.style, "height", lineHeight);
443443
dom.setStyle(annotationNode.style, "display", "block");
444444
dom.setStyle(annotationNode.style, "height", lineHeight);
445-
annotationNode.setAttribute("aria-label", nls("Read annotations row $0", [rowText]));
445+
var ariaLabel;
446+
switch(foldAnnotationClass) {
447+
case " ace_error_fold":
448+
ariaLabel = nls("gutter.annotation.aria-label.error", "Read annotations row $0", [rowText]);
449+
break;
450+
451+
case " ace_warning_fold":
452+
ariaLabel = nls("gutter.annotation.aria-label.warning", "Read annotations row $0", [rowText]);
453+
break;
454+
}
455+
annotationNode.setAttribute("aria-label", ariaLabel);
446456
annotationNode.setAttribute("tabindex", "-1");
447457
annotationNode.setAttribute("role", "button");
448458
}
@@ -458,7 +468,21 @@ class Gutter{
458468
dom.setStyle(annotationIconNode.style, "height", lineHeight);
459469
dom.setStyle(annotationNode.style, "display", "block");
460470
dom.setStyle(annotationNode.style, "height", lineHeight);
461-
annotationNode.setAttribute("aria-label", nls("Read annotations row $0", [rowText]));
471+
var ariaLabel;
472+
switch(this.$annotations[row].className) {
473+
case " ace_error":
474+
ariaLabel = nls("gutter.annotation.aria-label.error", "Read annotations row $0", [rowText]);
475+
break;
476+
477+
case " ace_warning":
478+
ariaLabel = nls("gutter.annotation.aria-label.warning", "Read annotations row $0", [rowText]);
479+
break;
480+
481+
case " ace_info":
482+
ariaLabel = nls("gutter.annotation.aria-label.info", "Read annotations row $0", [rowText]);
483+
break;
484+
}
485+
annotationNode.setAttribute("aria-label", ariaLabel);
462486
annotationNode.setAttribute("tabindex", "-1");
463487
annotationNode.setAttribute("role", "button");
464488
}

src/layer/text.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ class Text {
418418
var span = this.dom.createElement("span");
419419
if (token.type == "fold"){
420420
span.style.width = (token.value.length * this.config.characterWidth) + "px";
421-
span.setAttribute("title", nls("Unfold code"));
421+
span.setAttribute("title", nls("inline-fold.closed.title", "Unfold code"));
422422
}
423423

424424
span.className = classes;

src/lib/app_config.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
var oop = require("./oop");
33
var EventEmitter = require("./event_emitter").EventEmitter;
44
const reportError = require("./report_error").reportError;
5+
const defaultEnglishMessages = require("./default_english_messages").defaultEnglishMessages;
56

67
var optionsProvider = {
78
setOptions: function(optList) {
@@ -61,8 +62,9 @@ var messages;
6162

6263
class AppConfig {
6364
constructor() {
64-
this.$defaultOptions = {};
65-
}
65+
this.$defaultOptions = {};
66+
messages = defaultEnglishMessages;
67+
}
6668

6769
/**
6870
* @param {Object} obj
@@ -142,14 +144,19 @@ class AppConfig {
142144
}
143145

144146
/**
145-
* @param {string} string
147+
* @param {string} key
148+
* @param {string} defaultString
146149
* @param {{ [x: string]: any; }} [params]
147150
*/
148-
nls(string, params) {
149-
if (messages && !messages[string]) {
150-
warn("No message found for '" + string + "' in the provided messages, falling back to default English message.");
151-
}
152-
var translated = messages && messages[string] || string;
151+
nls(key, defaultString, params) {
152+
if (!messages[key]) {
153+
warn("No message found for the key '" + key + "' in the provided messages, trying to find a translation for the default string '" + defaultString + "'.");
154+
if (!messages[defaultString]) {
155+
warn("No message found for the default string '" + defaultString + "' in the provided messages. Falling back to the default English message.");
156+
}
157+
}
158+
159+
var translated = messages[key] || messages[defaultString] || defaultString;
153160
if (params) {
154161
translated = translated.replace(/\$(\$|[\d]+)/g, function(_, name) {
155162
if (name == "$") return "$";

0 commit comments

Comments
 (0)