Skip to content

Commit 9fedcc3

Browse files
authored
Merge pull request #129 from WebCoder49/i18n-contents
Add internationalisation features
2 parents 9cf2338 + ab71c60 commit 9fedcc3

File tree

9 files changed

+369
-44
lines changed

9 files changed

+369
-44
lines changed

code-input.css

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,23 @@ code-input textarea, code-input pre, code-input pre * {
6666
font-family: inherit!important;
6767
line-height: inherit!important;
6868
tab-size: inherit!important;
69+
text-align: inherit!important;
6970
}
7071

72+
/* Make changing the text direction propogate */
73+
code-input textarea[dir=auto] + pre {
74+
unicode-bidi: plaintext;
75+
}
76+
77+
code-input textarea[dir=ltr] + pre {
78+
direction: ltr;
79+
}
80+
81+
code-input textarea[dir=rtl] + pre {
82+
direction: rtl;
83+
}
84+
85+
7186
code-input textarea, code-input pre {
7287
/* In the same place */
7388
grid-column: 1;
@@ -138,37 +153,52 @@ code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) texta
138153

139154
/* Contains dialog boxes that might appear as the result of a plugin.
140155
Sticks to the top of the code-input element */
156+
141157
code-input .code-input_dialog-container {
142158
z-index: 2;
143159

144160
position: sticky;
145161
grid-row: 1;
146162
grid-column: 1;
147163

148-
top: 0px;
164+
top: 0;
149165
left: 0;
166+
167+
margin: 0;
168+
padding: 0;
150169
width: 100%;
151170
height: 0;
152171

153-
/* Dialog boxes' text is left-aligned */
154-
text-align: left;
172+
/* Dialog boxes' text is based on text-direction */
173+
text-align: inherit;
155174
}
175+
[dir=rtl] code-input .code-input_dialog-container, code-input[dir=rtl] .code-input_dialog-container {
176+
left: unset;
177+
right: 0;
178+
}
179+
156180
/* Instructions specific to keyboard navigation set by plugins that override Tab functionality. */
157181
code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions {
158182
top: 0;
159-
right: 0;
183+
left: 0;
184+
160185
display: block;
161186
position: absolute;
162187
background-color: black;
163188
color: white;
164189
padding: 2px;
165190
padding-left: 10px;
166-
text-wrap: pretty;
191+
margin: 0;
192+
text-wrap: balance;
167193
overflow: hidden;
168194
text-overflow: ellipsis;
169195
width: calc(100% - 12px);
170196
max-height: 3em;
171197
}
198+
code-input:has(pre[dir=rtl]) .code-input_dialog-container .code-input_keyboard-navigation-instructions {
199+
left: unset;
200+
right: 0;
201+
}
172202

