@involvex/fresh-editor 0.1.76 → 0.1.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/bin/CHANGELOG.md +1017 -0
  2. package/bin/LICENSE +117 -0
  3. package/bin/README.md +248 -0
  4. package/bin/fresh.exe +0 -0
  5. package/bin/plugins/README.md +71 -0
  6. package/bin/plugins/audit_mode.i18n.json +821 -0
  7. package/bin/plugins/audit_mode.ts +1810 -0
  8. package/bin/plugins/buffer_modified.i18n.json +67 -0
  9. package/bin/plugins/buffer_modified.ts +281 -0
  10. package/bin/plugins/calculator.i18n.json +93 -0
  11. package/bin/plugins/calculator.ts +770 -0
  12. package/bin/plugins/clangd-lsp.ts +168 -0
  13. package/bin/plugins/clangd_support.i18n.json +223 -0
  14. package/bin/plugins/clangd_support.md +20 -0
  15. package/bin/plugins/clangd_support.ts +325 -0
  16. package/bin/plugins/color_highlighter.i18n.json +145 -0
  17. package/bin/plugins/color_highlighter.ts +304 -0
  18. package/bin/plugins/config-schema.json +768 -0
  19. package/bin/plugins/csharp-lsp.ts +147 -0
  20. package/bin/plugins/csharp_support.i18n.json +80 -0
  21. package/bin/plugins/csharp_support.ts +170 -0
  22. package/bin/plugins/css-lsp.ts +143 -0
  23. package/bin/plugins/diagnostics_panel.i18n.json +236 -0
  24. package/bin/plugins/diagnostics_panel.ts +642 -0
  25. package/bin/plugins/examples/README.md +85 -0
  26. package/bin/plugins/examples/async_demo.ts +165 -0
  27. package/bin/plugins/examples/bookmarks.ts +329 -0
  28. package/bin/plugins/examples/buffer_query_demo.ts +110 -0
  29. package/bin/plugins/examples/git_grep.ts +262 -0
  30. package/bin/plugins/examples/hello_world.ts +93 -0
  31. package/bin/plugins/examples/virtual_buffer_demo.ts +116 -0
  32. package/bin/plugins/find_references.i18n.json +275 -0
  33. package/bin/plugins/find_references.ts +359 -0
  34. package/bin/plugins/git_blame.i18n.json +496 -0
  35. package/bin/plugins/git_blame.ts +707 -0
  36. package/bin/plugins/git_find_file.i18n.json +314 -0
  37. package/bin/plugins/git_find_file.ts +300 -0
  38. package/bin/plugins/git_grep.i18n.json +171 -0
  39. package/bin/plugins/git_grep.ts +191 -0
  40. package/bin/plugins/git_gutter.i18n.json +93 -0
  41. package/bin/plugins/git_gutter.ts +477 -0
  42. package/bin/plugins/git_log.i18n.json +481 -0
  43. package/bin/plugins/git_log.ts +1285 -0
  44. package/bin/plugins/go-lsp.ts +143 -0
  45. package/bin/plugins/html-lsp.ts +145 -0
  46. package/bin/plugins/json-lsp.ts +145 -0
  47. package/bin/plugins/lib/fresh.d.ts +1321 -0
  48. package/bin/plugins/lib/index.ts +24 -0
  49. package/bin/plugins/lib/navigation-controller.ts +214 -0
  50. package/bin/plugins/lib/panel-manager.ts +220 -0
  51. package/bin/plugins/lib/types.ts +72 -0
  52. package/bin/plugins/lib/virtual-buffer-factory.ts +130 -0
  53. package/bin/plugins/live_grep.i18n.json +171 -0
  54. package/bin/plugins/live_grep.ts +422 -0
  55. package/bin/plugins/markdown_compose.i18n.json +223 -0
  56. package/bin/plugins/markdown_compose.ts +630 -0
  57. package/bin/plugins/merge_conflict.i18n.json +821 -0
  58. package/bin/plugins/merge_conflict.ts +1810 -0
  59. package/bin/plugins/path_complete.i18n.json +80 -0
  60. package/bin/plugins/path_complete.ts +165 -0
  61. package/bin/plugins/python-lsp.ts +162 -0
  62. package/bin/plugins/rust-lsp.ts +166 -0
  63. package/bin/plugins/search_replace.i18n.json +405 -0
  64. package/bin/plugins/search_replace.ts +484 -0
  65. package/bin/plugins/test_i18n.i18n.json +67 -0
  66. package/bin/plugins/test_i18n.ts +18 -0
  67. package/bin/plugins/theme_editor.i18n.json +3746 -0
  68. package/bin/plugins/theme_editor.ts +2063 -0
  69. package/bin/plugins/todo_highlighter.i18n.json +184 -0
  70. package/bin/plugins/todo_highlighter.ts +206 -0
  71. package/bin/plugins/typescript-lsp.ts +167 -0
  72. package/bin/plugins/vi_mode.i18n.json +1549 -0
  73. package/bin/plugins/vi_mode.ts +2747 -0
  74. package/bin/plugins/welcome.i18n.json +236 -0
  75. package/bin/plugins/welcome.ts +76 -0
  76. package/bin/themes/dark.json +102 -0
  77. package/bin/themes/dracula.json +62 -0
  78. package/bin/themes/high-contrast.json +102 -0
  79. package/bin/themes/light.json +102 -0
  80. package/bin/themes/nord.json +62 -0
  81. package/bin/themes/nostalgia.json +102 -0
  82. package/bin/themes/solarized-dark.json +62 -0
  83. package/binary-install.js +1 -1
  84. package/dist/bin/fresh.js +9 -0
  85. package/dist/binary-install.js +149 -0
  86. package/dist/binary.js +30 -0
  87. package/dist/fresh-6yhknp07.exe +0 -0
  88. package/dist/install.js +158 -0
  89. package/dist/run-fresh.js +43 -0
  90. package/package.json +7 -2
