i18ntk 1.2.2 โ 1.3.1
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/CHANGELOG.md +75 -2
- package/LICENSE +3 -1
- package/README.md +48 -47
- package/docs/README.md +42 -19
- package/docs/SCRIPT_DIRECTORY_GUIDE.md +278 -0
- package/docs/release-notes/v1.3.0.md +162 -0
- package/docs/release-notes/v1.3.1.md +136 -0
- package/main/i18ntk-analyze.js +14 -6
- package/main/i18ntk-autorun.js +37 -36
- package/main/i18ntk-complete.js +9 -5
- package/main/i18ntk-init.js +16 -12
- package/main/i18ntk-manage.js +6 -4
- package/main/i18ntk-sizing.js +12 -12
- package/main/i18ntk-summary.js +6 -2
- package/main/i18ntk-usage.js +5 -5
- package/main/i18ntk-validate.js +9 -5
- package/package.json +20 -19
- package/scripts/copy-translations.js +90 -0
- package/settings/i18ntk-config.json +10 -0
- package/settings/settings-cli.js +201 -77
- package/settings/settings-manager.js +19 -0
- package/ui-locales/de/autorun.json +69 -64
- package/ui-locales/de/common.json +14 -1
- package/ui-locales/de/errors.json +11 -1
- package/ui-locales/de/menu.json +10 -1
- package/ui-locales/de/operations.json +11 -0
- package/ui-locales/de/security.json +2 -1
- package/ui-locales/de/settings.json +67 -9
- package/ui-locales/de/sizing.json +14 -1
- package/ui-locales/de/status.json +18 -1
- package/ui-locales/de/test-complete-system.json +13 -1
- package/ui-locales/en/autorun.json +68 -65
- package/ui-locales/en/common.json +15 -1
- package/ui-locales/en/init.json +20 -5
- package/ui-locales/en/menu.json +1 -0
- package/ui-locales/en/operations.json +11 -1
- package/ui-locales/en/settings.json +158 -35
- package/ui-locales/en/validate.json +1 -0
- package/ui-locales/es/autorun.json +68 -65
- package/ui-locales/es/common.json +14 -1
- package/ui-locales/es/errors.json +11 -1
- package/ui-locales/es/menu.json +10 -1
- package/ui-locales/es/operations.json +11 -0
- package/ui-locales/es/security.json +2 -1
- package/ui-locales/es/settings.json +60 -9
- package/ui-locales/es/sizing.json +14 -1
- package/ui-locales/es/status.json +18 -1
- package/ui-locales/es/test-complete-system.json +13 -1
- package/ui-locales/fr/autorun.json +69 -64
- package/ui-locales/fr/common.json +14 -1
- package/ui-locales/fr/errors.json +11 -1
- package/ui-locales/fr/menu.json +10 -1
- package/ui-locales/fr/operations.json +11 -0
- package/ui-locales/fr/security.json +2 -1
- package/ui-locales/fr/settings.json +60 -9
- package/ui-locales/fr/sizing.json +14 -1
- package/ui-locales/fr/status.json +18 -1
- package/ui-locales/fr/test-complete-system.json +13 -1
- package/ui-locales/ja/autorun.json +69 -64
- package/ui-locales/ja/common.json +14 -1
- package/ui-locales/ja/errors.json +11 -1
- package/ui-locales/ja/menu.json +10 -1
- package/ui-locales/ja/operations.json +11 -0
- package/ui-locales/ja/security.json +2 -1
- package/ui-locales/ja/settings.json +60 -9
- package/ui-locales/ja/sizing.json +14 -1
- package/ui-locales/ja/status.json +18 -1
- package/ui-locales/ja/test-complete-system.json +13 -1
- package/ui-locales/pt/analyze.json +2 -1
- package/ui-locales/pt/autorun.json +69 -64
- package/ui-locales/pt/common.json +14 -1
- package/ui-locales/pt/errors.json +11 -1
- package/ui-locales/pt/init.json +5 -1
- package/ui-locales/pt/menu.json +10 -1
- package/ui-locales/pt/operations.json +11 -0
- package/ui-locales/pt/security.json +2 -1
- package/ui-locales/pt/settings.json +60 -9
- package/ui-locales/pt/sizing.json +14 -1
- package/ui-locales/pt/status.json +18 -1
- package/ui-locales/pt/test-complete-system.json +13 -1
- package/ui-locales/ru/autorun.json +69 -64
- package/ui-locales/ru/common.json +14 -1
- package/ui-locales/ru/errors.json +11 -1
- package/ui-locales/ru/menu.json +10 -1
- package/ui-locales/ru/operations.json +11 -0
- package/ui-locales/ru/security.json +2 -1
- package/ui-locales/ru/settings.json +60 -9
- package/ui-locales/ru/sizing.json +14 -1
- package/ui-locales/ru/status.json +18 -1
- package/ui-locales/ru/test-complete-system.json +13 -1
- package/ui-locales/zh/autorun.json +69 -64
- package/ui-locales/zh/common.json +14 -1
- package/ui-locales/zh/errors.json +11 -1
- package/ui-locales/zh/menu.json +10 -1
- package/ui-locales/zh/operations.json +11 -0
- package/ui-locales/zh/security.json +2 -1
- package/ui-locales/zh/settings.json +67 -9
- package/ui-locales/zh/sizing.json +13 -1
- package/ui-locales/zh/status.json +25 -8
- package/ui-locales/zh/test-complete-system.json +13 -1
- package/utils/test-complete-system.js +178 -162
- package/settings/backups/i18ntk-config-backup-2025-08-01T23-38-43-753Z.json +0 -117
- package/ui-locales/de-old.json +0 -705
- package/ui-locales/de.json +0 -15
- package/ui-locales/en-old.json +0 -709
- package/ui-locales/en.json +0 -15
- package/ui-locales/es-old.json +0 -654
- package/ui-locales/es.json +0 -15
- package/ui-locales/fr-old.json +0 -606
- package/ui-locales/fr.json +0 -15
- package/ui-locales/ja-old.json +0 -660
- package/ui-locales/ja.json +0 -15
- package/ui-locales/pt.json +0 -15
- package/ui-locales/ru-old.json +0 -655
- package/ui-locales/ru.json +0 -15
- package/ui-locales/zh-old.json +0 -647
- package/ui-locales/zh.json +0 -15
package/main/i18ntk-sizing.js
CHANGED
|
@@ -41,8 +41,12 @@ const SecurityUtils = require('../utils/security');
|
|
|
41
41
|
// Get configuration from settings manager
|
|
42
42
|
function getConfig() {
|
|
43
43
|
const settings = settingsManager.getSettings();
|
|
44
|
+
|
|
45
|
+
// Check for per-script directory override, fallback to global sourceDir
|
|
46
|
+
const sourceDir = settings.scriptDirectories?.sizing || settings.sourceDir || './locales';
|
|
47
|
+
|
|
44
48
|
return {
|
|
45
|
-
sourceDir:
|
|
49
|
+
sourceDir: sourceDir,
|
|
46
50
|
outputDir: settings.outputDir || './i18ntk-reports',
|
|
47
51
|
threshold: settings.processing?.sizingThreshold || 50,
|
|
48
52
|
uiLanguage: settings.language || 'en'
|
|
@@ -350,27 +354,19 @@ class I18nSizingAnalyzer {
|
|
|
350
354
|
if (data.isProblematic) {
|
|
351
355
|
const comparison = data.percentageDifference > 0 ? 'longer' : 'shorter';
|
|
352
356
|
const absPercentage = Math.abs(data.percentageDifference);
|
|
353
|
-
recommendations.push(
|
|
354
|
-
lang,
|
|
355
|
-
percentageDifference: absPercentage,
|
|
356
|
-
comparison
|
|
357
|
-
}));
|
|
357
|
+
recommendations.push(`Review ${lang} translations - they are ${absPercentage}% ${comparison} than baseline. Consider UI layout adjustments or translation optimization.`);
|
|
358
358
|
}
|
|
359
359
|
});
|
|
360
360
|
|
|
361
361
|
// Check for problematic keys
|
|
362
362
|
if (this.stats.summary.problematicKeys.length > 0) {
|
|
363
|
-
recommendations.push(
|
|
363
|
+
recommendations.push(`${this.stats.summary.problematicKeys.length} keys have significant size variations. Check individual translations for potential layout issues.`);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
// Check for very long translations
|
|
367
367
|
Object.entries(this.stats.languages).forEach(([lang, data]) => {
|
|
368
368
|
if (data.longKeys > 0) {
|
|
369
|
-
recommendations.push(
|
|
370
|
-
lang,
|
|
371
|
-
longKeys: data.longKeys,
|
|
372
|
-
threshold: 100
|
|
373
|
-
}));
|
|
369
|
+
recommendations.push(`${lang} has ${data.longKeys} translations exceeding 100 characters. Consider breaking into shorter segments or reviewing for conciseness.`);
|
|
374
370
|
}
|
|
375
371
|
});
|
|
376
372
|
|
|
@@ -379,6 +375,10 @@ class I18nSizingAnalyzer {
|
|
|
379
375
|
|
|
380
376
|
// Display results in table format
|
|
381
377
|
displayTable() {
|
|
378
|
+
console.log(`๐ Source directory: ${path.resolve(this.sourceDir)}`);
|
|
379
|
+
console.log(`๐ Source language: ${this.config?.sourceLanguage || 'en'}`);
|
|
380
|
+
console.log(`โ๏ธ Strict mode: OFF`);
|
|
381
|
+
console.log();
|
|
382
382
|
console.log(this.t("sizing.sizing_analysis_results"));
|
|
383
383
|
console.log(this.t("sizing.separator"));
|
|
384
384
|
|
package/main/i18ntk-summary.js
CHANGED
|
@@ -9,11 +9,15 @@ const UIi18n = require('./i18ntk-ui');
|
|
|
9
9
|
// Get configuration from settings manager
|
|
10
10
|
function getConfig() {
|
|
11
11
|
const settings = settingsManager.getSettings();
|
|
12
|
+
|
|
13
|
+
// Check for per-script directory override, fallback to global sourceDir
|
|
14
|
+
const sourceDir = settings.scriptDirectories?.summary || settings.sourceDir || null;
|
|
15
|
+
|
|
12
16
|
return {
|
|
13
|
-
sourceLanguage: settings.
|
|
17
|
+
sourceLanguage: settings.sourceLanguage || 'en',
|
|
14
18
|
excludeFiles: settings.processing?.excludeFiles || ['index.js', 'index.ts', '.DS_Store'],
|
|
15
19
|
supportedExtensions: settings.processing?.supportedExtensions || ['.json', '.js', '.ts'],
|
|
16
|
-
sourceDir:
|
|
20
|
+
sourceDir: sourceDir,
|
|
17
21
|
uiLanguage: settings.language || 'en'
|
|
18
22
|
};
|
|
19
23
|
}
|
package/main/i18ntk-usage.js
CHANGED
|
@@ -34,9 +34,9 @@ async function getConfig() {
|
|
|
34
34
|
try {
|
|
35
35
|
const settings = settingsManager.getSettings();
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
let sourceDir = settings.
|
|
39
|
-
let i18nDir = settings.
|
|
37
|
+
// Use per-script directory configuration if available, fallback to global settings
|
|
38
|
+
let sourceDir = settings.scriptDirectories?.usage || settings.sourceDir;
|
|
39
|
+
let i18nDir = settings.scriptDirectories?.usage || settings.i18nDir;
|
|
40
40
|
|
|
41
41
|
// Auto-detect only if settings don't specify directories
|
|
42
42
|
if (!sourceDir) {
|
|
@@ -111,8 +111,8 @@ async function getConfig() {
|
|
|
111
111
|
const config = {
|
|
112
112
|
sourceDir: sourceDir,
|
|
113
113
|
i18nDir: i18nDir,
|
|
114
|
-
sourceLanguage: settings.
|
|
115
|
-
outputDir: settings.
|
|
114
|
+
sourceLanguage: settings.sourceLanguage || 'en',
|
|
115
|
+
outputDir: settings.outputDir || './i18ntk-reports',
|
|
116
116
|
excludeDirs: settings.processing?.excludeDirs || [
|
|
117
117
|
'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
|
|
118
118
|
'i18ntk-reports', 'reports', 'dev', 'utils', 'test', 'tests'
|
package/main/i18ntk-validate.js
CHANGED
|
@@ -27,12 +27,16 @@ async function getConfig(t) {
|
|
|
27
27
|
try {
|
|
28
28
|
SecurityUtils.logSecurityEvent(t('validate.configAccess'), 'info', 'Accessing configuration for validation');
|
|
29
29
|
const settings = settingsManager.getSettings();
|
|
30
|
+
|
|
31
|
+
// Check for per-script directory override, fallback to global sourceDir
|
|
32
|
+
const sourceDir = settings.scriptDirectories?.validate || settings.sourceDir || './locales';
|
|
33
|
+
|
|
30
34
|
const config = {
|
|
31
|
-
sourceDir:
|
|
32
|
-
sourceLanguage: settings.
|
|
33
|
-
notTranslatedMarker: settings.processing?.notTranslatedMarker || 'NOT_TRANSLATED',
|
|
34
|
-
excludeFiles: settings.processing?.excludeFiles || ['.DS_Store', 'Thumbs.db'],
|
|
35
|
-
strictMode: settings.processing?.strictMode || false,
|
|
35
|
+
sourceDir: sourceDir,
|
|
36
|
+
sourceLanguage: settings.sourceLanguage || 'en',
|
|
37
|
+
notTranslatedMarker: settings.notTranslatedMarker || settings.processing?.notTranslatedMarker || 'NOT_TRANSLATED',
|
|
38
|
+
excludeFiles: settings.excludeFiles || settings.processing?.excludeFiles || ['.DS_Store', 'Thumbs.db'],
|
|
39
|
+
strictMode: settings.strictMode || settings.processing?.strictMode || false,
|
|
36
40
|
uiLanguage: settings.language || 'en'
|
|
37
41
|
};
|
|
38
42
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18ntk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "i18ntk (i18n Toolkit) - A comprehensive, enterprise-grade internationalization (i18n) management toolkit for JavaScript/TypeScript projects with advanced analysis, validation, and automation features",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"analysis",
|
|
21
21
|
"enterprise",
|
|
22
22
|
"debugging",
|
|
23
|
-
"reporting"
|
|
23
|
+
"reporting",
|
|
24
|
+
"path-resolution",
|
|
25
|
+
"directory-configuration"
|
|
24
26
|
],
|
|
25
27
|
"homepage": "https://github.com/vladnoskv/i18n-management-toolkit-main#readme",
|
|
26
28
|
"bugs": {
|
|
@@ -97,23 +99,15 @@
|
|
|
97
99
|
"test": "node utils/test-complete-system.js"
|
|
98
100
|
},
|
|
99
101
|
"dependencies": {
|
|
100
|
-
"
|
|
101
|
-
"i18next": "^25.3.2",
|
|
102
|
-
"react": "^19.1.0",
|
|
103
|
-
"react-i18next": "^15.6.1",
|
|
104
|
-
"void-elements": "^3.1.0"
|
|
102
|
+
"i18next": "^25.3.2"
|
|
105
103
|
},
|
|
106
104
|
"devDependencies": {},
|
|
107
105
|
"peerDependencies": {
|
|
108
|
-
"i18next": ">=20.0.0"
|
|
109
|
-
"react-i18next": "^15.6.1"
|
|
106
|
+
"i18next": ">=20.0.0"
|
|
110
107
|
},
|
|
111
108
|
"peerDependenciesMeta": {
|
|
112
109
|
"i18next": {
|
|
113
110
|
"optional": false
|
|
114
|
-
},
|
|
115
|
-
"react-i18next": {
|
|
116
|
-
"optional": true
|
|
117
111
|
}
|
|
118
112
|
},
|
|
119
113
|
"engines": {
|
|
@@ -124,26 +118,33 @@
|
|
|
124
118
|
},
|
|
125
119
|
"preferGlobal": true,
|
|
126
120
|
"versionInfo": {
|
|
127
|
-
"version": "1.
|
|
128
|
-
"releaseDate": "
|
|
129
|
-
"lastUpdated": "
|
|
121
|
+
"version": "1.3.1",
|
|
122
|
+
"releaseDate": "12/2024",
|
|
123
|
+
"lastUpdated": "12/2024",
|
|
130
124
|
"maintainer": "Vladimir Noskov",
|
|
131
125
|
"changelog": "./CHANGELOG.md",
|
|
132
126
|
"documentation": "./README.md",
|
|
133
127
|
"apiReference": "./docs/api/API_REFERENCE.md",
|
|
134
128
|
"majorChanges": [
|
|
129
|
+
"๐ Added per-script directory configuration support",
|
|
130
|
+
"๐ง Fixed path resolution to use relative paths from project root",
|
|
131
|
+
"๐ Enhanced internationalization with new translation keys",
|
|
132
|
+
"๐ Added custom directory override for each script type",
|
|
133
|
+
"โ๏ธ Improved settings CLI with script directory configuration",
|
|
135
134
|
"๐งน Enhanced documentation and cleaned up npm metadata",
|
|
136
135
|
"๐ Marked all previous versions as deprecated due to bugs",
|
|
137
136
|
"๐ Celebrated 200+ downloads milestone",
|
|
138
137
|
"๐ Fixed repository URLs to point to correct GitHub location",
|
|
139
|
-
"๐ Updated all version references to 1.
|
|
138
|
+
"๐ Updated all version references to 1.3.1",
|
|
140
139
|
"๐ Temporarily disabled UI language switching to focus on English core functionality",
|
|
141
140
|
"๐ Centralized reports in i18ntk-reports/ directory",
|
|
142
|
-
"โ๏ธ Organized configuration files in settings/ directory"
|
|
141
|
+
"โ๏ธ Organized configuration files in settings/ directory",
|
|
142
|
+
"๐ Added reset to default values option",
|
|
143
|
+
"๐งน Cleaned up unused dependencies (React, react-i18next, html-parse-stringify, void-elements)"
|
|
143
144
|
],
|
|
144
|
-
"breakingChanges": ["Removed option 10 (Change UI language) from main menu"],
|
|
145
|
+
"breakingChanges": ["Removed option 10 (Change UI language) from main menu (Feature under redevelopment)"],
|
|
145
146
|
"deprecations": ["<1.2.0", "1.0.x", "1.1.x"],
|
|
146
|
-
"nextVersion": "1.
|
|
147
|
+
"nextVersion": "1.3.1",
|
|
147
148
|
"supportedNodeVersions": ">=16.0.0",
|
|
148
149
|
"supportedFrameworks": {
|
|
149
150
|
"react-i18next": ">=11.0.0",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// This script copies English translation files to other language directories,
|
|
2
|
+
// prefixing the keys with the country code and using the English value as a fallback.
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const uiLocalesPath = path.join(__dirname, '..', 'ui-locales');
|
|
8
|
+
const englishLocalesPath = path.join(uiLocalesPath, 'en');
|
|
9
|
+
|
|
10
|
+
function copyTranslations() {
|
|
11
|
+
console.log('Starting translation copy process...');
|
|
12
|
+
|
|
13
|
+
// Read all English JSON files
|
|
14
|
+
const englishFiles = fs.readdirSync(englishLocalesPath).filter(file => file.endsWith('.json'));
|
|
15
|
+
|
|
16
|
+
// Get all language directories except 'en'
|
|
17
|
+
const languageDirs = fs.readdirSync(uiLocalesPath)
|
|
18
|
+
.filter(dir => fs.statSync(path.join(uiLocalesPath, dir)).isDirectory() && dir !== 'en');
|
|
19
|
+
|
|
20
|
+
for (const lang of languageDirs) {
|
|
21
|
+
console.log(`Processing language: ${lang}`);
|
|
22
|
+
const langPath = path.join(uiLocalesPath, lang);
|
|
23
|
+
|
|
24
|
+
for (const file of englishFiles) {
|
|
25
|
+
const englishFilePath = path.join(englishLocalesPath, file);
|
|
26
|
+
const langFilePath = path.join(langPath, file);
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const englishContent = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'));
|
|
30
|
+
let langContent = {};
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(langFilePath)) {
|
|
33
|
+
langContent = JSON.parse(fs.readFileSync(langFilePath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let changesMade = false;
|
|
37
|
+
|
|
38
|
+
// Function to recursively synchronize keys
|
|
39
|
+
function synchronizeKeys(source, target, currentPath = '') {
|
|
40
|
+
let currentChanges = false;
|
|
41
|
+
|
|
42
|
+
// Add missing keys from source to target
|
|
43
|
+
for (const key in source) {
|
|
44
|
+
if (Object.hasOwnProperty.call(source, key)) {
|
|
45
|
+
const fullPath = currentPath ? `${currentPath}.${key}` : key;
|
|
46
|
+
if (typeof source[key] === 'object' && source[key] !== null) {
|
|
47
|
+
if (!Object.hasOwnProperty.call(target, key) || typeof target[key] !== 'object' || target[key] === null) {
|
|
48
|
+
target[key] = {};
|
|
49
|
+
currentChanges = true;
|
|
50
|
+
}
|
|
51
|
+
if (synchronizeKeys(source[key], target[key], fullPath)) {
|
|
52
|
+
currentChanges = true;
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
if (!Object.hasOwnProperty.call(target, key)) {
|
|
56
|
+
target[key] = `[${lang.toUpperCase()}] ${source[key]}`;
|
|
57
|
+
currentChanges = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Remove extra keys from target that are not in source
|
|
64
|
+
for (const key in target) {
|
|
65
|
+
if (Object.hasOwnProperty.call(target, key)) {
|
|
66
|
+
if (!Object.hasOwnProperty.call(source, key)) {
|
|
67
|
+
delete target[key];
|
|
68
|
+
currentChanges = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return currentChanges;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (synchronizeKeys(englishContent, langContent)) {
|
|
76
|
+
fs.writeFileSync(langFilePath, JSON.stringify(langContent, null, 2), 'utf8');
|
|
77
|
+
console.log(` Updated ${file} for ${lang}`);
|
|
78
|
+
} else {
|
|
79
|
+
console.log(` No changes needed for ${file} in ${lang}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`Error processing ${file} for ${lang}: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
console.log('Translation copy process completed.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
copyTranslations();
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
"ru"
|
|
11
11
|
],
|
|
12
12
|
"outputDir": "./i18ntk-reports",
|
|
13
|
+
"scriptDirectories": {
|
|
14
|
+
"analyze": null,
|
|
15
|
+
"init": null,
|
|
16
|
+
"validate": null,
|
|
17
|
+
"complete": null,
|
|
18
|
+
"manage": null,
|
|
19
|
+
"summary": null,
|
|
20
|
+
"usage": null,
|
|
21
|
+
"sizing": null
|
|
22
|
+
},
|
|
13
23
|
"reportLanguage": "auto",
|
|
14
24
|
"theme": "light",
|
|
15
25
|
"autoSave": true,
|