173203
code-input:not(:has(textarea:focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions,
174204
code-input.code-input_mouse-focused .code-input_dialog-container .code-input_keyboard-navigation-instructions,
@@ -182,4 +212,4 @@ code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(tex
182212
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code,
183213
code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre {
184214
padding-top: calc(var(--padding) + 3em)!important;
185-
}
215+
}

code-input.d.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,36 @@ export namespace plugins {
132132
* Create a find-and-replace command plugin to pass into a template
133133
* @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`.
134134
* @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`.
135+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text.
135136
*/
136-
constructor(useCtrlF?: boolean, useCtrlH?: boolean);
137+
constructor(useCtrlF?: boolean, useCtrlH?: boolean,
138+
instructionTranslations?: {
139+
start?: string;
140+
none?: string;
141+
oneFound?: string;
142+
matchIndex?: (index: Number, count: Number) => string;
143+
error?: (message: string) => string;
144+
infiniteLoopError?: string;
145+
closeDialog?: string;
146+
findPlaceholder?: string;
147+
findCaseSensitive?: string;
148+
findRegExp?: string;
149+
replaceTitle?: string;
150+
replacePlaceholder?: string;
151+
findNext?: string;
152+
findPrevious?: string;
153+
replaceActionShort?: string;
154+
replaceAction?: string;
155+
replaceAllActionShort?: string;
156+
replaceAllAction?: string
157+
}
158+
);
137159
/**
138160
* Show a find-and-replace dialog.
139161
* @param {codeInput.CodeInput} codeInputElement the `<code-input>` element.
140162
* @param {boolean} replacePartExpanded whether the replace part of the find-and-replace dialog should be expanded
141163
*/
142-
showPrompt(codeInput: CodeInput, replacePartExpanded: boolean): void;
164+
showPrompt(codeInputElement: CodeInput, replacePartExpanded: boolean): void;
143165
}
144166

145167
/**
@@ -150,8 +172,13 @@ export namespace plugins {
150172
/**
151173
* Create a go-to-line command plugin to pass into a template
152174
* @param {boolean} useCtrlG Should Ctrl+G be overriden for go-to-line functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`.
175+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text.
153176
*/
154-
constructor(useCtrlG: boolean);
177+
constructor(useCtrlG: boolean,
178+
instructionTranslations?: {
179+
closeDialog?: string;
180+
input?: string;
181+
});
155182
/**
156183
* Show a search-like dialog prompting line number.
157184
* @param {codeInput.CodeInput} codeInput the `<code-input>` element.
@@ -171,8 +198,12 @@ export namespace plugins {
171198
* @param {Number} numSpaces How many spaces is each tab character worth? Defaults to 4.
172199
* @param {Object} bracketPairs Opening brackets mapped to closing brackets, default and example {"(": ")", "[": "]", "{": "}"}. All brackets must only be one character, and this can be left as null to remove bracket-based indentation behaviour.
173200
* @param {boolean} escTabToChangeFocus Whether pressing the Escape key before (Shift+)Tab should make this keypress focus on a different element (Tab's default behaviour). You should always either enable this or use this plugin's disableTabIndentation and enableTabIndentation methods linked to other keyboard shortcuts, for accessibility.
201+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text.
174202
*/
175-
constructor(defaultSpaces?: boolean, numSpaces?: Number, bracketPairs?: Object, escTabToChangeFocus?: boolean);
203+
constructor(defaultSpaces?: boolean, numSpaces?: Number, bracketPairs?: Object, escTabToChangeFocus?: boolean, instructionTranslations?: {
204+
tabForIndentation?: string;
205+
tabForNavigation?: string;
206+
});
176207
}
177208

178209
/**
@@ -355,4 +386,4 @@ export class CodeInput extends HTMLElement { }
355386
* @param {string} templateName - the name to register the template under
356387
* @param {Object} template - a Template object instance - see `codeInput.templates`
357388
*/
358-
export function registerTemplate(templateName: string, template: Template): void;
389+
export function registerTemplate(templateName: string, template: Template): void;

code-input.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,17 @@ var codeInput = {
377377
});
378378
}
379379