@@ -0,0 +1,484 @@
1
+ /// <reference path="../types/fresh.d.ts" />
2
+ const editor = getEditor();
3
+
4
+
5
+ /**
6
+ * Multi-File Search & Replace Plugin
7
+ *
8
+ * Provides project-wide search and replace functionality using git grep.
9
+ * Shows results in a virtual buffer split with preview and confirmation.
10
+ */
11
+
12
+ // Result item structure
13
+ interface SearchResult {
14
+ file: string;
15
+ line: number;
16
+ column: number;
17
+ content: string;
18
+ selected: boolean; // Whether this result will be replaced
19
+ }
20
+
21
+ // Plugin state
22
+ let panelOpen = false;
23
+ let resultsBufferId: number | null = null;
24
+ let sourceSplitId: number | null = null;
25
+ let resultsSplitId: number | null = null;
26
+ let searchResults: SearchResult[] = [];
27
+ let searchPattern: string = "";
28
+ let replaceText: string = "";
29
+ let searchRegex: boolean = false;
30
+
31
+ // Maximum results to display
32
+ const MAX_RESULTS = 200;
33
+
34
+ // Define the search-replace mode with keybindings
35
+ editor.defineMode(
36
+ "search-replace-list",
37
+ null,
38
+ [
39
+ ["Return", "search_replace_preview"],
40
+ ["space", "search_replace_toggle_item"],
41
+ ["a", "search_replace_select_all"],
42
+ ["n", "search_replace_select_none"],
43
+ ["r", "search_replace_execute"],
44
+ ["q", "search_replace_close"],
45
+ ["Escape", "search_replace_close"],
46
+ ],
47
+ true // read-only
48
+ );
49
+
50
+ // Get relative path for display
51
+ function getRelativePath(filePath: string): string {
52
+ const cwd = editor.getCwd();
53
+ if (filePath.startsWith(cwd)) {
54
+ return filePath.slice(cwd.length + 1);
55
+ }
56
+ return filePath;
57
+ }
58
+
59
+ // Parse git grep output
60
+ function parseGitGrepLine(line: string): SearchResult | null {
61
+ const match = line.match(/^([^:]+):(\d+):(\d+):(.*)$/);
62
+ if (match) {
63
+ return {
64
+ file: match[1],
65
+ line: parseInt(match[2], 10),
66
+ column: parseInt(match[3], 10),
67
+ content: match[4],
68
+ selected: true, // Selected by default
69
+ };
70
+ }
71
+ return null;
72
+ }
73
+
74
+ // Format a result for display
75
+ function formatResult(item: SearchResult, index: number): string {
76
+ const checkbox = item.selected ? "[x]" : "[ ]";
77
+ const displayPath = getRelativePath(item.file);
78
+ const location = `${displayPath}:${item.line}`;
79
+
80
+ // Truncate for display
81
+ const maxLocationLen = 40;
82
+ const truncatedLocation = location.length > maxLocationLen
83
+ ? "..." + location.slice(-(maxLocationLen - 3))
84
+ : location.padEnd(maxLocationLen);
85
+
86
+ const trimmedContent = item.content.trim();
87
+ const maxContentLen = 50;
88
+ const displayContent = trimmedContent.length > maxContentLen
89
+ ? trimmedContent.slice(0, maxContentLen - 3) + "..."
90
+ : trimmedContent;
91
+
92
+ return `${checkbox} ${truncatedLocation} ${displayContent}\n`;
93
+ }
94
+
95
+ // Build panel entries
96
+ function buildPanelEntries(): TextPropertyEntry[] {
97
+ const entries: TextPropertyEntry[] = [];
98
+
99
+ // Header
100
+ const selectedCount = searchResults.filter(r => r.selected).length;
101
+ entries.push({
102
+ text: `═══ ${editor.t("panel.header")} ═══\n`,
103
+ properties: { type: "header" },
104
+ });
105
+ entries.push({
106
+ text: `${editor.t("panel.search_label")} "${searchPattern}"${searchRegex ? " " + editor.t("panel.regex") : ""}\n`,
107
+ properties: { type: "info" },
108
+ });
109
+ entries.push({
110
+ text: `${editor.t("panel.replace_label")} "${replaceText}"\n`,
111
+ properties: { type: "info" },
112
+ });
113
+ entries.push({
114
+ text: `\n`,
115
+ properties: { type: "spacer" },
116
+ });
117
+
118
+ if (searchResults.length === 0) {
119
+ entries.push({
120
+ text: " " + editor.t("panel.no_matches") + "\n",
121
+ properties: { type: "empty" },
122
+ });
123
+ } else {
124
+ // Results header
125
+ const limitNote = searchResults.length >= MAX_RESULTS ? " " + editor.t("panel.limited", { max: String(MAX_RESULTS) }) : "";
126
+ entries.push({
127
+ text: `${editor.t("panel.results", { count: String(searchResults.length) })}${limitNote} ${editor.t("panel.selected", { selected: String(selectedCount) })}\n`,
128
+ properties: { type: "count" },
129
+ });
130
+ entries.push({
131
+ text: `\n`,
132
+ properties: { type: "spacer" },
133
+ });
134
+
135
+ // Add each result
136
+ for (let i = 0; i < searchResults.length; i++) {
137
+ const result = searchResults[i];
138
+ entries.push({
139
+ text: formatResult(result, i),
140
+ properties: {
141
+ type: "result",
142
+ index: i,
143
+ location: {
144
+ file: result.file,
145
+ line: result.line,
146
+ column: result.column,
147
+ },
148
+ },
149
+ });
150
+ }
151
+ }
152
+
153
+ // Footer
154
+ entries.push({
155
+ text: `───────────────────────────────────────────────────────────────────────────────\n`,
156
+ properties: { type: "separator" },
157
+ });
158
+ entries.push({
159
+ text: editor.t("panel.help") + "\n",
160
+ properties: { type: "help" },
161
+ });
162
+
163
+ return entries;
164
+ }
165
+
166
+ // Update panel content
167
+ function updatePanelContent(): void {
168
+ if (resultsBufferId !== null) {
169
+ const entries = buildPanelEntries();
170
+ editor.setVirtualBufferContent(resultsBufferId, entries);
171
+ }
172
+ }
173
+
174
+ // Perform the search
175
+ async function performSearch(pattern: string, replace: string, isRegex: boolean): Promise<void> {
176
+ searchPattern = pattern;
177
+ replaceText = replace;
178
+ searchRegex = isRegex;
179
+
180
+ // Build git grep args
181
+ const args = ["grep", "-n", "--column", "-I"];
182
+ if (isRegex) {
183
+ args.push("-E"); // Extended regex
184
+ } else {
185
+ args.push("-F"); // Fixed string
186
+ }
187
+ args.push("--", pattern);
188
+
189
+ try {
190
+ const cwd = editor.getCwd();
191
+ const result = await editor.spawnProcess("git", args, cwd);
192
+
193
+ searchResults = [];
194
+
195
+ if (result.exit_code === 0) {
196
+ for (const line of result.stdout.split("\n")) {
197
+ if (!line.trim()) continue;
198
+ const match = parseGitGrepLine(line);
199
+ if (match) {
200
+ searchResults.push(match);
201
+ if (searchResults.length >= MAX_RESULTS) break;
202
+ }
203
+ }
204
+ }
205
+
206
+ if (searchResults.length === 0) {
207
+ editor.setStatus(editor.t("status.no_matches", { pattern }));
208
+ } else {
209
+ editor.setStatus(editor.t("status.found_matches", { count: String(searchResults.length) }));
210
+ }
211
+ } catch (e) {
212
+ editor.setStatus(editor.t("status.search_error", { error: String(e) }));
213
+ searchResults = [];
214
+ }
215
+ }
216
+
217
+ // Show the search results panel
218
+ async function showResultsPanel(): Promise<void> {
219
+ if (panelOpen && resultsBufferId !== null) {
220
+ updatePanelContent();
221
+ return;
222
+ }
223
+
224
+ sourceSplitId = editor.getActiveSplitId();
225
+ const entries = buildPanelEntries();
226
+
227
+ try {
228
+ resultsBufferId = await editor.createVirtualBufferInSplit({
229
+ name: "*Search/Replace*",
230
+ mode: "search-replace-list",
231
+ read_only: true,
232
+ entries: entries,
233
+ ratio: 0.6, // 60/40 split
234
+ panel_id: "search-replace-panel",
235
+ show_line_numbers: false,
236
+ show_cursors: true,
237
+ });
238
+
239
+ panelOpen = true;
240
+ resultsSplitId = editor.getActiveSplitId();
241
+ editor.debug(`Search/Replace panel opened with buffer ID ${resultsBufferId}`);
242
+ } catch (error) {
243
+ const errorMessage = error instanceof Error ? error.message : String(error);
244
+ editor.setStatus(editor.t("status.failed_open_panel"));
245
+ editor.debug(`ERROR: createVirtualBufferInSplit failed: ${errorMessage}`);
246
+ }
247
+ }
248
+
249
+ // Execute replacements
250
+ async function executeReplacements(): Promise<void> {
251
+ const selectedResults = searchResults.filter(r => r.selected);
252
+
253
+ if (selectedResults.length === 0) {
254
+ editor.setStatus(editor.t("status.no_selected"));
255
+ return;
256
+ }
257
+
258
+ // Group by file
259
+ const fileGroups: Map<string, SearchResult[]> = new Map();
260
+ for (const result of selectedResults) {
261
+ if (!fileGroups.has(result.file)) {
262
+ fileGroups.set(result.file, []);
263
+ }
264
+ fileGroups.get(result.file)!.push(result);
265
+ }
266
+
267
+ let filesModified = 0;
268
+ let replacementsCount = 0;
269
+ const errors: string[] = [];
270
+
271
+ for (const [filePath, results] of fileGroups) {
272
+ try {
273
+ // Read file
274
+ const content = await editor.readFile(filePath);
275
+ const lines = content.split("\n");
276
+
277
+ // Sort results by line (descending) to avoid offset issues
278
+ const sortedResults = [...results].sort((a, b) => {
279
+ if (a.line !== b.line) return b.line - a.line;
280
+ return b.column - a.column;
281
+ });
282
+
283
+ // Apply replacements
284
+ for (const result of sortedResults) {
285
+ const lineIndex = result.line - 1;
286
+ if (lineIndex >= 0 && lineIndex < lines.length) {
287
+ let line = lines[lineIndex];
288
+
289
+ if (searchRegex) {
290
+ // Regex replacement
291
+ const regex = new RegExp(searchPattern, "g");
292
+ lines[lineIndex] = line.replace(regex, replaceText);
293
+ } else {
294
+ // Simple string replacement (all occurrences in line)
295
+ lines[lineIndex] = line.split(searchPattern).join(replaceText);
296
+ }
297
+ replacementsCount++;
298
+ }
299
+ }
300
+
301
+ // Write back
302
+ const newContent = lines.join("\n");
303
+ await editor.writeFile(filePath, newContent);
304
+ filesModified++;
305
+
306
+ } catch (e) {
307
+ const errorMessage = e instanceof Error ? e.message : String(e);
308
+ errors.push(`${filePath}: ${errorMessage}`);
309
+ }
310
+ }
311
+
312
+ // Report results
313
+ if (errors.length > 0) {
314
+ editor.setStatus(editor.t("status.replaced_with_errors", { files: String(filesModified), errors: String(errors.length) }));
315
+ editor.debug(`Replacement errors: ${errors.join(", ")}`);
316
+ } else {
317
+ editor.setStatus(editor.t("status.replaced", { count: String(replacementsCount), files: String(filesModified) }));
318
+ }
319
+
320
+ // Close panel after replacement
321
+ globalThis.search_replace_close();
322
+ }
323
+
324
+ // Start search/replace workflow
325
+ globalThis.start_search_replace = function(): void {
326
+ searchResults = [];
327
+ searchPattern = "";
328
+ replaceText = "";
329
+
330
+ editor.startPrompt(editor.t("prompt.search"), "search-replace-search");
331
+ editor.setStatus(editor.t("status.enter_pattern"));
332
+ };
333
+
334
+ // Handle search prompt confirmation
335
+ globalThis.onSearchReplaceSearchConfirmed = function(args: {
336
+ prompt_type: string;
337
+ selected_index: number | null;
338
+ input: string;
339
+ }): boolean {
340
+ if (args.prompt_type !== "search-replace-search") {
341
+ return true;
342
+ }
343
+
344
+ const pattern = args.input.trim();
345
+ if (!pattern) {
346
+ editor.setStatus(editor.t("status.cancelled_empty"));
347
+ return true;
348
+ }
349
+
350
+ searchPattern = pattern;
351
+
352
+ // Ask for replacement text
353
+ editor.startPrompt(editor.t("prompt.replace"), "search-replace-replace");
354
+ return true;
355
+ };
356
+
357
+ // Handle replace prompt confirmation
358
+ globalThis.onSearchReplaceReplaceConfirmed = async function(args: {
359
+ prompt_type: string;
360
+ selected_index: number | null;
361
+ input: string;
362
+ }): Promise<boolean> {
363
+ if (args.prompt_type !== "search-replace-replace") {
364
+ return true;
365
+ }
366
+
367
+ replaceText = args.input; // Can be empty for deletion
368
+
369
+ // Perform search and show results
370
+ await performSearch(searchPattern, replaceText, false);
371
+ await showResultsPanel();
372
+
373
+ return true;
374
+ };
375
+
376
+ // Handle prompt cancellation
377
+ globalThis.onSearchReplacePromptCancelled = function(args: {
378
+ prompt_type: string;
379
+ }): boolean {
380
+ if (args.prompt_type !== "search-replace-search" &&
381
+ args.prompt_type !== "search-replace-replace") {
382
+ return true;
383
+ }
384
+
385
+ editor.setStatus(editor.t("status.cancelled"));
386
+ return true;
387
+ };
388
+
389
+ // Toggle selection of current item
390
+ globalThis.search_replace_toggle_item = function(): void {
391
+ if (resultsBufferId === null || searchResults.length === 0) return;
392
+
393
+ const props = editor.getTextPropertiesAtCursor(resultsBufferId);
394
+ if (props.length > 0 && typeof props[0].index === "number") {
395
+ const index = props[0].index as number;
396
+ if (index >= 0 && index < searchResults.length) {
397
+ searchResults[index].selected = !searchResults[index].selected;
398
+ updatePanelContent();
399
+ const selected = searchResults.filter(r => r.selected).length;
400
+ editor.setStatus(editor.t("status.selected_count", { selected: String(selected), total: String(searchResults.length) }));
401
+ }
402
+ }
403
+ };
404
+
405
+ // Select all items
406
+ globalThis.search_replace_select_all = function(): void {
407
+ for (const result of searchResults) {
408
+ result.selected = true;
409
+ }
410
+ updatePanelContent();
411
+ editor.setStatus(editor.t("status.selected_count", { selected: String(searchResults.length), total: String(searchResults.length) }));
412
+ };
413
+
414
+ // Select no items
415
+ globalThis.search_replace_select_none = function(): void {
416
+ for (const result of searchResults) {
417
+ result.selected = false;
418
+ }
419
+ updatePanelContent();
420
+ editor.setStatus(editor.t("status.selected_count", { selected: "0", total: String(searchResults.length) }));
421
+ };
422
+
423
+ // Execute replacement
424
+ globalThis.search_replace_execute = function(): void {
425
+ const selected = searchResults.filter(r => r.selected).length;
426
+ if (selected === 0) {
427
+ editor.setStatus(editor.t("status.no_items_selected"));
428
+ return;
429
+ }
430
+
431
+ editor.setStatus(editor.t("status.replacing", { count: String(selected) }));
432
+ executeReplacements();
433
+ };
434
+
435
+ // Preview current item (jump to location)
436
+ globalThis.search_replace_preview = function(): void {
437
+ if (sourceSplitId === null || resultsBufferId === null) return;
438
+
439
+ const props = editor.getTextPropertiesAtCursor(resultsBufferId);
440
+ if (props.length > 0) {
441
+ const location = props[0].location as { file: string; line: number; column: number } | undefined;
442
+ if (location) {
443
+ editor.openFileInSplit(sourceSplitId, location.file, location.line, location.column);
444
+ editor.setStatus(editor.t("status.preview", { file: getRelativePath(location.file), line: String(location.line) }));
445
+ }
446
+ }
447
+ };
448
+
449
+ // Close the panel
450
+ globalThis.search_replace_close = function(): void {
451
+ if (!panelOpen) return;
452
+
453
+ if (resultsBufferId !== null) {
454
+ editor.closeBuffer(resultsBufferId);
455
+ }
456
+
457
+ if (resultsSplitId !== null && resultsSplitId !== sourceSplitId) {
458
+ editor.closeSplit(resultsSplitId);
459
+ }
460
+
461
+ panelOpen = false;
462
+ resultsBufferId = null;
463
+ sourceSplitId = null;
464
+ resultsSplitId = null;
465
+ searchResults = [];
466
+ editor.setStatus(editor.t("status.closed"));
467
+ };
468
+
469
+ // Register event handlers
470
+ editor.on("prompt_confirmed", "onSearchReplaceSearchConfirmed");
471
+ editor.on("prompt_confirmed", "onSearchReplaceReplaceConfirmed");
472
+ editor.on("prompt_cancelled", "onSearchReplacePromptCancelled");
473
+
474
+ // Register command
475
+ editor.registerCommand(
476
+ "%cmd.search_replace",
477
+ "%cmd.search_replace_desc",
478
+ "start_search_replace",
479
+ "normal"
480
+ );
481
+
482
+ // Plugin initialization
483
+ editor.debug("Search & Replace plugin loaded");
484
+ editor.setStatus(editor.t("status.ready"));
@@ -0,0 +1,67 @@
1
+ {
2
+ "cs": {
3
+ "cmd.test": "Testovací lokalizovaný příkaz",
4
+ "cmd.test_desc": "Příkaz pro testování lokalizace pluginů",
5
+ "msg.hello": "Ahoj, %{name}! Tvůj jazyk je %{locale}."
6
+ },
7
+ "de": {
8
+ "cmd.test": "Lokalisierter Testbefehl",
9
+ "cmd.test_desc": "Ein Befehl zum Testen der Plugin-Lokalisierung",
10
+ "msg.hello": "Hallo, %{name}! Deine Sprache ist %{locale}."
11
+ },
12
+ "en": {
13
+ "cmd.test": "Test Localized Command",
14
+ "cmd.test_desc": "A command to test plugin localization",
15
+ "msg.hello": "Hello, %{name}! Your locale is %{locale}."
16
+ },
17
+ "es": {
18
+ "cmd.test": "Comando de prueba localizado",
19
+ "cmd.test_desc": "Un comando para probar la localización de plugins",
20
+ "msg.hello": "¡Hola, %{name}! Tu idioma es %{locale}."
21
+ },
22
+ "fr": {
23
+ "cmd.test": "Commande de test localisée",
24
+ "cmd.test_desc": "Une commande pour tester la localisation des plugins",
25
+ "msg.hello": "Bonjour, %{name} ! Votre langue est %{locale}."
26
+ },
27
+ "it": {
28
+ "cmd.test": "Comando di test localizzato",
29
+ "cmd.test_desc": "Un comando per testare la localizzazione dei plugin",
30
+ "msg.hello": "Ciao, %{name}! La tua lingua è %{locale}."
31
+ },
32
+ "ja": {
33
+ "cmd.test": "ローカライズテストコマンド",
34
+ "cmd.test_desc": "プラグインのローカライズをテストするコマンド",
35
+ "msg.hello": "こんにちは、%{name}さん!あなたの言語は%{locale}です。"
36
+ },
37
+ "ko": {
38
+ "cmd.test": "현지화 테스트 명령",
39
+ "cmd.test_desc": "플러그인 현지화를 테스트하는 명령",
40
+ "msg.hello": "안녕하세요, %{name}님! 당신의 언어는 %{locale}입니다."
41
+ },
42
+ "pt-BR": {
43
+ "cmd.test": "Comando de teste localizado",
44
+ "cmd.test_desc": "Um comando para testar a localização de plugins",
45
+ "msg.hello": "Olá, %{name}! Seu idioma é %{locale}."
46
+ },
47
+ "ru": {
48
+ "cmd.test": "Локализованная тестовая команда",
49
+ "cmd.test_desc": "Команда для тестирования локализации плагинов",
50
+ "msg.hello": "Привет, %{name}! Ваш язык — %{locale}."
51
+ },
52
+ "th": {
53
+ "cmd.test": "คำสั่งทดสอบการแปล",
54
+ "cmd.test_desc": "คำสั่งสำหรับทดสอบการแปลปลั๊กอิน",
55
+ "msg.hello": "สวัสดี %{name}! ภาษาของคุณคือ %{locale}"
56
+ },
57
+ "uk": {
58
+ "cmd.test": "Локалізована тестова команда",
59
+ "cmd.test_desc": "Команда для тестування локалізації плагінів",
60
+ "msg.hello": "Привіт, %{name}! Ваша мова — %{locale}."
61
+ },
62
+ "zh-CN": {
63
+ "cmd.test": "本地化测试命令",
64
+ "cmd.test_desc": "用于测试插件本地化的命令",
65
+ "msg.hello": "你好,%{name}!你的语言是%{locale}。"
66
+ }
67
+ }
@@ -0,0 +1,18 @@
1
+ /// <reference path="./lib/fresh.d.ts" />
2
+ const editor = getEditor();
3
+
4
+
5
+ globalThis.test_i18n_action = function() {
6
+ const locale = editor.getCurrentLocale();
7
+ const msg = editor.t("msg.hello", { name: "User", locale: locale });
8
+ editor.setStatus(msg);
9
+ };
10
+
11
+ editor.registerCommand(
12
+ "%cmd.test",
13
+ "%cmd.test_desc",
14
+ "test_i18n_action",
15
+ "normal"
16
+ );
17
+
18
+ editor.setStatus("Test i18n plugin loaded");