@wads.dev/i18n-editor 0.0.1-alpha.0 → 0.0.1-beta.0
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.
- package/README.md +29 -4
- package/dist/cli/index.js +55 -0
- package/dist/core/keyPath.js +18 -4
- package/dist/core/projectApi.d.ts +33 -0
- package/dist/core/removeKey.d.ts +5 -0
- package/dist/core/removeKey.js +16 -0
- package/dist/public/editor.js +900 -97
- package/dist/public/index.html +78 -53
- package/dist/public/styles.css +31 -0
- package/dist/server/createServer.js +44 -2
- package/dist/server/exportProject.d.ts +5 -1
- package/dist/server/exportProject.js +6 -0
- package/dist/server/projectContext.d.ts +1 -0
- package/dist/server/projectContext.js +1 -1
- package/dist/server/usageAnalysis.d.ts +13 -0
- package/dist/server/usageAnalysis.js +357 -0
- package/package.json +10 -5
package/dist/public/editor.js
CHANGED
|
@@ -1,3 +1,374 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/web/i18n/en.ts
|
|
17
|
+
var en_exports = {};
|
|
18
|
+
__export(en_exports, {
|
|
19
|
+
default: () => en_default
|
|
20
|
+
});
|
|
21
|
+
var en, en_default;
|
|
22
|
+
var init_en = __esm({
|
|
23
|
+
"src/web/i18n/en.ts"() {
|
|
24
|
+
"use strict";
|
|
25
|
+
en = {
|
|
26
|
+
language: { label: "Language", english: "English", portuguese: "Portugu\xEAs" },
|
|
27
|
+
common: {
|
|
28
|
+
root: "Root",
|
|
29
|
+
level: (index) => `Level ${index}`,
|
|
30
|
+
keys: (count) => `${count} key${count === 1 ? "" : "s"}`
|
|
31
|
+
},
|
|
32
|
+
page: {
|
|
33
|
+
eyebrow: "i18n bundle",
|
|
34
|
+
title: "Translation Editor",
|
|
35
|
+
intro: "The editor detects the current project's configuration and bundle. When necessary, it generates the bundle directly from the TypeScript catalog."
|
|
36
|
+
},
|
|
37
|
+
settings: {
|
|
38
|
+
show: "Show settings",
|
|
39
|
+
hide: "Hide settings",
|
|
40
|
+
title: "Project settings",
|
|
41
|
+
intro: "Define the key structure and paths generated by exports from the project root.",
|
|
42
|
+
levelCount: "Level count",
|
|
43
|
+
levelNames: "Level names",
|
|
44
|
+
importsByLevel: "Imports by level",
|
|
45
|
+
importsHelp: "Level 0 represents the root and does not consume a key segment. Use {value} in the remaining levels. Replacers accept flat or nested JSON.",
|
|
46
|
+
pathTemplate: "Path template",
|
|
47
|
+
valueReplacer: "Value replacer",
|
|
48
|
+
fullReplacer: "Full replacer",
|
|
49
|
+
translationsDirectory: "Translations directory",
|
|
50
|
+
languageFileTemplate: "Language file template",
|
|
51
|
+
languageReplacer: "Language replacer (JSON)",
|
|
52
|
+
exportTitle: "Export",
|
|
53
|
+
exportIntro: "Configure path resolution and formatting for generated TypeScript files.",
|
|
54
|
+
importAliases: "Import aliases (JSON)",
|
|
55
|
+
doubleQuotes: "Use double quotes in generated strings",
|
|
56
|
+
semicolons: "Use semicolons",
|
|
57
|
+
shorthandProperties: "Use shorthand properties",
|
|
58
|
+
trailingCommas: "Use trailing commas",
|
|
59
|
+
indentationCharacter: "Indentation character",
|
|
60
|
+
space: "Space",
|
|
61
|
+
tab: "Tab",
|
|
62
|
+
indentationSize: "Indentation size",
|
|
63
|
+
printWidth: "Maximum line width",
|
|
64
|
+
maxObjectInlineItems: "Maximum inline properties",
|
|
65
|
+
maxArrayInlineItems: "Maximum inline items",
|
|
66
|
+
objectLayout: "Object layout",
|
|
67
|
+
arrayLayout: "Array layout",
|
|
68
|
+
inlineWhenFits: "Inline when it fits",
|
|
69
|
+
alwaysMultiline: "Always multiline",
|
|
70
|
+
divergentFiles: "Divergent files",
|
|
71
|
+
divergentFilesHelp: "Files inside translation directories that are absent from the plan are deletion candidates.",
|
|
72
|
+
detectDeletionCandidates: "Detect and warn about deletion candidates",
|
|
73
|
+
ignoredExtensions: "Ignored extensions",
|
|
74
|
+
allowDeletion: "Allow deletion without --delete"
|
|
75
|
+
},
|
|
76
|
+
editor: {
|
|
77
|
+
title: "Translations",
|
|
78
|
+
emptySummary: "Load a bundle to view translations.",
|
|
79
|
+
noBundle: "No bundle loaded.",
|
|
80
|
+
analyzeUsages: "Analyze usages",
|
|
81
|
+
updateUsages: "Update usages",
|
|
82
|
+
loadingUsages: "Loading usages\u2026",
|
|
83
|
+
updatingUsages: "Updating usages\u2026",
|
|
84
|
+
unreferencedOnly: "Unreferenced only",
|
|
85
|
+
visualizationLevels: "Visualization levels",
|
|
86
|
+
search: "Search",
|
|
87
|
+
searchPlaceholder: "Key or translation",
|
|
88
|
+
key: "Key",
|
|
89
|
+
usages: "Usages",
|
|
90
|
+
base: "Base",
|
|
91
|
+
missing: "Missing",
|
|
92
|
+
inconsistent: "Inconsistent",
|
|
93
|
+
notAnalyzed: "Not analyzed",
|
|
94
|
+
viewDetails: "View details",
|
|
95
|
+
exactReferences: (count) => `${count} exact reference${count === 1 ? "" : "s"}`,
|
|
96
|
+
uncertainReferences: (count) => `${count} uncertain reference${count === 1 ? "" : "s"}`,
|
|
97
|
+
usageSummary: (references, files, uncertain) => `${uncertain ? "\u26A0 " : ""}${references} ${uncertain ? "uncertain" : "ref"}${references === 1 ? "" : "s"} \xB7 ${files} file${files === 1 ? "" : "s"}`,
|
|
98
|
+
potentiallyReachable: "Potentially reachable references",
|
|
99
|
+
noStaticReferences: "No static references",
|
|
100
|
+
referencesFound: "References found",
|
|
101
|
+
dynamicMayReach: "A dynamic access may reach this key.",
|
|
102
|
+
noExactReferences: "No exact references were found.",
|
|
103
|
+
noLocations: "No source locations were recorded.",
|
|
104
|
+
doubleClickMove: "Double-click to move this key",
|
|
105
|
+
doubleClickEdit: "Double-click to edit this translation",
|
|
106
|
+
editTranslation: "Edit translation",
|
|
107
|
+
removeKey: (key) => `Remove key ${key}`,
|
|
108
|
+
bundleSummary: (keys, languages) => `${keys} keys across ${languages} languages.`
|
|
109
|
+
},
|
|
110
|
+
exportPreview: {
|
|
111
|
+
title: "Generated files preview",
|
|
112
|
+
intro: "This is a real-time simulation. Check diffs to compare the result with the current project files.",
|
|
113
|
+
deleteDivergent: "Delete divergent files",
|
|
114
|
+
checkDiffs: "Check diffs",
|
|
115
|
+
checking: "Checking\u2026",
|
|
116
|
+
checkAgain: "Check again",
|
|
117
|
+
exportProject: "Export to project",
|
|
118
|
+
preparing: "Preparing\u2026",
|
|
119
|
+
exporting: "Exporting\u2026",
|
|
120
|
+
loadBundle: "Load a bundle to preview output files.",
|
|
121
|
+
noTranslations: "There are no translations to export.",
|
|
122
|
+
showDiff: "Show diff",
|
|
123
|
+
status: { create: "NEW", modify: "MODIFIED", unchanged: "UNCHANGED", delete: "DELETE" }
|
|
124
|
+
},
|
|
125
|
+
messages: {
|
|
126
|
+
connecting: "Connecting to the project\u2026",
|
|
127
|
+
retry: "Try again",
|
|
128
|
+
configurationUpdated: "Configuration updated.",
|
|
129
|
+
moveKeyPrompt: "Move key to:",
|
|
130
|
+
keyMoved: (source, target) => `Key moved from ${source} to ${target}.`,
|
|
131
|
+
removeKeyConfirm: (key) => `Remove key "${key}" from every language?
|
|
132
|
+
|
|
133
|
+
The change will remain in the in-memory bundle until you export it to the project.`,
|
|
134
|
+
keyRemoved: (key) => `Key ${key} removed from every language. Export to apply the change to the project.`,
|
|
135
|
+
translationUpdated: (language, key) => `Translation ${language}.${key} updated.`,
|
|
136
|
+
jsonObjectExpected: (field) => `${field} must be a JSON object.`,
|
|
137
|
+
couldNotSaveSettings: (error) => `Could not save settings: ${error}`,
|
|
138
|
+
couldNotSaveLocalCopy: (error) => `Could not save the local copy: ${error}`,
|
|
139
|
+
comparingFiles: "Comparing with the current project files\u2026",
|
|
140
|
+
recalculating: "Recalculating the plan before export\u2026",
|
|
141
|
+
writingFiles: "Writing files to the project\u2026",
|
|
142
|
+
exportCancelled: "Export cancelled. No files were changed.",
|
|
143
|
+
projectUpToDate: "The project files are already up to date.",
|
|
144
|
+
exported: "Changes exported to the project files.",
|
|
145
|
+
generatingBundle: "Generating the project bundle\u2026",
|
|
146
|
+
generatedBundle: "Bundle generated automatically from the project catalog.",
|
|
147
|
+
restoredBundle: "The newest version was restored from the browser.",
|
|
148
|
+
loadedBundle: "Project bundle loaded automatically.",
|
|
149
|
+
newerBundle: "A newer bundle exists in the project. It was loaded and will replace the browser copy.",
|
|
150
|
+
noUsageCache: "No usage cache was found. Analyzing typed references\u2026",
|
|
151
|
+
unverifiedUsageCache: "Showing an unverified cached usage report while a fresh analysis runs\u2026",
|
|
152
|
+
usageSummary: (used, uncertain, unreferenced, files) => `${used} used keys, ${uncertain} uncertain keys, and ${unreferenced} unreferenced keys across ${files} source files.`,
|
|
153
|
+
couldNotAnalyzeUsages: (error) => `Could not analyze usages: ${error}`,
|
|
154
|
+
plannedChanges: (count) => `${count} file${count === 1 ? "" : "s"} with planned changes.`,
|
|
155
|
+
projectMatchesPreview: "The project files already match the preview.",
|
|
156
|
+
couldNotCheckDiffs: (error) => `Could not check diffs: ${error}`,
|
|
157
|
+
autoDeleteWarning: (count) => `${count} divergent file${count === 1 ? "" : "s"} will be deleted because autoDelete is enabled.`,
|
|
158
|
+
preservedDeletionWarning: (count) => `${count} deletion candidate${count === 1 ? "" : "s"} will be preserved without i18n-edit export --delete.`,
|
|
159
|
+
noFilesDeleted: "No files will be deleted.",
|
|
160
|
+
filesWillBeDeleted: (count) => `${count} divergent file${count === 1 ? "" : "s"} will be deleted.`,
|
|
161
|
+
filesWillBePreserved: (count) => `${count} divergent file${count === 1 ? "" : "s"} will be preserved.`,
|
|
162
|
+
exportConfirm: (writable, deletion) => `Export changes to the project?
|
|
163
|
+
|
|
164
|
+
${writable} file${writable === 1 ? "" : "s"} will be created or overwritten. ${deletion}
|
|
165
|
+
|
|
166
|
+
Review the displayed diffs before continuing.`,
|
|
167
|
+
exportResult: (written, deleted, preserved) => `${written} file${written === 1 ? "" : "s"} written.${deleted ? ` ${deleted} deleted.` : ""}${preserved ? ` ${preserved} divergent file${preserved === 1 ? "" : "s"} preserved.` : ""}`,
|
|
168
|
+
couldNotExport: (error) => `Could not export: ${error}`,
|
|
169
|
+
importAliasesUpdated: "Import aliases updated.",
|
|
170
|
+
doubleQuotesEnabled: "Exports will use double quotes.",
|
|
171
|
+
singleQuotesEnabled: "Exports will use single quotes.",
|
|
172
|
+
semicolonsEnabled: "Exports will use semicolons.",
|
|
173
|
+
semicolonsDisabled: "Exports will not use semicolons.",
|
|
174
|
+
shorthandUpdated: "Shorthand property preference updated.",
|
|
175
|
+
trailingCommasUpdated: "Trailing comma preference updated.",
|
|
176
|
+
indentationUpdated: "Export indentation updated.",
|
|
177
|
+
printWidthUpdated: "Maximum export line width updated.",
|
|
178
|
+
inlineLimitsUpdated: "Inline collection limits updated.",
|
|
179
|
+
layoutsUpdated: "Object and array layouts updated.",
|
|
180
|
+
languageReplacerUpdated: "Language replacer updated.",
|
|
181
|
+
deletionDetectionEnabled: "Divergent file detection enabled.",
|
|
182
|
+
deletionDetectionDisabled: "Deletion detection and warnings disabled.",
|
|
183
|
+
ignoredExtensionsUpdated: "Ignored extensions updated.",
|
|
184
|
+
autoDeleteEnabled: "Automatic deletion enabled for this project.",
|
|
185
|
+
autoDeleteDisabled: "Deletions will require the --delete option again.",
|
|
186
|
+
missingCatalog: "The project has no bundle and its TypeScript catalog could not be found.",
|
|
187
|
+
couldNotLoadProject: (error) => `Could not load the project: ${error}`
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
en_default = en;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// src/web/i18n/pt.ts
|
|
195
|
+
var pt_exports = {};
|
|
196
|
+
__export(pt_exports, {
|
|
197
|
+
default: () => pt_default
|
|
198
|
+
});
|
|
199
|
+
var pt, pt_default;
|
|
200
|
+
var init_pt = __esm({
|
|
201
|
+
"src/web/i18n/pt.ts"() {
|
|
202
|
+
"use strict";
|
|
203
|
+
pt = {
|
|
204
|
+
language: { label: "Idioma", english: "English", portuguese: "Portugu\xEAs" },
|
|
205
|
+
common: {
|
|
206
|
+
root: "Raiz",
|
|
207
|
+
level: (index) => `N\xEDvel ${index}`,
|
|
208
|
+
keys: (count) => `${count} chave${count === 1 ? "" : "s"}`
|
|
209
|
+
},
|
|
210
|
+
page: {
|
|
211
|
+
eyebrow: "bundle i18n",
|
|
212
|
+
title: "Editor de tradu\xE7\xF5es",
|
|
213
|
+
intro: "O editor detecta a configura\xE7\xE3o e a bundle do projeto atual. Quando necess\xE1rio, ele gera a bundle diretamente do cat\xE1logo TypeScript."
|
|
214
|
+
},
|
|
215
|
+
settings: {
|
|
216
|
+
show: "Mostrar configura\xE7\xF5es",
|
|
217
|
+
hide: "Ocultar configura\xE7\xF5es",
|
|
218
|
+
title: "Configura\xE7\xF5es do projeto",
|
|
219
|
+
intro: "Defina a estrutura de chaves e os caminhos gerados pelas exporta\xE7\xF5es a partir da raiz do projeto.",
|
|
220
|
+
levelCount: "Quantidade de n\xEDveis",
|
|
221
|
+
levelNames: "Nomes dos n\xEDveis",
|
|
222
|
+
importsByLevel: "Imports por n\xEDvel",
|
|
223
|
+
importsHelp: "O n\xEDvel 0 representa a raiz e n\xE3o consome um segmento da chave. Use {value} nos n\xEDveis restantes. Os substituidores aceitam JSON plano ou aninhado.",
|
|
224
|
+
pathTemplate: "Template do caminho",
|
|
225
|
+
valueReplacer: "Substituidor de valor",
|
|
226
|
+
fullReplacer: "Substituidor completo",
|
|
227
|
+
translationsDirectory: "Diret\xF3rio de tradu\xE7\xF5es",
|
|
228
|
+
languageFileTemplate: "Template do arquivo de idioma",
|
|
229
|
+
languageReplacer: "Substituidor de idioma (JSON)",
|
|
230
|
+
exportTitle: "Exporta\xE7\xE3o",
|
|
231
|
+
exportIntro: "Configure a resolu\xE7\xE3o de caminhos e a formata\xE7\xE3o dos arquivos TypeScript gerados.",
|
|
232
|
+
importAliases: "Aliases de importa\xE7\xE3o (JSON)",
|
|
233
|
+
doubleQuotes: "Usar aspas duplas nas strings geradas",
|
|
234
|
+
semicolons: "Usar ponto e v\xEDrgula",
|
|
235
|
+
shorthandProperties: "Usar propriedades abreviadas",
|
|
236
|
+
trailingCommas: "Usar v\xEDrgulas finais",
|
|
237
|
+
indentationCharacter: "Caractere de indenta\xE7\xE3o",
|
|
238
|
+
space: "Espa\xE7o",
|
|
239
|
+
tab: "Tab",
|
|
240
|
+
indentationSize: "Tamanho da indenta\xE7\xE3o",
|
|
241
|
+
printWidth: "Largura m\xE1xima da linha",
|
|
242
|
+
maxObjectInlineItems: "M\xE1ximo de propriedades inline",
|
|
243
|
+
maxArrayInlineItems: "M\xE1ximo de itens inline",
|
|
244
|
+
objectLayout: "Layout de objetos",
|
|
245
|
+
arrayLayout: "Layout de arrays",
|
|
246
|
+
inlineWhenFits: "Inline quando couber",
|
|
247
|
+
alwaysMultiline: "Sempre multilinha",
|
|
248
|
+
divergentFiles: "Arquivos divergentes",
|
|
249
|
+
divergentFilesHelp: "Arquivos dentro dos diret\xF3rios de tradu\xE7\xE3o que n\xE3o est\xE3o no plano s\xE3o candidatos \xE0 exclus\xE3o.",
|
|
250
|
+
detectDeletionCandidates: "Detectar e avisar sobre candidatos \xE0 exclus\xE3o",
|
|
251
|
+
ignoredExtensions: "Extens\xF5es ignoradas",
|
|
252
|
+
allowDeletion: "Permitir exclus\xE3o sem --delete"
|
|
253
|
+
},
|
|
254
|
+
editor: {
|
|
255
|
+
title: "Tradu\xE7\xF5es",
|
|
256
|
+
emptySummary: "Carregue uma bundle para ver as tradu\xE7\xF5es.",
|
|
257
|
+
noBundle: "Nenhuma bundle carregada.",
|
|
258
|
+
analyzeUsages: "Analisar usos",
|
|
259
|
+
updateUsages: "Atualizar usos",
|
|
260
|
+
loadingUsages: "Carregando usos\u2026",
|
|
261
|
+
updatingUsages: "Atualizando usos\u2026",
|
|
262
|
+
unreferencedOnly: "Somente n\xE3o referenciadas",
|
|
263
|
+
visualizationLevels: "N\xEDveis de visualiza\xE7\xE3o",
|
|
264
|
+
search: "Pesquisar",
|
|
265
|
+
searchPlaceholder: "Chave ou tradu\xE7\xE3o",
|
|
266
|
+
key: "Chave",
|
|
267
|
+
usages: "Usos",
|
|
268
|
+
base: "Base",
|
|
269
|
+
missing: "Ausente",
|
|
270
|
+
inconsistent: "Inconsistente",
|
|
271
|
+
notAnalyzed: "N\xE3o analisada",
|
|
272
|
+
viewDetails: "Ver detalhes",
|
|
273
|
+
exactReferences: (count) => `${count} refer\xEAncia${count === 1 ? "" : "s"} exata${count === 1 ? "" : "s"}`,
|
|
274
|
+
uncertainReferences: (count) => `${count} refer\xEAncia${count === 1 ? "" : "s"} incerta${count === 1 ? "" : "s"}`,
|
|
275
|
+
usageSummary: (references, files, uncertain) => `${uncertain ? "\u26A0 " : ""}${references} ${uncertain ? "incerta" : "ref"}${references === 1 ? "" : "s"} \xB7 ${files} arquivo${files === 1 ? "" : "s"}`,
|
|
276
|
+
potentiallyReachable: "Refer\xEAncias potencialmente alcan\xE7\xE1veis",
|
|
277
|
+
noStaticReferences: "Sem refer\xEAncias est\xE1ticas",
|
|
278
|
+
referencesFound: "Refer\xEAncias encontradas",
|
|
279
|
+
dynamicMayReach: "Um acesso din\xE2mico pode alcan\xE7ar esta chave.",
|
|
280
|
+
noExactReferences: "Nenhuma refer\xEAncia exata foi encontrada.",
|
|
281
|
+
noLocations: "Nenhuma localiza\xE7\xE3o no c\xF3digo-fonte foi registrada.",
|
|
282
|
+
doubleClickMove: "Clique duas vezes para mover esta chave",
|
|
283
|
+
doubleClickEdit: "Clique duas vezes para editar esta tradu\xE7\xE3o",
|
|
284
|
+
editTranslation: "Editar tradu\xE7\xE3o",
|
|
285
|
+
removeKey: (key) => `Remover chave ${key}`,
|
|
286
|
+
bundleSummary: (keys, languages) => `${keys} chaves em ${languages} idiomas.`
|
|
287
|
+
},
|
|
288
|
+
exportPreview: {
|
|
289
|
+
title: "Pr\xE9via dos arquivos gerados",
|
|
290
|
+
intro: "Esta \xE9 uma simula\xE7\xE3o em tempo real. Verifique as diferen\xE7as para comparar o resultado com os arquivos atuais do projeto.",
|
|
291
|
+
deleteDivergent: "Excluir arquivos divergentes",
|
|
292
|
+
checkDiffs: "Verificar diferen\xE7as",
|
|
293
|
+
checking: "Verificando\u2026",
|
|
294
|
+
checkAgain: "Verificar novamente",
|
|
295
|
+
exportProject: "Exportar para o projeto",
|
|
296
|
+
preparing: "Preparando\u2026",
|
|
297
|
+
exporting: "Exportando\u2026",
|
|
298
|
+
loadBundle: "Carregue uma bundle para prever os arquivos de sa\xEDda.",
|
|
299
|
+
noTranslations: "N\xE3o h\xE1 tradu\xE7\xF5es para exportar.",
|
|
300
|
+
showDiff: "Mostrar diferen\xE7as",
|
|
301
|
+
status: { create: "NOVO", modify: "MODIFICADO", unchanged: "INALTERADO", delete: "EXCLUIR" }
|
|
302
|
+
},
|
|
303
|
+
messages: {
|
|
304
|
+
connecting: "Conectando ao projeto\u2026",
|
|
305
|
+
retry: "Tentar novamente",
|
|
306
|
+
configurationUpdated: "Configura\xE7\xE3o atualizada.",
|
|
307
|
+
moveKeyPrompt: "Mover chave para:",
|
|
308
|
+
keyMoved: (source, target) => `Chave movida de ${source} para ${target}.`,
|
|
309
|
+
removeKeyConfirm: (key) => `Remover a chave "${key}" de todos os idiomas?
|
|
310
|
+
|
|
311
|
+
A altera\xE7\xE3o permanecer\xE1 na bundle em mem\xF3ria at\xE9 que voc\xEA a exporte para o projeto.`,
|
|
312
|
+
keyRemoved: (key) => `Chave ${key} removida de todos os idiomas. Exporte para aplicar a altera\xE7\xE3o ao projeto.`,
|
|
313
|
+
translationUpdated: (language, key) => `Tradu\xE7\xE3o ${language}.${key} atualizada.`,
|
|
314
|
+
jsonObjectExpected: (field) => `${field} deve ser um objeto JSON.`,
|
|
315
|
+
couldNotSaveSettings: (error) => `N\xE3o foi poss\xEDvel salvar as configura\xE7\xF5es: ${error}`,
|
|
316
|
+
couldNotSaveLocalCopy: (error) => `N\xE3o foi poss\xEDvel salvar a c\xF3pia local: ${error}`,
|
|
317
|
+
comparingFiles: "Comparando com os arquivos atuais do projeto\u2026",
|
|
318
|
+
recalculating: "Recalculando o plano antes da exporta\xE7\xE3o\u2026",
|
|
319
|
+
writingFiles: "Gravando arquivos no projeto\u2026",
|
|
320
|
+
exportCancelled: "Exporta\xE7\xE3o cancelada. Nenhum arquivo foi alterado.",
|
|
321
|
+
projectUpToDate: "Os arquivos do projeto j\xE1 est\xE3o atualizados.",
|
|
322
|
+
exported: "Altera\xE7\xF5es exportadas para os arquivos do projeto.",
|
|
323
|
+
generatingBundle: "Gerando a bundle do projeto\u2026",
|
|
324
|
+
generatedBundle: "Bundle gerada automaticamente a partir do cat\xE1logo do projeto.",
|
|
325
|
+
restoredBundle: "A vers\xE3o mais recente foi restaurada do navegador.",
|
|
326
|
+
loadedBundle: "Bundle do projeto carregada automaticamente.",
|
|
327
|
+
newerBundle: "Existe uma bundle mais recente no projeto. Ela foi carregada e substituir\xE1 a c\xF3pia do navegador.",
|
|
328
|
+
noUsageCache: "Nenhum cache de usos foi encontrado. Analisando refer\xEAncias tipadas\u2026",
|
|
329
|
+
unverifiedUsageCache: "Exibindo um cache de usos n\xE3o verificado enquanto uma nova an\xE1lise \xE9 executada\u2026",
|
|
330
|
+
usageSummary: (used, uncertain, unreferenced, files) => `${used} chaves usadas, ${uncertain} incertas e ${unreferenced} n\xE3o referenciadas em ${files} arquivos de c\xF3digo-fonte.`,
|
|
331
|
+
couldNotAnalyzeUsages: (error) => `N\xE3o foi poss\xEDvel analisar os usos: ${error}`,
|
|
332
|
+
plannedChanges: (count) => `${count} arquivo${count === 1 ? "" : "s"} com altera\xE7\xF5es planejadas.`,
|
|
333
|
+
projectMatchesPreview: "Os arquivos do projeto j\xE1 correspondem \xE0 pr\xE9via.",
|
|
334
|
+
couldNotCheckDiffs: (error) => `N\xE3o foi poss\xEDvel verificar as diferen\xE7as: ${error}`,
|
|
335
|
+
autoDeleteWarning: (count) => `${count} arquivo${count === 1 ? "" : "s"} divergente${count === 1 ? "" : "s"} ser\xE1${count === 1 ? "" : "\xE3o"} exclu\xEDdo${count === 1 ? "" : "s"} porque autoDelete est\xE1 ativado.`,
|
|
336
|
+
preservedDeletionWarning: (count) => `${count} candidato${count === 1 ? "" : "s"} \xE0 exclus\xE3o ser\xE1${count === 1 ? "" : "\xE3o"} preservado${count === 1 ? "" : "s"} sem i18n-edit export --delete.`,
|
|
337
|
+
noFilesDeleted: "Nenhum arquivo ser\xE1 exclu\xEDdo.",
|
|
338
|
+
filesWillBeDeleted: (count) => `${count} arquivo${count === 1 ? "" : "s"} divergente${count === 1 ? "" : "s"} ser\xE1${count === 1 ? "" : "\xE3o"} exclu\xEDdo${count === 1 ? "" : "s"}.`,
|
|
339
|
+
filesWillBePreserved: (count) => `${count} arquivo${count === 1 ? "" : "s"} divergente${count === 1 ? "" : "s"} ser\xE1${count === 1 ? "" : "\xE3o"} preservado${count === 1 ? "" : "s"}.`,
|
|
340
|
+
exportConfirm: (writable, deletion) => `Exportar altera\xE7\xF5es para o projeto?
|
|
341
|
+
|
|
342
|
+
${writable} arquivo${writable === 1 ? "" : "s"} ser\xE1${writable === 1 ? "" : "\xE3o"} criado${writable === 1 ? "" : "s"} ou sobrescrito${writable === 1 ? "" : "s"}. ${deletion}
|
|
343
|
+
|
|
344
|
+
Revise as diferen\xE7as exibidas antes de continuar.`,
|
|
345
|
+
exportResult: (written, deleted, preserved) => `${written} arquivo${written === 1 ? "" : "s"} gravado${written === 1 ? "" : "s"}.${deleted ? ` ${deleted} exclu\xEDdo${deleted === 1 ? "" : "s"}.` : ""}${preserved ? ` ${preserved} arquivo${preserved === 1 ? "" : "s"} divergente${preserved === 1 ? "" : "s"} preservado${preserved === 1 ? "" : "s"}.` : ""}`,
|
|
346
|
+
couldNotExport: (error) => `N\xE3o foi poss\xEDvel exportar: ${error}`,
|
|
347
|
+
importAliasesUpdated: "Aliases de importa\xE7\xE3o atualizados.",
|
|
348
|
+
doubleQuotesEnabled: "As exporta\xE7\xF5es usar\xE3o aspas duplas.",
|
|
349
|
+
singleQuotesEnabled: "As exporta\xE7\xF5es usar\xE3o aspas simples.",
|
|
350
|
+
semicolonsEnabled: "As exporta\xE7\xF5es usar\xE3o ponto e v\xEDrgula.",
|
|
351
|
+
semicolonsDisabled: "As exporta\xE7\xF5es n\xE3o usar\xE3o ponto e v\xEDrgula.",
|
|
352
|
+
shorthandUpdated: "Prefer\xEAncia de propriedades abreviadas atualizada.",
|
|
353
|
+
trailingCommasUpdated: "Prefer\xEAncia de v\xEDrgulas finais atualizada.",
|
|
354
|
+
indentationUpdated: "Indenta\xE7\xE3o da exporta\xE7\xE3o atualizada.",
|
|
355
|
+
printWidthUpdated: "Largura m\xE1xima da linha atualizada.",
|
|
356
|
+
inlineLimitsUpdated: "Limites de cole\xE7\xF5es inline atualizados.",
|
|
357
|
+
layoutsUpdated: "Layouts de objetos e arrays atualizados.",
|
|
358
|
+
languageReplacerUpdated: "Substituidor de idioma atualizado.",
|
|
359
|
+
deletionDetectionEnabled: "Detec\xE7\xE3o de arquivos divergentes ativada.",
|
|
360
|
+
deletionDetectionDisabled: "Detec\xE7\xE3o e avisos de exclus\xE3o desativados.",
|
|
361
|
+
ignoredExtensionsUpdated: "Extens\xF5es ignoradas atualizadas.",
|
|
362
|
+
autoDeleteEnabled: "Exclus\xE3o autom\xE1tica ativada para este projeto.",
|
|
363
|
+
autoDeleteDisabled: "As exclus\xF5es voltar\xE3o a exigir a op\xE7\xE3o --delete.",
|
|
364
|
+
missingCatalog: "O projeto n\xE3o possui uma bundle e seu cat\xE1logo TypeScript n\xE3o foi encontrado.",
|
|
365
|
+
couldNotLoadProject: (error) => `N\xE3o foi poss\xEDvel carregar o projeto: ${error}`
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
pt_default = pt;
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
1
372
|
// node_modules/@wads.dev/i18n-ts/dist/bundle/index.js
|
|
2
373
|
var BUNDLE_FORMAT = "wads-i18n-bundle";
|
|
3
374
|
var BUNDLE_VERSION = 2;
|
|
@@ -41,6 +412,9 @@ var STORE_NAME = "bundles";
|
|
|
41
412
|
var CURRENT_BUNDLE_KEY = "current";
|
|
42
413
|
var PROJECT_CONFIG_KEY = "project-config";
|
|
43
414
|
var LEGACY_VISUALIZATION_CONFIG_KEY = "visualization-config";
|
|
415
|
+
function projectKey(projectDirectory, key) {
|
|
416
|
+
return `${key}:${projectDirectory}`;
|
|
417
|
+
}
|
|
44
418
|
function requestResult(request) {
|
|
45
419
|
return new Promise((resolve, reject) => {
|
|
46
420
|
request.onsuccess = () => resolve(request.result);
|
|
@@ -66,12 +440,12 @@ function openDatabase() {
|
|
|
66
440
|
request.onerror = () => reject(request.error);
|
|
67
441
|
});
|
|
68
442
|
}
|
|
69
|
-
async function loadStoredBundle() {
|
|
443
|
+
async function loadStoredBundle(projectDirectory) {
|
|
70
444
|
const database = await openDatabase();
|
|
71
445
|
try {
|
|
72
446
|
const transaction = database.transaction(STORE_NAME, "readonly");
|
|
73
447
|
const record = await requestResult(
|
|
74
|
-
transaction.objectStore(STORE_NAME).get(CURRENT_BUNDLE_KEY)
|
|
448
|
+
transaction.objectStore(STORE_NAME).get(projectKey(projectDirectory, CURRENT_BUNDLE_KEY))
|
|
75
449
|
);
|
|
76
450
|
await transactionComplete(transaction);
|
|
77
451
|
return record ? assertBundle(record.bundle) : null;
|
|
@@ -79,34 +453,34 @@ async function loadStoredBundle() {
|
|
|
79
453
|
database.close();
|
|
80
454
|
}
|
|
81
455
|
}
|
|
82
|
-
async function saveStoredBundle(bundle) {
|
|
456
|
+
async function saveStoredBundle(projectDirectory, bundle) {
|
|
83
457
|
const database = await openDatabase();
|
|
84
458
|
try {
|
|
85
459
|
const transaction = database.transaction(STORE_NAME, "readwrite");
|
|
86
|
-
await requestResult(transaction.objectStore(STORE_NAME).put({ id: CURRENT_BUNDLE_KEY, bundle }));
|
|
460
|
+
await requestResult(transaction.objectStore(STORE_NAME).put({ id: projectKey(projectDirectory, CURRENT_BUNDLE_KEY), bundle }));
|
|
87
461
|
await transactionComplete(transaction);
|
|
88
462
|
} finally {
|
|
89
463
|
database.close();
|
|
90
464
|
}
|
|
91
465
|
}
|
|
92
|
-
async function loadStoredProjectConfig() {
|
|
466
|
+
async function loadStoredProjectConfig(projectDirectory) {
|
|
93
467
|
const database = await openDatabase();
|
|
94
468
|
try {
|
|
95
469
|
const transaction = database.transaction(STORE_NAME, "readonly");
|
|
96
470
|
const store = transaction.objectStore(STORE_NAME);
|
|
97
|
-
const record = await requestResult(store.get(PROJECT_CONFIG_KEY));
|
|
98
|
-
const legacyRecord = record ? void 0 : await requestResult(store.get(LEGACY_VISUALIZATION_CONFIG_KEY));
|
|
471
|
+
const record = await requestResult(store.get(projectKey(projectDirectory, PROJECT_CONFIG_KEY)));
|
|
472
|
+
const legacyRecord = record ? void 0 : await requestResult(store.get(projectKey(projectDirectory, LEGACY_VISUALIZATION_CONFIG_KEY)));
|
|
99
473
|
await transactionComplete(transaction);
|
|
100
474
|
return record?.config ?? legacyRecord?.config ?? null;
|
|
101
475
|
} finally {
|
|
102
476
|
database.close();
|
|
103
477
|
}
|
|
104
478
|
}
|
|
105
|
-
async function saveStoredProjectConfig(config) {
|
|
479
|
+
async function saveStoredProjectConfig(projectDirectory, config) {
|
|
106
480
|
const database = await openDatabase();
|
|
107
481
|
try {
|
|
108
482
|
const transaction = database.transaction(STORE_NAME, "readwrite");
|
|
109
|
-
await requestResult(transaction.objectStore(STORE_NAME).put({ id: PROJECT_CONFIG_KEY, config }));
|
|
483
|
+
await requestResult(transaction.objectStore(STORE_NAME).put({ id: projectKey(projectDirectory, PROJECT_CONFIG_KEY), config }));
|
|
110
484
|
await transactionComplete(transaction);
|
|
111
485
|
} finally {
|
|
112
486
|
database.close();
|
|
@@ -146,15 +520,24 @@ function getKeyPathValue(root, segments) {
|
|
|
146
520
|
return { found: true, value: current };
|
|
147
521
|
}
|
|
148
522
|
function deleteKeyPathValue(root, segments) {
|
|
149
|
-
const
|
|
150
|
-
const property = segments.at(-1);
|
|
523
|
+
const containers = [];
|
|
151
524
|
let parent = root;
|
|
152
|
-
for (const segment of
|
|
525
|
+
for (const segment of segments.slice(0, -1)) {
|
|
526
|
+
containers.push({ container: parent, segment });
|
|
153
527
|
const nested = asRecord(parent)[segment];
|
|
154
528
|
if (!isContainer(nested)) throw new Error(`The key path is invalid at ${String(segment)}.`);
|
|
155
529
|
parent = nested;
|
|
156
530
|
}
|
|
157
|
-
|
|
531
|
+
const property = segments.at(-1);
|
|
532
|
+
if (Array.isArray(parent) && typeof property === "number") parent.splice(property, 1);
|
|
533
|
+
else delete asRecord(parent)[property];
|
|
534
|
+
for (let index = containers.length - 1; index >= 0; index -= 1) {
|
|
535
|
+
const { container, segment } = containers[index];
|
|
536
|
+
const child = asRecord(container)[segment];
|
|
537
|
+
if (!isContainer(child) || Object.keys(child).length > 0) break;
|
|
538
|
+
if (Array.isArray(container) && typeof segment === "number") container.splice(segment, 1);
|
|
539
|
+
else delete asRecord(container)[segment];
|
|
540
|
+
}
|
|
158
541
|
}
|
|
159
542
|
function setKeyPathValue(root, segments, value) {
|
|
160
543
|
const property = segments.at(-1);
|
|
@@ -203,6 +586,22 @@ function moveKey(bundle, { sourceKey, targetKey }) {
|
|
|
203
586
|
return nextBundle;
|
|
204
587
|
}
|
|
205
588
|
|
|
589
|
+
// src/core/removeKey.ts
|
|
590
|
+
function removeKey(bundle, { key }) {
|
|
591
|
+
const validBundle = assertBundle(bundle);
|
|
592
|
+
const segments = parseKeyPath(key, "key");
|
|
593
|
+
Object.entries(validBundle.languages).forEach(([languageKey, language]) => {
|
|
594
|
+
if (!getKeyPathValue(language.translations, segments).found) {
|
|
595
|
+
throw new Error(`Key "${key}" does not exist in language "${languageKey}".`);
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
const nextBundle = structuredClone(validBundle);
|
|
599
|
+
Object.values(nextBundle.languages).forEach((language) => {
|
|
600
|
+
deleteKeyPathValue(language.translations, segments);
|
|
601
|
+
});
|
|
602
|
+
return nextBundle;
|
|
603
|
+
}
|
|
604
|
+
|
|
206
605
|
// node_modules/@wads.dev/i18n-ts/dist/config/index.js
|
|
207
606
|
var PROJECT_CONFIG_FORMAT = "wads-i18n-project-config";
|
|
208
607
|
var PROJECT_CONFIG_VERSION = 1;
|
|
@@ -596,12 +995,6 @@ function buildExportPlan(bundle, config) {
|
|
|
596
995
|
}
|
|
597
996
|
|
|
598
997
|
// src/web/export-preview.ts
|
|
599
|
-
var statusLabels = {
|
|
600
|
-
create: "new",
|
|
601
|
-
modify: "modified",
|
|
602
|
-
unchanged: "unchanged",
|
|
603
|
-
delete: "delete"
|
|
604
|
-
};
|
|
605
998
|
function createDiff(document2, diff) {
|
|
606
999
|
const pre = document2.createElement("pre");
|
|
607
1000
|
pre.className = "export-file-diff";
|
|
@@ -616,12 +1009,12 @@ function createDiff(document2, diff) {
|
|
|
616
1009
|
function renderExportPreview(container, bundle, config, checkedChanges = null) {
|
|
617
1010
|
const document2 = container.ownerDocument;
|
|
618
1011
|
if (!bundle) {
|
|
619
|
-
container.textContent =
|
|
1012
|
+
container.textContent = Lang.exportPreview.loadBundle;
|
|
620
1013
|
return;
|
|
621
1014
|
}
|
|
622
1015
|
const plan = checkedChanges ?? buildExportPlan(bundle, config);
|
|
623
1016
|
if (plan.length === 0) {
|
|
624
|
-
container.textContent =
|
|
1017
|
+
container.textContent = Lang.exportPreview.noTranslations;
|
|
625
1018
|
return;
|
|
626
1019
|
}
|
|
627
1020
|
const list = document2.createElement("ul");
|
|
@@ -634,7 +1027,7 @@ function renderExportPreview(container, bundle, config, checkedChanges = null) {
|
|
|
634
1027
|
if (status) {
|
|
635
1028
|
const badge = document2.createElement("span");
|
|
636
1029
|
badge.className = "export-preview-status";
|
|
637
|
-
badge.textContent =
|
|
1030
|
+
badge.textContent = Lang.exportPreview.status[status];
|
|
638
1031
|
line.append(badge);
|
|
639
1032
|
}
|
|
640
1033
|
const code = document2.createElement("code");
|
|
@@ -645,7 +1038,7 @@ function renderExportPreview(container, bundle, config, checkedChanges = null) {
|
|
|
645
1038
|
const details = document2.createElement("details");
|
|
646
1039
|
details.className = "export-diff-details";
|
|
647
1040
|
const summary = document2.createElement("summary");
|
|
648
|
-
summary.textContent =
|
|
1041
|
+
summary.textContent = Lang.exportPreview.showDiff;
|
|
649
1042
|
details.append(summary, createDiff(document2, diff));
|
|
650
1043
|
item.append(details);
|
|
651
1044
|
}
|
|
@@ -655,6 +1048,10 @@ function renderExportPreview(container, bundle, config, checkedChanges = null) {
|
|
|
655
1048
|
}
|
|
656
1049
|
|
|
657
1050
|
// src/web/project-settings.ts
|
|
1051
|
+
function getDisplayLevelName(config, index) {
|
|
1052
|
+
const configured = getEditorLevelName(config, index);
|
|
1053
|
+
return configured === `Level ${index}` ? Lang.common.level(index) : configured;
|
|
1054
|
+
}
|
|
658
1055
|
function createJsonField(document2, { labelText, value, placeholder, onInput }) {
|
|
659
1056
|
const label = document2.createElement("label");
|
|
660
1057
|
label.className = "level-field level-json-field";
|
|
@@ -676,11 +1073,11 @@ function renderLevelImportFields(container, config, onChange) {
|
|
|
676
1073
|
const card = document2.createElement("section");
|
|
677
1074
|
card.className = "level-import-card";
|
|
678
1075
|
const heading = document2.createElement("h4");
|
|
679
|
-
heading.textContent = index === 0 ?
|
|
1076
|
+
heading.textContent = index === 0 ? `${Lang.common.root} (${Lang.common.level(0).toLocaleLowerCase()})` : `${getDisplayLevelName(config, index)} (${Lang.common.level(index).toLocaleLowerCase()})`;
|
|
680
1077
|
const pathLabel = document2.createElement("label");
|
|
681
1078
|
pathLabel.className = "level-field level-path-field";
|
|
682
1079
|
const pathTitle = document2.createElement("span");
|
|
683
|
-
pathTitle.textContent =
|
|
1080
|
+
pathTitle.textContent = Lang.settings.pathTemplate;
|
|
684
1081
|
const pathInput = document2.createElement("input");
|
|
685
1082
|
pathInput.type = "text";
|
|
686
1083
|
pathInput.value = levelImport.path;
|
|
@@ -693,13 +1090,13 @@ function renderLevelImportFields(container, config, onChange) {
|
|
|
693
1090
|
replacers.className = "level-replacers";
|
|
694
1091
|
replacers.append(
|
|
695
1092
|
createJsonField(document2, {
|
|
696
|
-
labelText:
|
|
1093
|
+
labelText: `${Lang.settings.valueReplacer} (JSON)`,
|
|
697
1094
|
value: levelImport.valueReplacer,
|
|
698
1095
|
placeholder: '{ "module.feature": "folder-name" }',
|
|
699
1096
|
onInput: (value) => onChange(index, "valueReplacer", value)
|
|
700
1097
|
}),
|
|
701
1098
|
createJsonField(document2, {
|
|
702
|
-
labelText:
|
|
1099
|
+
labelText: `${Lang.settings.fullReplacer} (JSON)`,
|
|
703
1100
|
value: levelImport.fullReplacer,
|
|
704
1101
|
placeholder: '{ "module.feature": "@/custom/path" }',
|
|
705
1102
|
onInput: (value) => onChange(index, "fullReplacer", value)
|
|
@@ -739,6 +1136,20 @@ async function checkProjectExport(bundle, config) {
|
|
|
739
1136
|
body: JSON.stringify({ bundle, config })
|
|
740
1137
|
}));
|
|
741
1138
|
}
|
|
1139
|
+
async function exportProject(bundle, config, deleteObsolete = false) {
|
|
1140
|
+
return readResponse(await fetch("/api/export", {
|
|
1141
|
+
method: "POST",
|
|
1142
|
+
headers: { "content-type": "application/json" },
|
|
1143
|
+
body: JSON.stringify({ bundle, config, deleteObsolete })
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
async function analyzeProjectUsage(bundle, config, wait = false) {
|
|
1147
|
+
return readResponse(await fetch("/api/usage-analysis", {
|
|
1148
|
+
method: "POST",
|
|
1149
|
+
headers: { "content-type": "application/json" },
|
|
1150
|
+
body: JSON.stringify({ bundle, config, wait })
|
|
1151
|
+
}));
|
|
1152
|
+
}
|
|
742
1153
|
|
|
743
1154
|
// src/web/state.ts
|
|
744
1155
|
function createEditorState() {
|
|
@@ -754,7 +1165,7 @@ function createEditorState() {
|
|
|
754
1165
|
notify("replace");
|
|
755
1166
|
},
|
|
756
1167
|
update(updater) {
|
|
757
|
-
if (!bundle) throw new Error("
|
|
1168
|
+
if (!bundle) throw new Error("There is no loaded bundle to edit.");
|
|
758
1169
|
bundle = withUpdatedAt(updater(bundle));
|
|
759
1170
|
notify("edit");
|
|
760
1171
|
},
|
|
@@ -766,6 +1177,11 @@ function createEditorState() {
|
|
|
766
1177
|
}
|
|
767
1178
|
|
|
768
1179
|
// src/web/table.ts
|
|
1180
|
+
var activeUsagePopover = null;
|
|
1181
|
+
function getDisplayLevelName2(config, level) {
|
|
1182
|
+
const configured = getEditorLevelName(config, level);
|
|
1183
|
+
return configured === `Level ${level}` ? Lang.common.level(level) : configured;
|
|
1184
|
+
}
|
|
769
1185
|
function isFunctionDescriptor2(value) {
|
|
770
1186
|
return value && typeof value === "object" && value.$type === "function";
|
|
771
1187
|
}
|
|
@@ -779,12 +1195,12 @@ function splitFunctionSource(source) {
|
|
|
779
1195
|
}
|
|
780
1196
|
function describeBase(entries) {
|
|
781
1197
|
const presentEntries = entries.filter(Boolean);
|
|
782
|
-
if (presentEntries.length === 0) return { label:
|
|
1198
|
+
if (presentEntries.length === 0) return { label: Lang.editor.missing, type: "missing" };
|
|
783
1199
|
if (presentEntries.every((entry) => entry.type === "string")) return { label: "string", type: "string" };
|
|
784
1200
|
if (presentEntries.every((entry) => entry.type === "function")) {
|
|
785
1201
|
return { label: presentEntries[0].signature, type: "function" };
|
|
786
1202
|
}
|
|
787
|
-
return { label:
|
|
1203
|
+
return { label: Lang.editor.inconsistent, type: "inconsistent" };
|
|
788
1204
|
}
|
|
789
1205
|
function appendSegment(path, segment) {
|
|
790
1206
|
return segment.startsWith("[") ? `${path}${segment}` : path ? `${path}.${segment}` : segment;
|
|
@@ -812,13 +1228,106 @@ function createCell(document2, value, className) {
|
|
|
812
1228
|
cell.textContent = value;
|
|
813
1229
|
return cell;
|
|
814
1230
|
}
|
|
1231
|
+
function createUsageCell(document2, usage) {
|
|
1232
|
+
const cell = document2.createElement("td");
|
|
1233
|
+
cell.className = "usage-cell";
|
|
1234
|
+
if (!usage) {
|
|
1235
|
+
cell.classList.add("usage-empty");
|
|
1236
|
+
cell.textContent = Lang.editor.notAnalyzed;
|
|
1237
|
+
return cell;
|
|
1238
|
+
}
|
|
1239
|
+
const trigger = document2.createElement("button");
|
|
1240
|
+
trigger.type = "button";
|
|
1241
|
+
trigger.className = `usage-trigger usage-${usage.status}`;
|
|
1242
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
1243
|
+
const count = document2.createElement("span");
|
|
1244
|
+
count.textContent = Lang.editor.usageSummary(
|
|
1245
|
+
usage.status === "uncertain" ? usage.uncertainReferenceCount : usage.referenceCount,
|
|
1246
|
+
usage.fileCount,
|
|
1247
|
+
usage.status === "uncertain"
|
|
1248
|
+
);
|
|
1249
|
+
const action = document2.createElement("span");
|
|
1250
|
+
action.className = "usage-trigger-action";
|
|
1251
|
+
action.textContent = Lang.editor.viewDetails;
|
|
1252
|
+
trigger.append(count, action);
|
|
1253
|
+
const references = document2.createElement("div");
|
|
1254
|
+
references.className = `usage-popover usage-popover-${usage.status}`;
|
|
1255
|
+
references.setAttribute("role", "dialog");
|
|
1256
|
+
const title = document2.createElement("strong");
|
|
1257
|
+
title.textContent = usage.status === "uncertain" ? Lang.editor.potentiallyReachable : usage.status === "unreferenced" ? Lang.editor.noStaticReferences : Lang.editor.referencesFound;
|
|
1258
|
+
references.append(title);
|
|
1259
|
+
const displayedReferences = usage.status === "uncertain" ? usage.uncertainReferences : usage.references;
|
|
1260
|
+
if (displayedReferences.length === 0) {
|
|
1261
|
+
const empty = document2.createElement("span");
|
|
1262
|
+
empty.textContent = usage.status === "uncertain" ? Lang.editor.dynamicMayReach : Lang.editor.noExactReferences;
|
|
1263
|
+
references.append(empty);
|
|
1264
|
+
} else {
|
|
1265
|
+
displayedReferences.forEach((reference) => {
|
|
1266
|
+
const location = document2.createElement("code");
|
|
1267
|
+
location.textContent = `${reference.file}:${reference.line}:${reference.column}`;
|
|
1268
|
+
references.append(location);
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
trigger.addEventListener("click", () => {
|
|
1272
|
+
if (activeUsagePopover?.trigger === trigger) {
|
|
1273
|
+
activeUsagePopover.close();
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
activeUsagePopover?.close();
|
|
1277
|
+
const view2 = document2.defaultView;
|
|
1278
|
+
if (!view2) return;
|
|
1279
|
+
document2.body.append(references);
|
|
1280
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
1281
|
+
const position = () => {
|
|
1282
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
1283
|
+
const popoverRect = references.getBoundingClientRect();
|
|
1284
|
+
const margin = 12;
|
|
1285
|
+
const left = Math.min(
|
|
1286
|
+
Math.max(margin, triggerRect.left),
|
|
1287
|
+
Math.max(margin, view2.innerWidth - popoverRect.width - margin)
|
|
1288
|
+
);
|
|
1289
|
+
const below = triggerRect.bottom + 8;
|
|
1290
|
+
const top = below + popoverRect.height <= view2.innerHeight - margin ? below : Math.max(margin, triggerRect.top - popoverRect.height - 8);
|
|
1291
|
+
references.style.left = `${left}px`;
|
|
1292
|
+
references.style.top = `${top}px`;
|
|
1293
|
+
};
|
|
1294
|
+
const close = () => {
|
|
1295
|
+
references.remove();
|
|
1296
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
1297
|
+
document2.removeEventListener("pointerdown", closeOnOutside);
|
|
1298
|
+
document2.removeEventListener("keydown", closeOnEscape);
|
|
1299
|
+
view2.removeEventListener("resize", close);
|
|
1300
|
+
view2.removeEventListener("scroll", close, true);
|
|
1301
|
+
if (activeUsagePopover?.trigger === trigger) activeUsagePopover = null;
|
|
1302
|
+
};
|
|
1303
|
+
const closeOnOutside = (event) => {
|
|
1304
|
+
const target = event.target;
|
|
1305
|
+
if (!target || references.contains(target) || trigger.contains(target)) return;
|
|
1306
|
+
close();
|
|
1307
|
+
};
|
|
1308
|
+
const closeOnEscape = (event) => {
|
|
1309
|
+
if (event.key === "Escape") {
|
|
1310
|
+
close();
|
|
1311
|
+
trigger.focus();
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
activeUsagePopover = { element: references, trigger, close };
|
|
1315
|
+
position();
|
|
1316
|
+
document2.addEventListener("pointerdown", closeOnOutside);
|
|
1317
|
+
document2.addEventListener("keydown", closeOnEscape);
|
|
1318
|
+
view2.addEventListener("resize", close);
|
|
1319
|
+
view2.addEventListener("scroll", close, true);
|
|
1320
|
+
});
|
|
1321
|
+
cell.append(trigger);
|
|
1322
|
+
return cell;
|
|
1323
|
+
}
|
|
815
1324
|
function beginTextEdit(cell, originalValue, onCommit) {
|
|
816
1325
|
let completed = false;
|
|
817
1326
|
const input = cell.ownerDocument.createElement("input");
|
|
818
1327
|
input.className = "inline-value-input";
|
|
819
1328
|
input.type = "text";
|
|
820
1329
|
input.value = originalValue;
|
|
821
|
-
input.setAttribute("aria-label",
|
|
1330
|
+
input.setAttribute("aria-label", Lang.editor.editTranslation);
|
|
822
1331
|
function finish(save) {
|
|
823
1332
|
if (completed) return;
|
|
824
1333
|
completed = true;
|
|
@@ -858,7 +1367,7 @@ function createRows(bundle) {
|
|
|
858
1367
|
};
|
|
859
1368
|
}
|
|
860
1369
|
function createGroupTree(rows, levelCount) {
|
|
861
|
-
const root = { depth: 0, name: "
|
|
1370
|
+
const root = { depth: 0, name: "Root", rows: [], children: /* @__PURE__ */ new Map() };
|
|
862
1371
|
rows.forEach((row) => {
|
|
863
1372
|
const segments = row.fullKey.split(".");
|
|
864
1373
|
const groupCount = Math.min(levelCount, Math.max(segments.length - 1, 0));
|
|
@@ -883,12 +1392,15 @@ function getNodeRowCount(node) {
|
|
|
883
1392
|
function getRelativeKey(fullKey, depth) {
|
|
884
1393
|
return fullKey.split(".").slice(depth).join(".");
|
|
885
1394
|
}
|
|
886
|
-
function createTranslationTable(document2, languages, rows, depth, { onMoveKey, onEditValue }) {
|
|
1395
|
+
function createTranslationTable(document2, languages, rows, depth, { onMoveKey, onRemoveKey, onEditValue, usageReport }) {
|
|
887
1396
|
const table = document2.createElement("table");
|
|
888
1397
|
const header = table.createTHead().insertRow();
|
|
889
1398
|
const keyHeader = document2.createElement("th");
|
|
890
|
-
keyHeader.textContent =
|
|
1399
|
+
keyHeader.textContent = Lang.editor.key;
|
|
891
1400
|
header.append(keyHeader);
|
|
1401
|
+
const usageHeader = document2.createElement("th");
|
|
1402
|
+
usageHeader.textContent = Lang.editor.usages;
|
|
1403
|
+
header.append(usageHeader);
|
|
892
1404
|
languages.forEach(([key, language]) => {
|
|
893
1405
|
const cell = document2.createElement("th");
|
|
894
1406
|
cell.className = "language-header";
|
|
@@ -896,7 +1408,7 @@ function createTranslationTable(document2, languages, rows, depth, { onMoveKey,
|
|
|
896
1408
|
header.append(cell);
|
|
897
1409
|
});
|
|
898
1410
|
const baseHeader = document2.createElement("th");
|
|
899
|
-
baseHeader.textContent =
|
|
1411
|
+
baseHeader.textContent = Lang.editor.base;
|
|
900
1412
|
header.append(baseHeader);
|
|
901
1413
|
const body = table.createTBody();
|
|
902
1414
|
rows.forEach((rowData) => {
|
|
@@ -906,18 +1418,44 @@ function createTranslationTable(document2, languages, rows, depth, { onMoveKey,
|
|
|
906
1418
|
fullKey,
|
|
907
1419
|
...languageEntries.map((entry) => entry?.text ?? "")
|
|
908
1420
|
].join(" ").toLocaleLowerCase();
|
|
909
|
-
|
|
1421
|
+
row.dataset.usageStatus = usageReport?.entries[fullKey]?.status || "not-analyzed";
|
|
1422
|
+
const keyCell = document2.createElement("td");
|
|
1423
|
+
keyCell.className = "key";
|
|
1424
|
+
const keyContent = document2.createElement("div");
|
|
1425
|
+
keyContent.className = "key-content";
|
|
1426
|
+
const keyLabel = document2.createElement("span");
|
|
1427
|
+
keyLabel.textContent = getRelativeKey(fullKey, depth);
|
|
910
1428
|
if (onMoveKey) {
|
|
911
|
-
keyCell.title =
|
|
1429
|
+
keyCell.title = Lang.editor.doubleClickMove;
|
|
912
1430
|
keyCell.addEventListener("dblclick", () => onMoveKey(fullKey));
|
|
913
1431
|
}
|
|
1432
|
+
if (onRemoveKey) {
|
|
1433
|
+
const removeButton = document2.createElement("button");
|
|
1434
|
+
removeButton.type = "button";
|
|
1435
|
+
removeButton.className = [
|
|
1436
|
+
"remove-key-button",
|
|
1437
|
+
usageReport?.entries[fullKey]?.status === "unreferenced" ? "remove-key-button-unreferenced" : ""
|
|
1438
|
+
].filter(Boolean).join(" ");
|
|
1439
|
+
removeButton.title = Lang.editor.removeKey(fullKey);
|
|
1440
|
+
removeButton.setAttribute("aria-label", Lang.editor.removeKey(fullKey));
|
|
1441
|
+
removeButton.textContent = "\xD7";
|
|
1442
|
+
removeButton.addEventListener("click", (event) => {
|
|
1443
|
+
event.stopPropagation();
|
|
1444
|
+
onRemoveKey(fullKey);
|
|
1445
|
+
});
|
|
1446
|
+
removeButton.addEventListener("dblclick", (event) => event.stopPropagation());
|
|
1447
|
+
keyContent.append(removeButton);
|
|
1448
|
+
}
|
|
1449
|
+
keyContent.append(keyLabel);
|
|
1450
|
+
keyCell.append(keyContent);
|
|
914
1451
|
row.append(keyCell);
|
|
1452
|
+
row.append(createUsageCell(document2, usageReport?.entries[fullKey]));
|
|
915
1453
|
languages.forEach(([languageKey], index) => {
|
|
916
1454
|
const entry = languageEntries[index];
|
|
917
1455
|
const className = ["language-value", entry?.type === "function" ? "function-value" : ""].filter(Boolean).join(" ");
|
|
918
|
-
const valueCell = createCell(document2, entry?.text ??
|
|
1456
|
+
const valueCell = createCell(document2, entry?.text ?? Lang.editor.missing, className);
|
|
919
1457
|
if (entry?.type === "string" && onEditValue) {
|
|
920
|
-
valueCell.title =
|
|
1458
|
+
valueCell.title = Lang.editor.doubleClickEdit;
|
|
921
1459
|
valueCell.addEventListener("dblclick", () => {
|
|
922
1460
|
beginTextEdit(valueCell, entry.text, (value) => onEditValue({
|
|
923
1461
|
languageKey,
|
|
@@ -940,7 +1478,7 @@ function appendTableGroup(document2, container, node, languages, callbacks, isRo
|
|
|
940
1478
|
rootGroup.className = "table-group";
|
|
941
1479
|
const title = document2.createElement("h3");
|
|
942
1480
|
title.className = "table-group-title";
|
|
943
|
-
title.textContent =
|
|
1481
|
+
title.textContent = Lang.common.root;
|
|
944
1482
|
rootGroup.append(title, createTranslationTable(document2, languages, node.rows, node.depth, callbacks));
|
|
945
1483
|
container.append(rootGroup);
|
|
946
1484
|
}
|
|
@@ -949,10 +1487,10 @@ function appendTableGroup(document2, container, node, languages, callbacks, isRo
|
|
|
949
1487
|
details.className = "translation-group";
|
|
950
1488
|
const summary = document2.createElement("summary");
|
|
951
1489
|
const label = document2.createElement("span");
|
|
952
|
-
label.textContent = `${
|
|
1490
|
+
label.textContent = `${getDisplayLevelName2(callbacks.projectConfig, node.depth)} \xB7 ${node.name}`;
|
|
953
1491
|
const count = document2.createElement("span");
|
|
954
1492
|
count.className = "group-count";
|
|
955
|
-
count.textContent =
|
|
1493
|
+
count.textContent = Lang.common.keys(getNodeRowCount(node));
|
|
956
1494
|
summary.append(label, count);
|
|
957
1495
|
details.append(summary);
|
|
958
1496
|
const content = document2.createElement("div");
|
|
@@ -963,7 +1501,7 @@ function appendTableGroup(document2, container, node, languages, callbacks, isRo
|
|
|
963
1501
|
if (node.children.size > 0) {
|
|
964
1502
|
const title = document2.createElement("h3");
|
|
965
1503
|
title.className = "table-group-title";
|
|
966
|
-
title.textContent =
|
|
1504
|
+
title.textContent = Lang.common.root;
|
|
967
1505
|
tableGroup.append(title);
|
|
968
1506
|
}
|
|
969
1507
|
tableGroup.append(createTranslationTable(document2, languages, node.rows, node.depth, callbacks));
|
|
@@ -975,58 +1513,164 @@ function appendTableGroup(document2, container, node, languages, callbacks, isRo
|
|
|
975
1513
|
}
|
|
976
1514
|
[...node.children.values()].sort((left, right) => left.name.localeCompare(right.name)).forEach((child) => appendTableGroup(document2, container, child, languages, callbacks));
|
|
977
1515
|
}
|
|
978
|
-
function renderTranslationTables(container, bundle, { projectConfig: projectConfig2, onMoveKey, onEditValue }) {
|
|
1516
|
+
function renderTranslationTables(container, bundle, { projectConfig: projectConfig2, usageReport, onMoveKey, onRemoveKey, onEditValue }) {
|
|
1517
|
+
activeUsagePopover?.close();
|
|
979
1518
|
const document2 = container.ownerDocument;
|
|
980
1519
|
const { languages, rows } = createRows(bundle);
|
|
981
1520
|
const normalizedLevelCount = Math.max(0, Number.isInteger(projectConfig2?.levelCount) ? projectConfig2.levelCount : 0);
|
|
982
1521
|
const tree = createGroupTree(rows, normalizedLevelCount);
|
|
983
1522
|
const fragment = document2.createDocumentFragment();
|
|
984
|
-
appendTableGroup(document2, fragment, tree, languages, { projectConfig: projectConfig2, onMoveKey, onEditValue }, true);
|
|
1523
|
+
appendTableGroup(document2, fragment, tree, languages, { projectConfig: projectConfig2, usageReport, onMoveKey, onRemoveKey, onEditValue }, true);
|
|
985
1524
|
container.replaceChildren(fragment);
|
|
986
1525
|
return rows.length;
|
|
987
1526
|
}
|
|
988
1527
|
|
|
989
1528
|
// src/web/view.ts
|
|
990
|
-
function createEditorView({ emptyState, tableWrap, tableContainer, summary, search, onMoveKey, onEditValue }) {
|
|
1529
|
+
function createEditorView({ emptyState, tableWrap, tableContainer, summary, search, onMoveKey, onRemoveKey, onEditValue }) {
|
|
991
1530
|
let currentRows = [];
|
|
992
1531
|
let currentBundle = null;
|
|
993
1532
|
let projectConfig2 = createDefaultEditorProjectConfig();
|
|
994
|
-
|
|
1533
|
+
let usageReport = null;
|
|
1534
|
+
let showUnreferenced = false;
|
|
1535
|
+
function applyFilters() {
|
|
995
1536
|
const query = search.value.trim().toLocaleLowerCase();
|
|
996
1537
|
currentRows.forEach((row) => {
|
|
997
|
-
|
|
1538
|
+
const matchesSearch = query === "" || row.dataset.search.includes(query);
|
|
1539
|
+
const matchesUsage = !showUnreferenced || row.dataset.usageStatus === "unreferenced";
|
|
1540
|
+
row.hidden = !matchesSearch || !matchesUsage;
|
|
998
1541
|
});
|
|
999
|
-
|
|
1542
|
+
tableContainer.querySelectorAll(".table-group, .translation-group").forEach((group) => {
|
|
1543
|
+
group.hidden = group.querySelector("tbody tr:not([hidden])") === null;
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
search.addEventListener("input", applyFilters);
|
|
1000
1547
|
return {
|
|
1001
1548
|
render(bundle) {
|
|
1002
1549
|
currentBundle = bundle;
|
|
1003
1550
|
if (!bundle) {
|
|
1004
1551
|
emptyState.hidden = false;
|
|
1005
1552
|
tableWrap.hidden = true;
|
|
1006
|
-
summary.textContent =
|
|
1553
|
+
summary.textContent = Lang.editor.emptySummary;
|
|
1007
1554
|
search.value = "";
|
|
1008
1555
|
currentRows = [];
|
|
1009
1556
|
return;
|
|
1010
1557
|
}
|
|
1011
1558
|
const keyCount = renderTranslationTables(tableContainer, bundle, {
|
|
1012
1559
|
projectConfig: projectConfig2,
|
|
1560
|
+
usageReport,
|
|
1013
1561
|
onMoveKey,
|
|
1562
|
+
onRemoveKey,
|
|
1014
1563
|
onEditValue
|
|
1015
1564
|
});
|
|
1016
1565
|
currentRows = [...tableContainer.querySelectorAll("tbody tr")];
|
|
1017
1566
|
emptyState.hidden = true;
|
|
1018
1567
|
tableWrap.hidden = false;
|
|
1019
|
-
|
|
1020
|
-
summary.textContent =
|
|
1568
|
+
applyFilters();
|
|
1569
|
+
summary.textContent = Lang.editor.bundleSummary(keyCount, Object.keys(bundle.languages).length);
|
|
1021
1570
|
},
|
|
1022
1571
|
setProjectConfig(nextProjectConfig) {
|
|
1023
1572
|
projectConfig2 = nextProjectConfig;
|
|
1024
1573
|
if (currentBundle) this.render(currentBundle);
|
|
1574
|
+
},
|
|
1575
|
+
setUsageReport(nextUsageReport) {
|
|
1576
|
+
usageReport = nextUsageReport;
|
|
1577
|
+
if (currentBundle) this.render(currentBundle);
|
|
1578
|
+
},
|
|
1579
|
+
setShowUnreferenced(value) {
|
|
1580
|
+
showUnreferenced = value;
|
|
1581
|
+
applyFilters();
|
|
1025
1582
|
}
|
|
1026
1583
|
};
|
|
1027
1584
|
}
|
|
1028
1585
|
|
|
1586
|
+
// node_modules/@wads.dev/i18n-ts/dist/runtime/language.js
|
|
1587
|
+
function detectLanguage() {
|
|
1588
|
+
if (typeof navigator !== "undefined" && typeof navigator.language === "string")
|
|
1589
|
+
return navigator.language;
|
|
1590
|
+
if (typeof Intl !== "undefined")
|
|
1591
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
1592
|
+
return void 0;
|
|
1593
|
+
}
|
|
1594
|
+
async function loadLanguage(availableLangs, defaultLang, language) {
|
|
1595
|
+
if (!language || !availableLangs[language]) {
|
|
1596
|
+
const browserLang = (detectLanguage() || "").replace("-", "").toLowerCase();
|
|
1597
|
+
const languageKeys = Object.keys(availableLangs).map((key) => key.toLowerCase());
|
|
1598
|
+
language = languageKeys.find((key) => key === browserLang) || languageKeys.find((key) => browserLang.startsWith(key)) || defaultLang;
|
|
1599
|
+
}
|
|
1600
|
+
const info = availableLangs[language];
|
|
1601
|
+
const lang = typeof info.lang === "function" ? await info.lang().then(({ default: loadedLang }) => loadedLang) : info.lang;
|
|
1602
|
+
return { ...info, lang: deepFreeze(lang) };
|
|
1603
|
+
}
|
|
1604
|
+
function deepFreeze(value) {
|
|
1605
|
+
Object.freeze(value);
|
|
1606
|
+
Object.getOwnPropertyNames(value).forEach((property) => {
|
|
1607
|
+
const nestedValue = value[property];
|
|
1608
|
+
if (nestedValue && (typeof nestedValue === "object" || typeof nestedValue === "function") && !Object.isFrozen(nestedValue)) {
|
|
1609
|
+
deepFreeze(nestedValue);
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
return value;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// src/web/i18n/index.ts
|
|
1616
|
+
var Langs = {
|
|
1617
|
+
en: { name: "English", short: "EN", locale: "en-US", lang: () => Promise.resolve().then(() => (init_en(), en_exports)) },
|
|
1618
|
+
pt: { name: "Portugu\xEAs", short: "PT", locale: "pt-BR", lang: () => Promise.resolve().then(() => (init_pt(), pt_exports)) }
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
// src/web/language.ts
|
|
1622
|
+
init_en();
|
|
1623
|
+
var LANGUAGE_STORAGE_KEY = "@wads.dev/i18n-editor/language";
|
|
1624
|
+
globalThis.Lang = en_default;
|
|
1625
|
+
globalThis.CurrentLanguage = "en";
|
|
1626
|
+
async function setLanguage(language) {
|
|
1627
|
+
const selected = await loadLanguage(Langs, "en", language);
|
|
1628
|
+
globalThis.CurrentLanguage = selected.locale.startsWith("pt") ? "pt" : "en";
|
|
1629
|
+
globalThis.Lang = selected.lang;
|
|
1630
|
+
document.documentElement.lang = selected.locale;
|
|
1631
|
+
localStorage.setItem(LANGUAGE_STORAGE_KEY, globalThis.CurrentLanguage);
|
|
1632
|
+
}
|
|
1633
|
+
async function initializeLanguage() {
|
|
1634
|
+
const stored = localStorage.getItem(LANGUAGE_STORAGE_KEY);
|
|
1635
|
+
await setLanguage(stored === "en" || stored === "pt" ? stored : void 0);
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// node_modules/@wads.dev/i18n-html/dist/runtime/index.js
|
|
1639
|
+
function readTranslationValue(translations, key) {
|
|
1640
|
+
return key.split(".").reduce((current, segment) => {
|
|
1641
|
+
return current && typeof current === "object" ? current[segment] : void 0;
|
|
1642
|
+
}, translations);
|
|
1643
|
+
}
|
|
1644
|
+
function readHtmlTranslation(translations, key) {
|
|
1645
|
+
const value = readTranslationValue(translations, key);
|
|
1646
|
+
if (typeof value !== "string")
|
|
1647
|
+
throw new Error(`HTML translation must resolve to a string: ${key}`);
|
|
1648
|
+
return value;
|
|
1649
|
+
}
|
|
1650
|
+
function translateHtml(document2, translations) {
|
|
1651
|
+
document2.querySelectorAll("[data-i18n]").forEach((element) => {
|
|
1652
|
+
const key = element.dataset.i18n;
|
|
1653
|
+
if (key)
|
|
1654
|
+
element.textContent = readHtmlTranslation(translations, key);
|
|
1655
|
+
});
|
|
1656
|
+
document2.querySelectorAll("*").forEach((element) => {
|
|
1657
|
+
Array.from(element.attributes).forEach((attribute) => {
|
|
1658
|
+
if (!attribute.name.startsWith("data-i18n-"))
|
|
1659
|
+
return;
|
|
1660
|
+
const targetAttribute = attribute.name.slice("data-i18n-".length);
|
|
1661
|
+
if (targetAttribute)
|
|
1662
|
+
element.setAttribute(targetAttribute, readHtmlTranslation(translations, attribute.value));
|
|
1663
|
+
});
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1029
1667
|
// src/web/main.ts
|
|
1668
|
+
await initializeLanguage();
|
|
1669
|
+
function translateStaticDocument() {
|
|
1670
|
+
translateHtml(document, Lang);
|
|
1671
|
+
document.title = `${Lang.page.title} \xB7 i18n`;
|
|
1672
|
+
}
|
|
1673
|
+
translateStaticDocument();
|
|
1030
1674
|
var state = createEditorState();
|
|
1031
1675
|
function getElement(selector) {
|
|
1032
1676
|
const element = document.querySelector(selector);
|
|
@@ -1060,6 +1704,8 @@ var elements = {
|
|
|
1060
1704
|
exportPreview: getElement("#export-preview"),
|
|
1061
1705
|
exportPreviewFeedback: getElement("#export-preview-feedback"),
|
|
1062
1706
|
checkExportDiffs: getElement("#check-export-diffs"),
|
|
1707
|
+
exportProject: getElement("#export-project"),
|
|
1708
|
+
exportDeleteObsolete: getElement("#export-delete-obsolete"),
|
|
1063
1709
|
search: getElement("#translation-search"),
|
|
1064
1710
|
summary: getElement("#bundle-summary"),
|
|
1065
1711
|
settingsPanel: getElement("#settings-panel"),
|
|
@@ -1067,12 +1713,46 @@ var elements = {
|
|
|
1067
1713
|
toggleSettingsPanel: getElement("#toggle-settings-panel"),
|
|
1068
1714
|
projectStatus: getElement("#project-status"),
|
|
1069
1715
|
projectStatusText: getElement("#project-status-text"),
|
|
1070
|
-
retryProjectLoad: getElement("#retry-project-load")
|
|
1716
|
+
retryProjectLoad: getElement("#retry-project-load"),
|
|
1717
|
+
analyzeUsage: getElement("#analyze-usage"),
|
|
1718
|
+
language: getElement("#editor-language"),
|
|
1719
|
+
showUnreferenced: getElement("#show-unreferenced"),
|
|
1720
|
+
visualLevelCount: getElement("#visual-level-count")
|
|
1071
1721
|
};
|
|
1072
1722
|
var settingsPanelVisible = false;
|
|
1073
1723
|
var projectConfig = createDefaultEditorProjectConfig();
|
|
1724
|
+
var currentProjectDirectory = "";
|
|
1074
1725
|
var checkedExportChanges = null;
|
|
1075
1726
|
var exportPreviewRequestVersion = 0;
|
|
1727
|
+
var visualLevelCountOverride = null;
|
|
1728
|
+
var currentUsageReport = null;
|
|
1729
|
+
function replaceUsageReport(report) {
|
|
1730
|
+
currentUsageReport = report;
|
|
1731
|
+
view.setUsageReport(report);
|
|
1732
|
+
elements.showUnreferenced.disabled = report === null;
|
|
1733
|
+
if (report === null) {
|
|
1734
|
+
view.setShowUnreferenced(false);
|
|
1735
|
+
elements.showUnreferenced.checked = false;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
function remapUsageKey(sourceKey, targetKey) {
|
|
1739
|
+
if (!currentUsageReport) return;
|
|
1740
|
+
const entries = Object.fromEntries(Object.entries(currentUsageReport.entries).map(([key, usage]) => {
|
|
1741
|
+
if (key === sourceKey) return [targetKey, usage];
|
|
1742
|
+
if (key.startsWith(`${sourceKey}.`) || key.startsWith(`${sourceKey}[`)) {
|
|
1743
|
+
return [`${targetKey}${key.slice(sourceKey.length)}`, usage];
|
|
1744
|
+
}
|
|
1745
|
+
return [key, usage];
|
|
1746
|
+
}));
|
|
1747
|
+
replaceUsageReport({ ...currentUsageReport, entries });
|
|
1748
|
+
}
|
|
1749
|
+
function removeUsageKey(keyToRemove) {
|
|
1750
|
+
if (!currentUsageReport) return;
|
|
1751
|
+
const entries = Object.fromEntries(Object.entries(currentUsageReport.entries).filter(([key]) => {
|
|
1752
|
+
return key !== keyToRemove && !key.startsWith(`${keyToRemove}.`) && !key.startsWith(`${keyToRemove}[`);
|
|
1753
|
+
}));
|
|
1754
|
+
replaceUsageReport({ ...currentUsageReport, entries });
|
|
1755
|
+
}
|
|
1076
1756
|
function renderCurrentExportPreview() {
|
|
1077
1757
|
renderExportPreview(elements.exportPreview, state.getBundle(), projectConfig, checkedExportChanges);
|
|
1078
1758
|
}
|
|
@@ -1082,9 +1762,16 @@ function invalidateExportPreview() {
|
|
|
1082
1762
|
elements.exportPreviewFeedback.textContent = "";
|
|
1083
1763
|
elements.exportPreviewFeedback.classList.remove("error", "warning");
|
|
1084
1764
|
elements.checkExportDiffs.disabled = !state.getBundle();
|
|
1085
|
-
elements.
|
|
1765
|
+
elements.exportProject.disabled = !state.getBundle();
|
|
1766
|
+
elements.checkExportDiffs.textContent = Lang.exportPreview.checkDiffs;
|
|
1086
1767
|
renderCurrentExportPreview();
|
|
1087
1768
|
}
|
|
1769
|
+
function invalidateUsageAnalysis(message = "") {
|
|
1770
|
+
view.setUsageReport(currentUsageReport);
|
|
1771
|
+
elements.analyzeUsage.disabled = !state.getBundle();
|
|
1772
|
+
elements.analyzeUsage.textContent = currentUsageReport ? Lang.editor.updateUsages : Lang.editor.analyzeUsages;
|
|
1773
|
+
if (message) setFeedback(message);
|
|
1774
|
+
}
|
|
1088
1775
|
var view = createEditorView({
|
|
1089
1776
|
emptyState: elements.emptyState,
|
|
1090
1777
|
tableWrap: elements.tableWrap,
|
|
@@ -1092,11 +1779,22 @@ var view = createEditorView({
|
|
|
1092
1779
|
summary: elements.summary,
|
|
1093
1780
|
search: elements.search,
|
|
1094
1781
|
onMoveKey(sourceKey) {
|
|
1095
|
-
const targetKey = window.prompt(
|
|
1782
|
+
const targetKey = window.prompt(Lang.messages.moveKeyPrompt, sourceKey);
|
|
1096
1783
|
if (targetKey === null || targetKey.trim() === "" || targetKey === sourceKey) return;
|
|
1097
1784
|
try {
|
|
1098
1785
|
state.update((bundle) => moveKey(bundle, { sourceKey, targetKey: targetKey.trim() }));
|
|
1099
|
-
|
|
1786
|
+
remapUsageKey(sourceKey, targetKey.trim());
|
|
1787
|
+
setFeedback(Lang.messages.keyMoved(sourceKey, targetKey.trim()));
|
|
1788
|
+
} catch (error) {
|
|
1789
|
+
setFeedback(error instanceof Error ? error.message : String(error), true);
|
|
1790
|
+
}
|
|
1791
|
+
},
|
|
1792
|
+
onRemoveKey(key) {
|
|
1793
|
+
if (!window.confirm(Lang.messages.removeKeyConfirm(key))) return;
|
|
1794
|
+
try {
|
|
1795
|
+
state.update((bundle) => removeKey(bundle, { key }));
|
|
1796
|
+
removeUsageKey(key);
|
|
1797
|
+
setFeedback(Lang.messages.keyRemoved(key));
|
|
1100
1798
|
} catch (error) {
|
|
1101
1799
|
setFeedback(error instanceof Error ? error.message : String(error), true);
|
|
1102
1800
|
}
|
|
@@ -1104,7 +1802,7 @@ var view = createEditorView({
|
|
|
1104
1802
|
onEditValue({ languageKey, key, value }) {
|
|
1105
1803
|
try {
|
|
1106
1804
|
state.update((bundle) => setStringValue(bundle, { languageKey, key, value }));
|
|
1107
|
-
setFeedback(
|
|
1805
|
+
setFeedback(Lang.messages.translationUpdated(languageKey, key));
|
|
1108
1806
|
return true;
|
|
1109
1807
|
} catch (error) {
|
|
1110
1808
|
setFeedback(error instanceof Error ? error.message : String(error), true);
|
|
@@ -1122,7 +1820,7 @@ function setSettingsPanelVisibility(visible) {
|
|
|
1122
1820
|
elements.toggleSettingsPanel.setAttribute("aria-expanded", String(visible));
|
|
1123
1821
|
elements.toggleSettingsPanel.setAttribute(
|
|
1124
1822
|
"aria-label",
|
|
1125
|
-
visible ?
|
|
1823
|
+
visible ? Lang.settings.hide : Lang.settings.show
|
|
1126
1824
|
);
|
|
1127
1825
|
elements.toggleSettingsPanel.title = elements.toggleSettingsPanel.getAttribute("aria-label");
|
|
1128
1826
|
}
|
|
@@ -1130,7 +1828,7 @@ var projectConfigQueue = Promise.resolve();
|
|
|
1130
1828
|
function parseJsonObject(value, fieldName) {
|
|
1131
1829
|
const parsed = JSON.parse(value);
|
|
1132
1830
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1133
|
-
throw new Error(
|
|
1831
|
+
throw new Error(Lang.messages.jsonObjectExpected(fieldName));
|
|
1134
1832
|
}
|
|
1135
1833
|
return parsed;
|
|
1136
1834
|
}
|
|
@@ -1145,6 +1843,8 @@ function applyProjectConfig(nextConfig, persist = true, renderLevelImports = tru
|
|
|
1145
1843
|
elements.ignoredDeletionExtensions.disabled = projectConfig.deletion === false;
|
|
1146
1844
|
elements.autoDelete.checked = projectConfig.deletion !== false && projectConfig.deletion.autoDelete;
|
|
1147
1845
|
elements.autoDelete.disabled = projectConfig.deletion === false;
|
|
1846
|
+
elements.exportDeleteObsolete.disabled = projectConfig.deletion === false ? true : projectConfig.deletion.autoDelete;
|
|
1847
|
+
elements.exportDeleteObsolete.checked = projectConfig.deletion !== false && projectConfig.deletion.autoDelete;
|
|
1148
1848
|
const codeFormat = projectConfig.exportConfig.codeFormat;
|
|
1149
1849
|
elements.useDoubleQuotes.checked = codeFormat.useDoubleQuotes;
|
|
1150
1850
|
elements.useSemicolons.checked = codeFormat.useSemicolons;
|
|
@@ -1167,41 +1867,99 @@ function applyProjectConfig(nextConfig, persist = true, renderLevelImports = tru
|
|
|
1167
1867
|
const levelImports = projectConfig.levelImports.map((item) => ({ ...item }));
|
|
1168
1868
|
levelImports[index][field] = field === "path" ? value : parseJsonObject(value, field === "valueReplacer" ? "Value replacer" : "Full replacer");
|
|
1169
1869
|
applyProjectConfig({ ...projectConfig, levelImports }, true, false);
|
|
1170
|
-
setFeedback(
|
|
1870
|
+
setFeedback(Lang.messages.configurationUpdated);
|
|
1171
1871
|
} catch (error) {
|
|
1172
1872
|
setFeedback(error instanceof Error ? error.message : String(error), true);
|
|
1173
1873
|
}
|
|
1174
1874
|
});
|
|
1175
1875
|
}
|
|
1176
|
-
|
|
1876
|
+
if (visualLevelCountOverride === null) elements.visualLevelCount.value = String(projectConfig.levelCount);
|
|
1877
|
+
view.setProjectConfig({
|
|
1878
|
+
...projectConfig,
|
|
1879
|
+
levelCount: visualLevelCountOverride ?? projectConfig.levelCount
|
|
1880
|
+
});
|
|
1881
|
+
invalidateUsageAnalysis();
|
|
1177
1882
|
invalidateExportPreview();
|
|
1178
1883
|
if (!persist) return;
|
|
1179
|
-
projectConfigQueue = projectConfigQueue.catch(() => void 0).then(() => saveStoredProjectConfig(projectConfig)).catch((error) => {
|
|
1180
|
-
setFeedback(
|
|
1884
|
+
projectConfigQueue = projectConfigQueue.catch(() => void 0).then(() => saveStoredProjectConfig(currentProjectDirectory, projectConfig)).catch((error) => {
|
|
1885
|
+
setFeedback(Lang.messages.couldNotSaveSettings(error instanceof Error ? error.message : String(error)), true);
|
|
1181
1886
|
});
|
|
1182
1887
|
}
|
|
1183
1888
|
var persistenceQueue = Promise.resolve();
|
|
1184
1889
|
function persistBundle(bundle) {
|
|
1185
|
-
persistenceQueue = persistenceQueue.catch(() => void 0).then(() => saveStoredBundle(bundle)).catch((error) => {
|
|
1186
|
-
setFeedback(
|
|
1890
|
+
persistenceQueue = persistenceQueue.catch(() => void 0).then(() => saveStoredBundle(currentProjectDirectory, bundle)).catch((error) => {
|
|
1891
|
+
setFeedback(Lang.messages.couldNotSaveLocalCopy(error instanceof Error ? error.message : String(error)), true);
|
|
1187
1892
|
});
|
|
1188
1893
|
}
|
|
1189
1894
|
state.subscribe((bundle) => {
|
|
1190
1895
|
view.render(bundle);
|
|
1191
1896
|
invalidateExportPreview();
|
|
1897
|
+
invalidateUsageAnalysis();
|
|
1192
1898
|
if (bundle) persistBundle(bundle);
|
|
1193
1899
|
});
|
|
1194
1900
|
view.render(state.getBundle());
|
|
1195
1901
|
elements.toggleSettingsPanel.addEventListener("click", () => {
|
|
1196
1902
|
setSettingsPanelVisibility(!settingsPanelVisible);
|
|
1197
1903
|
});
|
|
1904
|
+
elements.language.value = globalThis.CurrentLanguage;
|
|
1905
|
+
elements.language.addEventListener("change", async () => {
|
|
1906
|
+
await setLanguage(elements.language.value);
|
|
1907
|
+
translateStaticDocument();
|
|
1908
|
+
setSettingsPanelVisibility(settingsPanelVisible);
|
|
1909
|
+
applyProjectConfig(projectConfig, false);
|
|
1910
|
+
view.render(state.getBundle());
|
|
1911
|
+
invalidateExportPreview();
|
|
1912
|
+
invalidateUsageAnalysis();
|
|
1913
|
+
});
|
|
1914
|
+
async function loadUsageAnalysis(wait) {
|
|
1915
|
+
const bundle = state.getBundle();
|
|
1916
|
+
if (!bundle) return;
|
|
1917
|
+
const config = projectConfig;
|
|
1918
|
+
elements.analyzeUsage.disabled = true;
|
|
1919
|
+
elements.analyzeUsage.textContent = wait ? Lang.editor.updatingUsages : Lang.editor.loadingUsages;
|
|
1920
|
+
try {
|
|
1921
|
+
const cached = await analyzeProjectUsage(bundle, config, wait);
|
|
1922
|
+
if (state.getBundle() !== bundle || projectConfig !== config) return;
|
|
1923
|
+
if (cached.report) replaceUsageReport(cached.report);
|
|
1924
|
+
if (!wait && cached.cacheStatus !== "verified") {
|
|
1925
|
+
setFeedback(cached.report ? Lang.messages.unverifiedUsageCache : Lang.messages.noUsageCache);
|
|
1926
|
+
const refreshed = await analyzeProjectUsage(bundle, config, true);
|
|
1927
|
+
if (state.getBundle() !== bundle || projectConfig !== config) return;
|
|
1928
|
+
if (refreshed.report) replaceUsageReport(refreshed.report);
|
|
1929
|
+
}
|
|
1930
|
+
const report = currentUsageReport;
|
|
1931
|
+
if (!report) throw new Error("The usage analysis did not return a report.");
|
|
1932
|
+
const usages = Object.values(report.entries);
|
|
1933
|
+
const used = usages.filter(({ status }) => status === "used").length;
|
|
1934
|
+
const uncertain = usages.filter(({ status }) => status === "uncertain").length;
|
|
1935
|
+
const unreferenced = usages.filter(({ status }) => status === "unreferenced").length;
|
|
1936
|
+
setFeedback(Lang.messages.usageSummary(used, uncertain, unreferenced, report.sourceFileCount));
|
|
1937
|
+
} catch (error) {
|
|
1938
|
+
setFeedback(Lang.messages.couldNotAnalyzeUsages(error instanceof Error ? error.message : String(error)), true);
|
|
1939
|
+
} finally {
|
|
1940
|
+
elements.analyzeUsage.disabled = !state.getBundle();
|
|
1941
|
+
elements.analyzeUsage.textContent = currentUsageReport ? Lang.editor.updateUsages : Lang.editor.analyzeUsages;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
elements.analyzeUsage.addEventListener("click", async () => {
|
|
1945
|
+
await loadUsageAnalysis(true);
|
|
1946
|
+
});
|
|
1947
|
+
elements.showUnreferenced.addEventListener("change", () => {
|
|
1948
|
+
view.setShowUnreferenced(elements.showUnreferenced.checked);
|
|
1949
|
+
});
|
|
1950
|
+
elements.visualLevelCount.addEventListener("input", () => {
|
|
1951
|
+
const parsed = Number.parseInt(elements.visualLevelCount.value, 10);
|
|
1952
|
+
visualLevelCountOverride = Number.isFinite(parsed) ? Math.min(12, Math.max(0, parsed)) : 0;
|
|
1953
|
+
elements.visualLevelCount.value = String(visualLevelCountOverride);
|
|
1954
|
+
view.setProjectConfig({ ...projectConfig, levelCount: visualLevelCountOverride });
|
|
1955
|
+
});
|
|
1198
1956
|
elements.checkExportDiffs.addEventListener("click", async () => {
|
|
1199
1957
|
const bundle = state.getBundle();
|
|
1200
1958
|
if (!bundle) return;
|
|
1201
1959
|
const requestVersion = ++exportPreviewRequestVersion;
|
|
1202
1960
|
elements.checkExportDiffs.disabled = true;
|
|
1203
|
-
elements.checkExportDiffs.textContent =
|
|
1204
|
-
elements.exportPreviewFeedback.textContent =
|
|
1961
|
+
elements.checkExportDiffs.textContent = Lang.exportPreview.checking;
|
|
1962
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.comparingFiles;
|
|
1205
1963
|
elements.exportPreviewFeedback.classList.remove("error", "warning");
|
|
1206
1964
|
try {
|
|
1207
1965
|
const result = await checkProjectExport(bundle, projectConfig);
|
|
@@ -1211,21 +1969,63 @@ elements.checkExportDiffs.addEventListener("click", async () => {
|
|
|
1211
1969
|
const changed = result.changes.filter(({ status }) => status !== "unchanged").length;
|
|
1212
1970
|
const deletionCount = result.changes.filter(({ status }) => status === "delete").length;
|
|
1213
1971
|
if (deletionCount > 0) {
|
|
1214
|
-
const deletionMessage = projectConfig.deletion !== false && projectConfig.deletion.autoDelete ?
|
|
1215
|
-
elements.exportPreviewFeedback.textContent = `${changed}
|
|
1972
|
+
const deletionMessage = projectConfig.deletion !== false && projectConfig.deletion.autoDelete ? Lang.messages.autoDeleteWarning(deletionCount) : Lang.messages.preservedDeletionWarning(deletionCount);
|
|
1973
|
+
elements.exportPreviewFeedback.textContent = `${Lang.messages.plannedChanges(changed)} ${deletionMessage}`;
|
|
1216
1974
|
elements.exportPreviewFeedback.classList.add("warning");
|
|
1217
1975
|
} else {
|
|
1218
|
-
elements.exportPreviewFeedback.textContent = changed === 0 ?
|
|
1976
|
+
elements.exportPreviewFeedback.textContent = changed === 0 ? Lang.messages.projectMatchesPreview : Lang.messages.plannedChanges(changed);
|
|
1219
1977
|
}
|
|
1220
1978
|
} catch (error) {
|
|
1221
1979
|
if (requestVersion !== exportPreviewRequestVersion) return;
|
|
1222
|
-
elements.exportPreviewFeedback.textContent =
|
|
1980
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.couldNotCheckDiffs(error instanceof Error ? error.message : String(error));
|
|
1223
1981
|
elements.exportPreviewFeedback.classList.add("error");
|
|
1224
1982
|
} finally {
|
|
1225
1983
|
if (requestVersion === exportPreviewRequestVersion) {
|
|
1226
1984
|
elements.checkExportDiffs.disabled = false;
|
|
1227
|
-
elements.checkExportDiffs.textContent =
|
|
1985
|
+
elements.checkExportDiffs.textContent = Lang.exportPreview.checkAgain;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
elements.exportProject.addEventListener("click", async () => {
|
|
1990
|
+
const bundle = state.getBundle();
|
|
1991
|
+
if (!bundle) return;
|
|
1992
|
+
elements.exportProject.disabled = true;
|
|
1993
|
+
elements.checkExportDiffs.disabled = true;
|
|
1994
|
+
elements.exportProject.textContent = Lang.exportPreview.preparing;
|
|
1995
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.recalculating;
|
|
1996
|
+
elements.exportPreviewFeedback.classList.remove("error", "warning");
|
|
1997
|
+
try {
|
|
1998
|
+
const preview = await checkProjectExport(bundle, projectConfig);
|
|
1999
|
+
checkedExportChanges = preview.changes;
|
|
2000
|
+
renderCurrentExportPreview();
|
|
2001
|
+
const writable = preview.changes.filter(({ status }) => status === "create" || status === "modify").length;
|
|
2002
|
+
const deletionCandidates = preview.changes.filter(({ status }) => status === "delete").length;
|
|
2003
|
+
const deleteObsolete = projectConfig.deletion !== false && (projectConfig.deletion.autoDelete || elements.exportDeleteObsolete.checked);
|
|
2004
|
+
const deletionText = deletionCandidates === 0 ? Lang.messages.noFilesDeleted : deleteObsolete ? Lang.messages.filesWillBeDeleted(deletionCandidates) : Lang.messages.filesWillBePreserved(deletionCandidates);
|
|
2005
|
+
if (writable === 0 && (!deleteObsolete || deletionCandidates === 0)) {
|
|
2006
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.projectUpToDate;
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
const confirmed = window.confirm(Lang.messages.exportConfirm(writable, deletionText));
|
|
2010
|
+
if (!confirmed) {
|
|
2011
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.exportCancelled;
|
|
2012
|
+
return;
|
|
1228
2013
|
}
|
|
2014
|
+
elements.exportProject.textContent = Lang.exportPreview.exporting;
|
|
2015
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.writingFiles;
|
|
2016
|
+
const result = await exportProject(bundle, projectConfig, deleteObsolete);
|
|
2017
|
+
const currentPreview = await checkProjectExport(bundle, projectConfig);
|
|
2018
|
+
checkedExportChanges = currentPreview.changes;
|
|
2019
|
+
renderCurrentExportPreview();
|
|
2020
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.exportResult(result.written, result.deleted, result.preserved);
|
|
2021
|
+
setFeedback(Lang.messages.exported);
|
|
2022
|
+
} catch (error) {
|
|
2023
|
+
elements.exportPreviewFeedback.textContent = Lang.messages.couldNotExport(error instanceof Error ? error.message : String(error));
|
|
2024
|
+
elements.exportPreviewFeedback.classList.add("error");
|
|
2025
|
+
} finally {
|
|
2026
|
+
elements.exportProject.disabled = !state.getBundle();
|
|
2027
|
+
elements.checkExportDiffs.disabled = !state.getBundle();
|
|
2028
|
+
elements.exportProject.textContent = Lang.exportPreview.exportProject;
|
|
1229
2029
|
}
|
|
1230
2030
|
});
|
|
1231
2031
|
elements.levelCount.addEventListener("input", () => {
|
|
@@ -1248,12 +2048,12 @@ elements.languageFileTemplate.addEventListener("input", () => {
|
|
|
1248
2048
|
});
|
|
1249
2049
|
elements.importAliases.addEventListener("input", () => {
|
|
1250
2050
|
try {
|
|
1251
|
-
const importAliases = parseJsonObject(elements.importAliases.value, "
|
|
2051
|
+
const importAliases = parseJsonObject(elements.importAliases.value, "Import aliases");
|
|
1252
2052
|
applyProjectConfig({
|
|
1253
2053
|
...projectConfig,
|
|
1254
2054
|
exportConfig: { ...projectConfig.exportConfig, importAliases }
|
|
1255
2055
|
}, true, false, false);
|
|
1256
|
-
setFeedback(
|
|
2056
|
+
setFeedback(Lang.messages.importAliasesUpdated);
|
|
1257
2057
|
} catch (error) {
|
|
1258
2058
|
setFeedback(error instanceof Error ? error.message : String(error), true);
|
|
1259
2059
|
}
|
|
@@ -1269,7 +2069,7 @@ elements.useDoubleQuotes.addEventListener("change", () => {
|
|
|
1269
2069
|
}
|
|
1270
2070
|
}
|
|
1271
2071
|
});
|
|
1272
|
-
setFeedback(elements.useDoubleQuotes.checked ?
|
|
2072
|
+
setFeedback(elements.useDoubleQuotes.checked ? Lang.messages.doubleQuotesEnabled : Lang.messages.singleQuotesEnabled);
|
|
1273
2073
|
});
|
|
1274
2074
|
elements.useSemicolons.addEventListener("change", () => {
|
|
1275
2075
|
applyProjectConfig({
|
|
@@ -1282,7 +2082,7 @@ elements.useSemicolons.addEventListener("change", () => {
|
|
|
1282
2082
|
}
|
|
1283
2083
|
}
|
|
1284
2084
|
});
|
|
1285
|
-
setFeedback(elements.useSemicolons.checked ?
|
|
2085
|
+
setFeedback(elements.useSemicolons.checked ? Lang.messages.semicolonsEnabled : Lang.messages.semicolonsDisabled);
|
|
1286
2086
|
});
|
|
1287
2087
|
elements.useShorthandProperties.addEventListener("change", () => {
|
|
1288
2088
|
applyProjectConfig({
|
|
@@ -1295,7 +2095,7 @@ elements.useShorthandProperties.addEventListener("change", () => {
|
|
|
1295
2095
|
}
|
|
1296
2096
|
}
|
|
1297
2097
|
});
|
|
1298
|
-
setFeedback(
|
|
2098
|
+
setFeedback(Lang.messages.shorthandUpdated);
|
|
1299
2099
|
});
|
|
1300
2100
|
elements.useTrailingCommas.addEventListener("change", () => {
|
|
1301
2101
|
applyProjectConfig({
|
|
@@ -1308,7 +2108,7 @@ elements.useTrailingCommas.addEventListener("change", () => {
|
|
|
1308
2108
|
}
|
|
1309
2109
|
}
|
|
1310
2110
|
});
|
|
1311
|
-
setFeedback(
|
|
2111
|
+
setFeedback(Lang.messages.trailingCommasUpdated);
|
|
1312
2112
|
});
|
|
1313
2113
|
function updateIndentation() {
|
|
1314
2114
|
applyProjectConfig({
|
|
@@ -1324,7 +2124,7 @@ function updateIndentation() {
|
|
|
1324
2124
|
}
|
|
1325
2125
|
}
|
|
1326
2126
|
});
|
|
1327
|
-
setFeedback(
|
|
2127
|
+
setFeedback(Lang.messages.indentationUpdated);
|
|
1328
2128
|
}
|
|
1329
2129
|
elements.indentationCharacter.addEventListener("change", updateIndentation);
|
|
1330
2130
|
elements.indentationSize.addEventListener("input", updateIndentation);
|
|
@@ -1339,7 +2139,7 @@ elements.printWidth.addEventListener("input", () => {
|
|
|
1339
2139
|
}
|
|
1340
2140
|
}
|
|
1341
2141
|
});
|
|
1342
|
-
setFeedback(
|
|
2142
|
+
setFeedback(Lang.messages.printWidthUpdated);
|
|
1343
2143
|
});
|
|
1344
2144
|
function updateInlineItemLimits() {
|
|
1345
2145
|
applyProjectConfig({
|
|
@@ -1353,7 +2153,7 @@ function updateInlineItemLimits() {
|
|
|
1353
2153
|
}
|
|
1354
2154
|
}
|
|
1355
2155
|
});
|
|
1356
|
-
setFeedback(
|
|
2156
|
+
setFeedback(Lang.messages.inlineLimitsUpdated);
|
|
1357
2157
|
}
|
|
1358
2158
|
elements.maxObjectInlineItems.addEventListener("input", updateInlineItemLimits);
|
|
1359
2159
|
elements.maxArrayInlineItems.addEventListener("input", updateInlineItemLimits);
|
|
@@ -1369,15 +2169,15 @@ function updateCollectionLayouts() {
|
|
|
1369
2169
|
}
|
|
1370
2170
|
}
|
|
1371
2171
|
});
|
|
1372
|
-
setFeedback(
|
|
2172
|
+
setFeedback(Lang.messages.layoutsUpdated);
|
|
1373
2173
|
}
|
|
1374
2174
|
elements.objectLayout.addEventListener("change", updateCollectionLayouts);
|
|
1375
2175
|
elements.arrayLayout.addEventListener("change", updateCollectionLayouts);
|
|
1376
2176
|
elements.languageReplacer.addEventListener("input", () => {
|
|
1377
2177
|
try {
|
|
1378
|
-
const languageReplacer = parseJsonObject(elements.languageReplacer.value, "
|
|
2178
|
+
const languageReplacer = parseJsonObject(elements.languageReplacer.value, "Language replacer");
|
|
1379
2179
|
applyProjectConfig({ ...projectConfig, languageReplacer }, true, false, false);
|
|
1380
|
-
setFeedback(
|
|
2180
|
+
setFeedback(Lang.messages.languageReplacerUpdated);
|
|
1381
2181
|
} catch (error) {
|
|
1382
2182
|
setFeedback(error instanceof Error ? error.message : String(error), true);
|
|
1383
2183
|
}
|
|
@@ -1387,7 +2187,7 @@ elements.deletionEnabled.addEventListener("change", () => {
|
|
|
1387
2187
|
...projectConfig,
|
|
1388
2188
|
deletion: elements.deletionEnabled.checked ? { ignoredExtensions: [], autoDelete: false } : false
|
|
1389
2189
|
});
|
|
1390
|
-
setFeedback(elements.deletionEnabled.checked ?
|
|
2190
|
+
setFeedback(elements.deletionEnabled.checked ? Lang.messages.deletionDetectionEnabled : Lang.messages.deletionDetectionDisabled);
|
|
1391
2191
|
});
|
|
1392
2192
|
elements.ignoredDeletionExtensions.addEventListener("input", () => {
|
|
1393
2193
|
if (projectConfig.deletion === false) return;
|
|
@@ -1398,7 +2198,7 @@ elements.ignoredDeletionExtensions.addEventListener("input", () => {
|
|
|
1398
2198
|
ignoredExtensions: elements.ignoredDeletionExtensions.value.split(",")
|
|
1399
2199
|
}
|
|
1400
2200
|
});
|
|
1401
|
-
setFeedback(
|
|
2201
|
+
setFeedback(Lang.messages.ignoredExtensionsUpdated);
|
|
1402
2202
|
});
|
|
1403
2203
|
elements.autoDelete.addEventListener("change", () => {
|
|
1404
2204
|
if (projectConfig.deletion === false) return;
|
|
@@ -1406,7 +2206,7 @@ elements.autoDelete.addEventListener("change", () => {
|
|
|
1406
2206
|
...projectConfig,
|
|
1407
2207
|
deletion: { ...projectConfig.deletion, autoDelete: elements.autoDelete.checked }
|
|
1408
2208
|
});
|
|
1409
|
-
setFeedback(elements.autoDelete.checked ?
|
|
2209
|
+
setFeedback(elements.autoDelete.checked ? Lang.messages.autoDeleteEnabled : Lang.messages.autoDeleteDisabled);
|
|
1410
2210
|
});
|
|
1411
2211
|
function setProjectStatus(message, isError = false) {
|
|
1412
2212
|
elements.projectStatus.hidden = false;
|
|
@@ -1419,12 +2219,13 @@ function hideProjectStatus() {
|
|
|
1419
2219
|
elements.projectStatus.hidden = true;
|
|
1420
2220
|
}
|
|
1421
2221
|
async function loadProject() {
|
|
1422
|
-
setProjectStatus(
|
|
2222
|
+
setProjectStatus(Lang.messages.connecting);
|
|
1423
2223
|
try {
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
2224
|
+
const info = await getProjectInfo();
|
|
2225
|
+
currentProjectDirectory = info.projectDirectory;
|
|
2226
|
+
const [storedBundle, storedConfig] = await Promise.all([
|
|
2227
|
+
loadStoredBundle(currentProjectDirectory),
|
|
2228
|
+
loadStoredProjectConfig(currentProjectDirectory)
|
|
1428
2229
|
]);
|
|
1429
2230
|
if (info.config) {
|
|
1430
2231
|
applyProjectConfig(info.config, false);
|
|
@@ -1435,27 +2236,29 @@ async function loadProject() {
|
|
|
1435
2236
|
let generated = false;
|
|
1436
2237
|
if (!projectBundle) {
|
|
1437
2238
|
if (!info.canGenerateBundle) {
|
|
1438
|
-
throw new Error(
|
|
2239
|
+
throw new Error(Lang.messages.missingCatalog);
|
|
1439
2240
|
}
|
|
1440
|
-
setProjectStatus(
|
|
2241
|
+
setProjectStatus(Lang.messages.generatingBundle);
|
|
1441
2242
|
projectBundle = (await generateProjectBundle()).bundle;
|
|
1442
2243
|
generated = true;
|
|
1443
2244
|
}
|
|
1444
2245
|
const projectIsNewer = storedBundle && projectBundle.updatedAt > storedBundle.updatedAt;
|
|
1445
2246
|
const selectedBundle = !storedBundle || projectIsNewer ? projectBundle : storedBundle;
|
|
1446
2247
|
if (projectIsNewer && !generated) {
|
|
1447
|
-
window.alert(
|
|
2248
|
+
window.alert(Lang.messages.newerBundle);
|
|
1448
2249
|
}
|
|
1449
2250
|
state.replaceBundle(selectedBundle);
|
|
1450
|
-
setFeedback(generated ?
|
|
2251
|
+
setFeedback(generated ? Lang.messages.generatedBundle : selectedBundle === storedBundle ? Lang.messages.restoredBundle : Lang.messages.loadedBundle);
|
|
1451
2252
|
hideProjectStatus();
|
|
2253
|
+
void loadUsageAnalysis(false);
|
|
1452
2254
|
} catch (error) {
|
|
1453
2255
|
setProjectStatus(
|
|
1454
|
-
|
|
2256
|
+
Lang.messages.couldNotLoadProject(error instanceof Error ? error.message : String(error)),
|
|
1455
2257
|
true
|
|
1456
2258
|
);
|
|
1457
2259
|
}
|
|
1458
2260
|
}
|
|
1459
2261
|
applyProjectConfig(projectConfig, false);
|
|
2262
|
+
setSettingsPanelVisibility(false);
|
|
1460
2263
|
elements.retryProjectLoad.addEventListener("click", () => void loadProject());
|
|
1461
2264
|
void loadProject();
|