380+
/**
381+
* Replace the keys in destination with any source
382+
* @param {Object} destination Where to place the translated strings, already filled with the keys pointing to English strings.
383+
* @param {Object} source The same keys, or some of them, mapped to translated strings.
384+
*/
385+
addTranslations(destination, source) {
386+
for(const key in source) {
387+
destination[key] = source[key];
388+
}
389+
}
390+
380391
/**
381392
* Runs before code is highlighted.
382393
* @param {codeInput.CodeInput} codeInput - The codeInput element

plugins/find-and-replace.js

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,38 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
88

99
findMatchesOnValueChange = true; // Needed so the program can insert text to the find value and thus add it to Ctrl+Z without highlighting matches.
1010

11+
instructions = {
12+
start: "Search for matches in your code.",
13+
none: "No matches",
14+
oneFound: "1 match found.",
15+
matchIndex: (index, count) => `${index} of ${count} matches.`,
16+
error: (message) => `Error: ${message}`,
17+
infiniteLoopError: "Causes an infinite loop",
18+
closeDialog: "Close Dialog and Return to Editor",
19+
findPlaceholder: "Find",
20+
findCaseSensitive: "Match Case Sensitive",
21+
findRegExp: "Use JavaScript Regular Expression",
22+
replaceTitle: "Replace",
23+
replacePlaceholder: "Replace with",
24+
findNext: "Find Next Occurrence",
25+
findPrevious: "Find Previous Occurrence",
26+
replaceActionShort: "Replace",
27+
replaceAction: "Replace This Occurrence",
28+
replaceAllActionShort: "Replace All",
29+
replaceAllAction: "Replace All Occurrences"
30+
};
31+
1132
/**
1233
* Create a find-and-replace command plugin to pass into a template
1334
* @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`.
1435
* @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`.
36+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text and available keys.
1537
*/
16-
constructor(useCtrlF = true, useCtrlH = true) {
38+
constructor(useCtrlF = true, useCtrlH = true, instructionTranslations = {}) {
1739
super([]); // No observed attributes
1840
this.useCtrlF = useCtrlF;
1941
this.useCtrlH = useCtrlH;
42+
this.addTranslations(this.instructions, instructionTranslations);
2043
}
2144

2245
/* Add keystroke events */
@@ -56,13 +79,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
5679
updateMatchDescription(dialog) {
5780
// 1-indexed
5881
if(dialog.findInput.value.length == 0) {
59-
dialog.matchDescription.textContent = "Search for matches in your code.";
82+
dialog.matchDescription.textContent = this.instructions.start;
6083
} else if(dialog.findMatchState.numMatches <= 0) {
61-
dialog.matchDescription.textContent = "No matches.";
84+
dialog.matchDescription.textContent = this.instructions.none;
6285
} else if(dialog.findMatchState.numMatches == 1) {
63-
dialog.matchDescription.textContent = "1 match found.";
86+
dialog.matchDescription.textContent = this.instructions.oneFound;
6487
} else {
65-
dialog.matchDescription.textContent = `${dialog.findMatchState.focusedMatchID+1} of ${dialog.findMatchState.numMatches} matches.`;
88+
dialog.matchDescription.textContent = this.instructions.matchIndex(dialog.findMatchState.focusedMatchID+1, dialog.findMatchState.numMatches);
6689
}
6790
}
6891

@@ -83,7 +106,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
83106
dialog.findInput.classList.add('code-input_find-and-replace_error');
84107
// Only show last part of error message
85108
let messageParts = err.message.split(": ");
86-
dialog.matchDescription.textContent = "Error: " + messageParts[messageParts.length-1]; // Show only last part of error.
109+
dialog.matchDescription.textContent = this.instructions.error(messageParts[messageParts.length-1]); // Show only last part of error.
87110
return;
88111
} else {
89112
throw err;
@@ -183,7 +206,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
183206
const replaceAllButton = document.createElement('button');
184207
const cancel = document.createElement('span');
185208
cancel.setAttribute("tabindex", 0); // Visible to keyboard navigation
186-
cancel.setAttribute("title", "Close Dialog and Return to Editor");
209+
cancel.setAttribute("title", this.instructions.closeDialog);
187210

188211
buttonContainer.appendChild(findNextButton);
189212
buttonContainer.appendChild(findPreviousButton);
@@ -203,35 +226,35 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
203226

204227
dialog.className = 'code-input_find-and-replace_dialog';
205228
findInput.spellcheck = false;
206-
findInput.placeholder = "Find";
229+
findInput.placeholder = this.instructions.findPlaceholder;
207230
findCaseSensitiveCheckbox.setAttribute("type", "checkbox");
208-
findCaseSensitiveCheckbox.title = "Match Case Sensitive";
231+
findCaseSensitiveCheckbox.title = this.instructions.findCaseSensitive;
209232
findCaseSensitiveCheckbox.classList.add("code-input_find-and-replace_case-sensitive-checkbox");
210233
findRegExpCheckbox.setAttribute("type", "checkbox");
211-
findRegExpCheckbox.title = "Use JavaScript Regular Expression";
234+
findRegExpCheckbox.title = this.instructions.findRegExp;
212235
findRegExpCheckbox.classList.add("code-input_find-and-replace_reg-exp-checkbox");
213236

214237
matchDescription.textContent = "Search for matches in your code.";
215238
matchDescription.classList.add("code-input_find-and-replace_match-description");
216239

217240

218-
replaceSummary.innerText = "Replace";
241+
replaceSummary.innerText = this.instructions.replaceTitle;
219242
replaceInput.spellcheck = false;
220-
replaceInput.placeholder = "Replace with";
243+
replaceInput.placeholder = this.instructions.replacePlaceholder;
221244
findNextButton.innerText = "↓";
222-
findNextButton.title = "Find Next Occurence";
245+
findNextButton.title = this.instructions.findNext;
223246
findPreviousButton.innerText = "↑";
224-
findPreviousButton.title = "Find Previous Occurence";
247+
findPreviousButton.title = this.instructions.findPrevious;
225248
replaceButton.className = 'code-input_find-and-replace_button-hidden';
226-
replaceButton.innerText = "Replace";
227-
replaceButton.title = "Replace This Occurence";
249+
replaceButton.innerText = this.instructions.replaceActionShort;
250+
replaceButton.title = this.instructions.replaceAction;
228251
replaceButton.addEventListener("focus", () => {
229252
// Show replace section
230253
replaceDropdown.setAttribute("open", true);
231254
});
232255
replaceAllButton.className = 'code-input_find-and-replace_button-hidden';
233-
replaceAllButton.innerText = "Replace All";
234-
replaceAllButton.title = "Replace All Occurences";
256+
replaceAllButton.innerText = this.instructions.replaceAllActionShort;
257+
replaceAllButton.title = this.instructions.replaceAllAction;
235258
replaceAllButton.addEventListener("focus", () => {
236259
// Show replace section
237260
replaceDropdown.setAttribute("open", true);
@@ -470,7 +493,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
470493
while ((match = searchRegexp.exec(this.codeInput.value)) !== null) {
471494
let matchText = match[0];
472495
if(matchText.length == 0) {
473-
throw SyntaxError("Causes an infinite loop");
496+
throw SyntaxError(this.instructions.infiniteLoopError);
474497
}
475498

476499
// Add next match block if needed
@@ -720,4 +743,4 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
720743
endIndex -= childText.length;
721744
}
722745
}
723-
}
746+
}

