Skip to content

Commit

Permalink
feat: Make translation system key based and add annotation type to gu…
Browse files Browse the repository at this point in the history
…tter icon aria labels (#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.
  • Loading branch information
akoreman authored Apr 3, 2024
1 parent 507ae2f commit bb8256d
Show file tree
Hide file tree
Showing 19 changed files with 276 additions and 151 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ lib/ace/mode/xml/*
lib/ace/mode/xquery/*
lib/ace/mode/xquery.js
lib/ace/mode/yaml/*
src/lib/default_english_messages.js
**/test/asyncjs/*
**/es5-shim.js
**/vim*.js
Expand Down
22 changes: 14 additions & 8 deletions Makefile.dryice.js
Original file line number Diff line number Diff line change
Expand Up @@ -654,28 +654,34 @@ function extractCss(callback) {
}

function extractNls() {
var allMessages = {};
var defaultData = require(__dirname + "/src/lib/default_english_messages").defaultEnglishMessages;

searchFiles(__dirname + "/src", function(path) {
if (/_test/.test(path)) return;
var text = fs.readFileSync(path, "utf8");
var matches = text.match(/nls\s*\(\s*("([^"\\]|\\.)+"|'([^'\\]|\\.)+')/g);
var matches = text.match(/nls\s*\(\s*("([^"\\]|\\.)+"|'([^'\\]|\\.)+'),\s*("([^"\\]|\\.)+"|'([^'\\]|\\.)+')/g);
matches && matches.forEach(function(m) {
var eng = m.replace(/^nls\s*\(\s*["']|["']$/g, "");
allMessages[eng] = "";
var match = m.match(/("([^"\\]|\\.)+"|'([^'\\]|\\.)+)/g);
var key = match[0].replace(/["']|["']$/g, "");
var defaultString = match[1].replace(/["']|["']$/g, "");

// If the key not yet in the default file, add it:
if (defaultData[key] !== undefined) return;
defaultData[key] = defaultString;
});
});
fs.writeFileSync(__dirname + "/src/lib/default_english_messages.js", "var defaultEnglishMessages = " + JSON.stringify(defaultData, null, 4) + "\n\nexports.defaultEnglishMessages = defaultEnglishMessages;", "utf8");

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

var newData = {$id: existing.$id};
for (var i in allMessages) {
newData[i] = existing[i] || "";
for (var i in defaultData) {
existing[i] = existing[i] || "";
}
fs.writeFileSync(path, JSON.stringify(newData, null, 4), "utf8");
fs.writeFileSync(path, JSON.stringify(existing, null, 4), "utf8");
console.log("Saved " + x);
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class Autocomplete {
}

static get completionsForLoading() { return [{
caption: config.nls("Loading..."),
caption: config.nls("autocomplete.loading", "Loading..."),
value: ""
}];
}
Expand Down
6 changes: 3 additions & 3 deletions src/autocomplete/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ class AcePopup {

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

popup.setOption("displayIndentGuides", false);
Expand Down Expand Up @@ -152,7 +152,7 @@ class AcePopup {
t.element.setAttribute("aria-activedescendant", ariaId);
el.setAttribute("aria-activedescendant", ariaId);
selected.setAttribute("role", optionAriaRole);
selected.setAttribute("aria-roledescription", nls("item"));
selected.setAttribute("aria-roledescription", nls("autocomplete.popup.item.aria-roledescription", "item"));
selected.setAttribute("aria-label", popup.getData(row).caption || popup.getData(row).value);
selected.setAttribute("aria-setsize", popup.data.length);
selected.setAttribute("aria-posinset", row + 1);
Expand Down
15 changes: 9 additions & 6 deletions src/config_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,16 @@ module.exports = {
"test: nls": function() {
var nls = config.nls;
config.setMessages({
foo: "hello world of $1"
foo: "hello world of $1",
test_key: "hello world for test key"
});
assert.equal(nls("bar $1"), "bar $1");
assert.equal(nls("bar"), "bar");
assert.equal(nls("foo"), "hello world of $1");
assert.equal(nls("foo", {1: "goo"}), "hello world of goo");
assert.equal(nls("$0B is $1$$", [0.11, 22]), "0.11B is 22$");
assert.equal(nls("untranslated_key","bar $1"), "bar $1");
assert.equal(nls("untranslated_key", "bar"), "bar");
assert.equal(nls("untranslated_key_but_translated_default_string", "foo"), "hello world of $1");
assert.equal(nls("untranslated_key_but_translated_default_string", "foo", {1: "goo"}), "hello world of goo");
assert.equal(nls("untranslated_key", "$0B is $1$$", [0.11, 22]), "0.11B is 22$");
assert.equal(nls("untranslated_key_but_translated_default_string", "foo", {1: "goo"}), "hello world of goo");
assert.equal(nls("test_key", "this text should not appear"), "hello world for test key");
},
"test: define options" : function() {
var o = {};
Expand Down
8 changes: 4 additions & 4 deletions src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2944,10 +2944,10 @@ config.defineOptions(Editor.prototype, "editor", {
this.textInput.setNumberOfExtraLines(useragent.isWin ? 3 : 0);
this.renderer.scroller.setAttribute("tabindex", 0);
this.renderer.scroller.setAttribute("role", "group");
this.renderer.scroller.setAttribute("aria-roledescription", nls("editor"));
this.renderer.scroller.setAttribute("aria-roledescription", nls("editor.scroller.aria-roledescription", "editor"));
this.renderer.scroller.classList.add(this.renderer.keyboardFocusClassName);
this.renderer.scroller.setAttribute("aria-label",
nls("Editor content, press Enter to start editing, press Escape to exit")
nls("editor.scroller.aria-label", "Editor content, press Enter to start editing, press Escape to exit")
);

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

Expand Down
2 changes: 1 addition & 1 deletion src/ext/error_marker.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ exports.showErrorMarker = function(editor, dir) {
return;
} else {
gutterAnno = {
text: [nls("Looks good!")],
text: [nls("error-marker.good-state", "Looks good!")],
className: "ace_ok"
};
}
Expand Down
6 changes: 3 additions & 3 deletions src/ext/prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,13 @@ prompt.commands = function(editor, callback) {
otherCommands = getFilteredCompletions(otherCommands, prefix);

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

var completions = recentlyUsedCommands.concat(otherCommands);
return completions.length > 0 ? completions : [{
value: nls("No matching commands"),
value: nls("prompt.no-matching-commands", "No matching commands"),
error: 1
}];
}
Expand Down
22 changes: 11 additions & 11 deletions src/ext/searchbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,24 @@ class SearchBox {
dom.buildDom(["div", {class:"ace_search right"},
["span", {action: "hide", class: "ace_searchbtn_close"}],
["div", {class: "ace_search_form"},
["input", {class: "ace_search_field", placeholder: nls("Search for"), spellcheck: "false"}],
["input", {class: "ace_search_field", placeholder: nls("search-box.find.placeholder", "Search for"), spellcheck: "false"}],
["span", {action: "findPrev", class: "ace_searchbtn prev"}, "\u200b"],
["span", {action: "findNext", class: "ace_searchbtn next"}, "\u200b"],
["span", {action: "findAll", class: "ace_searchbtn", title: "Alt-Enter"}, nls("All")]
["span", {action: "findAll", class: "ace_searchbtn", title: "Alt-Enter"}, nls("search-box.find-all.text", "All")]
],
["div", {class: "ace_replace_form"},
["input", {class: "ace_search_field", placeholder: nls("Replace with"), spellcheck: "false"}],
["span", {action: "replaceAndFindNext", class: "ace_searchbtn"}, nls("Replace")],
["span", {action: "replaceAll", class: "ace_searchbtn"}, nls("All")]
["input", {class: "ace_search_field", placeholder: nls("search-box.replace.placeholder", "Replace with"), spellcheck: "false"}],
["span", {action: "replaceAndFindNext", class: "ace_searchbtn"}, nls("search-box.replace-next.text", "Replace")],
["span", {action: "replaceAll", class: "ace_searchbtn"}, nls("search-box.replace-all.text", "All")]
],
["div", {class: "ace_search_options"},
["span", {action: "toggleReplace", class: "ace_button", title: nls("Toggle Replace mode"),
["span", {action: "toggleReplace", class: "ace_button", title: nls("search-box.toggle-replace.title", "Toggle Replace mode"),
style: "float:left;margin-top:-2px;padding:0 5px;"}, "+"],
["span", {class: "ace_search_counter"}],
["span", {action: "toggleRegexpMode", class: "ace_button", title: nls("RegExp Search")}, ".*"],
["span", {action: "toggleCaseSensitive", class: "ace_button", title: nls("CaseSensitive Search")}, "Aa"],
["span", {action: "toggleWholeWords", class: "ace_button", title: nls("Whole Word Search")}, "\\b"],
["span", {action: "searchInSelection", class: "ace_button", title: nls("Search In Selection")}, "S"]
["span", {action: "toggleRegexpMode", class: "ace_button", title: nls("search-box.toggle-regexp.title", "RegExp Search")}, ".*"],
["span", {action: "toggleCaseSensitive", class: "ace_button", title: nls("search-box.toggle-case.title", "CaseSensitive Search")}, "Aa"],
["span", {action: "toggleWholeWords", class: "ace_button", title: nls("search-box.toggle-whole-word.title", "Whole Word Search")}, "\\b"],
["span", {action: "searchInSelection", class: "ace_button", title: nls("search-box.toggle-in-selection.title", "Search In Selection")}, "S"]
]
], div);
/**@type {any}*/
Expand Down Expand Up @@ -234,7 +234,7 @@ class SearchBox {
}
}
}
this.searchCounter.textContent = nls("$0 of $1", [before , (all > MAX_COUNT ? MAX_COUNT + "+" : all)]);
this.searchCounter.textContent = nls("search-box.search-counter", "$0 of $1", [before , (all > MAX_COUNT ? MAX_COUNT + "+" : all)]);
}
findNext() {
this.find(true, false);
Expand Down
4 changes: 2 additions & 2 deletions src/keyboard/textinput.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ TextInput= function(parentNode, host) {
text.setAttribute("role", options.role);
}
if (options.setLabel) {
text.setAttribute("aria-roledescription", nls("editor"));
text.setAttribute("aria-roledescription", nls("text-input.aria-roledescription", "editor"));
if(host.session) {
var row = host.session.selection.cursor.row;
text.setAttribute("aria-label", nls("Cursor at row $0", [row + 1]));
text.setAttribute("aria-label", nls("text-input.aria-label", "Cursor at row $0", [row + 1]));
}
}
};
Expand Down
38 changes: 31 additions & 7 deletions src/layer/gutter.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,21 +409,21 @@ class Gutter{

// getFoldWidgetRange is optional to be implemented by fold modes, if not available we fall-back.
if (foldRange)
foldWidget.setAttribute("aria-label", nls("Toggle code folding, rows $0 through $1", [foldRange.start.row + 1, foldRange.end.row + 1]));
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]));
else {
if (fold)
foldWidget.setAttribute("aria-label", nls("Toggle code folding, rows $0 through $1", [fold.start.row + 1, fold.end.row + 1]));
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]));
else
foldWidget.setAttribute("aria-label", nls("Toggle code folding, row $0", [row + 1]));
foldWidget.setAttribute("aria-label", nls("gutter.code-folding.open.aria-label", "Toggle code folding, row $0", [row + 1]));
}

if (isClosedFold) {
foldWidget.setAttribute("aria-expanded", "false");
foldWidget.setAttribute("title", nls("Unfold code"));
foldWidget.setAttribute("title", nls("gutter.code-folding.closed.title", "Unfold code"));
}
else {
foldWidget.setAttribute("aria-expanded", "true");
foldWidget.setAttribute("title", nls("Fold code"));
foldWidget.setAttribute("title", nls("gutter.code-folding.open.title", "Fold code"));
}
} else {
if (foldWidget) {
Expand All @@ -442,7 +442,17 @@ class Gutter{
dom.setStyle(annotationIconNode.style, "height", lineHeight);
dom.setStyle(annotationNode.style, "display", "block");
dom.setStyle(annotationNode.style, "height", lineHeight);
annotationNode.setAttribute("aria-label", nls("Read annotations row $0", [rowText]));
var ariaLabel;
switch(foldAnnotationClass) {
case " ace_error_fold":
ariaLabel = nls("gutter.annotation.aria-label.error", "Read annotations row $0", [rowText]);
break;

case " ace_warning_fold":
ariaLabel = nls("gutter.annotation.aria-label.warning", "Read annotations row $0", [rowText]);
break;
}
annotationNode.setAttribute("aria-label", ariaLabel);
annotationNode.setAttribute("tabindex", "-1");
annotationNode.setAttribute("role", "button");
}
Expand All @@ -458,7 +468,21 @@ class Gutter{
dom.setStyle(annotationIconNode.style, "height", lineHeight);
dom.setStyle(annotationNode.style, "display", "block");
dom.setStyle(annotationNode.style, "height", lineHeight);
annotationNode.setAttribute("aria-label", nls("Read annotations row $0", [rowText]));
var ariaLabel;
switch(this.$annotations[row].className) {
case " ace_error":
ariaLabel = nls("gutter.annotation.aria-label.error", "Read annotations row $0", [rowText]);
break;

case " ace_warning":
ariaLabel = nls("gutter.annotation.aria-label.warning", "Read annotations row $0", [rowText]);
break;

case " ace_info":
ariaLabel = nls("gutter.annotation.aria-label.info", "Read annotations row $0", [rowText]);
break;
}
annotationNode.setAttribute("aria-label", ariaLabel);
annotationNode.setAttribute("tabindex", "-1");
annotationNode.setAttribute("role", "button");
}
Expand Down
2 changes: 1 addition & 1 deletion src/layer/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ class Text {
var span = this.dom.createElement("span");
if (token.type == "fold"){
span.style.width = (token.value.length * this.config.characterWidth) + "px";
span.setAttribute("title", nls("Unfold code"));
span.setAttribute("title", nls("inline-fold.closed.title", "Unfold code"));
}

span.className = classes;
Expand Down
23 changes: 15 additions & 8 deletions src/lib/app_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
var oop = require("./oop");
var EventEmitter = require("./event_emitter").EventEmitter;
const reportError = require("./report_error").reportError;
const defaultEnglishMessages = require("./default_english_messages").defaultEnglishMessages;

var optionsProvider = {
setOptions: function(optList) {
Expand Down Expand Up @@ -61,8 +62,9 @@ var messages;

class AppConfig {
constructor() {
this.$defaultOptions = {};
}
this.$defaultOptions = {};
messages = defaultEnglishMessages;
}

/**
* @param {Object} obj
Expand Down Expand Up @@ -142,14 +144,19 @@ class AppConfig {
}

/**
* @param {string} string
* @param {string} key
* @param {string} defaultString
* @param {{ [x: string]: any; }} [params]
*/
nls(string, params) {
if (messages && !messages[string]) {
warn("No message found for '" + string + "' in the provided messages, falling back to default English message.");
}
var translated = messages && messages[string] || string;
nls(key, defaultString, params) {
if (!messages[key]) {
warn("No message found for the key '" + key + "' in the provided messages, trying to find a translation for the default string '" + defaultString + "'.");
if (!messages[defaultString]) {
warn("No message found for the default string '" + defaultString + "' in the provided messages. Falling back to the default English message.");
}
}

var translated = messages[key] || messages[defaultString] || defaultString;
if (params) {
translated = translated.replace(/\$(\$|[\d]+)/g, function(_, name) {
if (name == "$") return "$";
Expand Down
Loading

0 comments on commit bb8256d

Please sign in to comment.