plugins/go-to-line.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
codeInput.plugins.GoToLine = class extends codeInput.Plugin {
66
useCtrlG = false;
77

8+
instructions = {
9+
closeDialog: "Close Dialog and Return to Editor",
10+
input: "Line:Column / Line no. then Enter",
11+
};
12+
813
/**
914
* Create a go-to-line command plugin to pass into a template
1015
* @param {boolean} useCtrlG Should Ctrl+G be overriden for go-to-line functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`.
16+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the available keys and English text.
1117
*/
12-
constructor(useCtrlG = true) {
18+
constructor(useCtrlG = true, instructionTranslations = {}) {
1319
super([]); // No observed attributes
1420
this.useCtrlG = useCtrlG;
21+
this.addTranslations(this.instructions, instructionTranslations);
1522
}
1623

1724
/* Add keystroke events */
@@ -85,14 +92,14 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
8592
const input = document.createElement('input');
8693
const cancel = document.createElement('span');
8794
cancel.setAttribute("tabindex", 0); // Visible to keyboard navigation
88-
cancel.setAttribute("title", "Close Dialog and Return to Editor");
95+
cancel.setAttribute("title", this.instructions.closeDialog);
8996

9097
dialog.appendChild(input);
9198
dialog.appendChild(cancel);
9299

93100
dialog.className = 'code-input_go-to-line_dialog';
94101
input.spellcheck = false;
95-
input.placeholder = "Line:Column / Line no. then Enter";
102+
input.placeholder = this.instructions.input;
96103
dialog.codeInput = codeInput;
97104
dialog.textarea = textarea;
98105
dialog.input = input;

0 commit comments

Comments
 (0)