@controleonline/ui-common 1.2.71 → 1.2.76
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/.github/actions/workers/manager/action.yml +147 -0
- package/.github/actions/workers/qa/action.yml +44 -0
- package/.github/actions/workers/security/action.yml +44 -0
- package/.github/actions/workers/technical-documenter/action.yml +51 -0
- package/.github/workflows/manager-worker.yml +168 -0
- package/.github/workflows/publish-npm.yml +45 -0
- package/.gitmodules +3 -0
- package/AGENTS.md +6 -0
- package/README.md +14 -0
- package/package.json +3 -2
- package/src/api/fetch.js +1 -1
- package/src/api/index.js +92 -22
- package/src/react/components/AddImportModal.js +94 -110
- package/src/react/components/BottomNavigationBar.config.js +211 -231
- package/src/react/components/CategoryForm.js +52 -35
- package/src/react/components/DefaultProvider.native.js +131 -95
- package/src/react/components/DefaultProvider.web.js +148 -74
- package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
- package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
- package/src/react/components/RuntimeInfoFooter.js +164 -104
- package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
- package/src/react/components/UnifiedPaymentBar.js +7 -4
- package/src/react/components/UserAvatar.js +160 -0
- package/src/react/components/integrations/IntegrationHero/index.js +48 -0
- package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
- package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
- package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
- package/src/react/config/deviceConfigBootstrap.js +5 -3
- package/src/react/pages/Connections.js +229 -0
- package/src/react/pages/Connections.styles.js +222 -0
- package/src/react/pages/DeviceDetailPage.js +2865 -0
- package/src/react/pages/DeviceDetailPage.styles.js +308 -0
- package/src/react/pages/Devices/currentDevice.js +194 -0
- package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
- package/src/react/pages/Devices/deviceTypes/all.js +23 -0
- package/src/react/pages/Devices/deviceTypes/device.js +25 -0
- package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
- package/src/react/pages/Devices/deviceTypes/display.js +26 -0
- package/src/react/pages/Devices/deviceTypes/index.js +145 -0
- package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
- package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
- package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
- package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
- package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
- package/src/react/pages/Devices.js +183 -0
- package/src/react/pages/Devices.styles.js +353 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
- package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
- package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
- package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
- package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
- package/src/react/pages/Food99IntegrationPage.js +1 -0
- package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
- package/src/react/pages/GenericLogPage.js +42 -167
- package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
- package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
- package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
- package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
- package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
- package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
- package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
- package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
- package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
- package/src/react/pages/IFoodIntegrationPage.js +1 -0
- package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
- package/src/react/pages/Imports.js +214 -166
- package/src/react/pages/IntegrationConfigPage.js +744 -0
- package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
- package/src/react/pages/Integrations.js +299 -0
- package/src/react/pages/Integrations.styles.js +130 -0
- package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
- package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
- package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
- package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
- package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
- package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
- package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
- package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
- package/src/react/pages/PrinterDeviceFormPage.js +521 -0
- package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
- package/src/react/pages/WhatsAppConnectionPage.js +459 -0
- package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
- package/src/react/pages/integrationsCatalog.js +399 -0
- package/src/react/services/Cielo/Cielo.js +193 -193
- package/src/react/utils/categoryContexts.js +46 -0
- package/src/react/utils/entityDisplay.js +222 -217
- package/src/react/utils/fileUrl.js +39 -20
- package/src/react/utils/integrationPage.js +16 -0
- package/src/react/utils/runtimeFooter.js +40 -22
- package/src/react/utils/runtimeLanguage.js +50 -9
- package/src/react/utils/runtimeMenu.js +191 -131
- package/src/react/utils/runtimeZoom.js +43 -0
- package/src/react/utils/shopConfig.js +27 -15
- package/src/react/utils/shopFranchises.js +126 -1
- package/src/react/utils/storeColumns.js +23 -4
- package/src/react/utils/userAvatar.js +104 -0
- package/src/store/categories/index.js +48 -20
- package/src/store/device_config/index.js +62 -21
- package/src/store/entity_log/index.js +22 -1
- package/src/store/imports/customActions.js +51 -0
- package/src/store/imports/index.js +89 -88
- package/src/store/status/index.js +11 -11
- package/src/store/themes/index.js +22 -0
- package/src/store/timezones/index.js +3 -1
- package/src/tests/browser/manager/connections.spec.js +143 -0
- package/src/tests/browser/manager/devices-current.spec.js +343 -0
- package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
- package/src/tests/react/api/deviceHeaders.test.js +114 -0
- package/src/tests/react/components/AddImportModal.test.js +166 -0
- package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
- package/src/tests/react/components/UserAvatar.test.js +52 -0
- package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
- package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
- package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
- package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
- package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
- package/src/tests/react/services/Cielo.test.js +190 -190
- package/src/tests/react/store/categoriesOrdering.test.js +26 -0
- package/src/tests/react/utils/appDomain.test.js +62 -0
- package/src/tests/react/utils/entityDisplay.test.js +2 -2
- package/src/tests/react/utils/fileUrl.test.js +3 -3
- package/src/tests/react/utils/formatter.test.mjs +33 -0
- package/src/tests/react/utils/runtimeFooter.test.js +29 -4
- package/src/tests/react/utils/runtimeLanguage.test.js +79 -9
- package/src/tests/react/utils/runtimeMenu.test.js +198 -134
- package/src/tests/react/utils/runtimeZoom.test.js +38 -0
- package/src/tests/react/utils/shopConfig.test.js +10 -9
- package/src/tests/react/utils/shopFranchises.test.js +43 -0
- package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
- package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
- package/src/tests/react/utils/translate.test.mjs +141 -349
- package/src/tests/react/utils/userAvatar.test.js +63 -0
- package/src/utils/appDomain.js +15 -4
- package/src/utils/formatter.js +104 -16
- package/src/utils/integrationConfigs.js +80 -80
- package/src/utils/translate.js +222 -438
- package/src/utils/translateDiscovery.js +96 -0
- package/src/utils/translateNormalize.js +33 -0
- package/src/utils/translatePending.js +92 -0
- package/src/utils/translateStorage.js +142 -0
- package/.github/agents/developer.agent.md +0 -30
- package/.github/agents/devops.agent.md +0 -30
- package/.github/agents/qa.agent.md +0 -30
- package/.github/agents/security.agent.md +0 -30
- package/.github/workflows/npmjs.yml +0 -35
package/src/utils/translate.js
CHANGED
|
@@ -1,12 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
|
+
formatMessage,
|
|
3
|
+
isNonEmptyMessage,
|
|
4
|
+
normalizeId,
|
|
5
|
+
normalizeLanguageCode,
|
|
6
|
+
resolveReferenceId,
|
|
7
|
+
} from "./translateNormalize.js";
|
|
8
|
+
import {
|
|
9
|
+
TRANSLATES_STORAGE_KEY,
|
|
10
|
+
collectCompaniesToCache,
|
|
11
|
+
getCompanyBucket as readCompanyBucket,
|
|
12
|
+
getLanguageBucket as readLanguageBucket,
|
|
13
|
+
getStoreBucket as readStoreBucket,
|
|
14
|
+
loadStorageObject,
|
|
15
|
+
writeMessageToBucket,
|
|
16
|
+
} from "./translateStorage.js";
|
|
17
|
+
import {
|
|
18
|
+
getPendingMessages as readPendingMessages,
|
|
19
|
+
getQueuedTranslateGroups as readQueuedGroups,
|
|
20
|
+
getQueuedTranslateGroupsForStore as readQueuedGroupsForStore,
|
|
21
|
+
hasPendingTranslate as readHasPendingTranslate,
|
|
22
|
+
} from "./translatePending.js";
|
|
23
|
+
import {
|
|
24
|
+
discoveryAll as runDiscoveryAll,
|
|
25
|
+
discoveryStoreTranslate as runDiscoveryStoreTranslate,
|
|
26
|
+
fetchTranslates as runFetchTranslates,
|
|
27
|
+
resolveStoreTranslates as runResolveStoreTranslates,
|
|
28
|
+
} from "./translateDiscovery.js";
|
|
2
29
|
|
|
3
30
|
export default class Translate {
|
|
4
31
|
constructor(companies, defaultCompany, currentCompany, stores, translateStore) {
|
|
5
|
-
this.translates =
|
|
32
|
+
this.translates = loadStorageObject(TRANSLATES_STORAGE_KEY);
|
|
6
33
|
|
|
7
|
-
this.language =
|
|
8
|
-
|
|
9
|
-
|
|
34
|
+
this.language =
|
|
35
|
+
normalizeLanguageCode(
|
|
36
|
+
JSON.parse(localStorage.getItem("config") || "{}").language,
|
|
37
|
+
) || "pt-br";
|
|
10
38
|
|
|
11
39
|
this.defaultCompany = defaultCompany;
|
|
12
40
|
this.currentCompany = currentCompany;
|
|
@@ -17,6 +45,7 @@ export default class Translate {
|
|
|
17
45
|
this.bootstrapStores = new Set(this.getStoreList());
|
|
18
46
|
this.discoveredStores = new Set();
|
|
19
47
|
this.pendingStoreDiscoveries = new Map();
|
|
48
|
+
this.pendingMissingTranslateSchedules = new Set();
|
|
20
49
|
this.pendingTranslateResolutionPromise = null;
|
|
21
50
|
this.pendingPersistRequests = new Map();
|
|
22
51
|
this.t = this.t.bind(this);
|
|
@@ -27,194 +56,101 @@ export default class Translate {
|
|
|
27
56
|
this.hydrateDiscoveredStores();
|
|
28
57
|
}
|
|
29
58
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
59
|
+
normalizeId(value) {
|
|
60
|
+
return normalizeId(value);
|
|
61
|
+
}
|
|
34
62
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
63
|
+
normalizeLanguageCode(value) {
|
|
64
|
+
return normalizeLanguageCode(value);
|
|
65
|
+
}
|
|
38
66
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} catch {
|
|
42
|
-
return {};
|
|
43
|
-
}
|
|
67
|
+
formatMessage(key) {
|
|
68
|
+
return formatMessage(key);
|
|
44
69
|
}
|
|
45
|
-
|
|
46
|
-
getLanguageBucket(language = this.language, createIfMissing = false) {
|
|
47
|
-
const normalizedLanguage = this.normalizeLanguageCode(language);
|
|
48
|
-
if (!normalizedLanguage) return null;
|
|
49
70
|
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
resolveReferenceId(value) {
|
|
72
|
+
if (value == null) return null;
|
|
73
|
+
if (typeof value === "object") {
|
|
74
|
+
return this.normalizeId(
|
|
75
|
+
value.id || value["@id"] || value.people || value.language,
|
|
76
|
+
);
|
|
52
77
|
}
|
|
78
|
+
return this.normalizeId(value);
|
|
79
|
+
}
|
|
53
80
|
|
|
54
|
-
|
|
81
|
+
getLanguageBucket(language = this.language, createIfMissing = false) {
|
|
82
|
+
return readLanguageBucket(this.translates, language, createIfMissing);
|
|
55
83
|
}
|
|
56
84
|
|
|
57
85
|
getCompanyBucket(companyId, createIfMissing = false, language = this.language) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (!languageBucket.companies && createIfMissing) {
|
|
65
|
-
languageBucket.companies = {};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
createIfMissing &&
|
|
70
|
-
languageBucket.companies &&
|
|
71
|
-
!languageBucket.companies[normalizedCompanyId]
|
|
72
|
-
) {
|
|
73
|
-
languageBucket.companies[normalizedCompanyId] = {};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return languageBucket.companies?.[normalizedCompanyId] || null;
|
|
86
|
+
return readCompanyBucket(
|
|
87
|
+
this.translates,
|
|
88
|
+
companyId,
|
|
89
|
+
createIfMissing,
|
|
90
|
+
language,
|
|
91
|
+
);
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
getStoreBucket(companyId, store, createIfMissing = false, language = this.language) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
companyBucket[store] = {};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return companyBucket[store] || null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const languageBucket = this.getLanguageBucket(language, createIfMissing);
|
|
94
|
-
if (!languageBucket) return null;
|
|
95
|
-
|
|
96
|
-
if (createIfMissing && !languageBucket[store]) {
|
|
97
|
-
languageBucket[store] = {};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return languageBucket[store] || null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
normalizeId(value) {
|
|
104
|
-
if (value == null) return null;
|
|
105
|
-
|
|
106
|
-
const match = String(value).match(/\d+/);
|
|
107
|
-
return match?.[0] || null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
normalizeLanguageCode(value) {
|
|
111
|
-
if (typeof value !== "string") return "";
|
|
112
|
-
|
|
113
|
-
return value.trim().replace(/_/g, "-").toLowerCase();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
getCompaniesToCache() {
|
|
117
|
-
const candidates = [
|
|
118
|
-
this.defaultCompany,
|
|
119
|
-
this.currentCompany,
|
|
120
|
-
].filter((company) => company?.id);
|
|
121
|
-
|
|
122
|
-
const unique = [];
|
|
123
|
-
const seen = new Set();
|
|
124
|
-
|
|
125
|
-
candidates.forEach((company) => {
|
|
126
|
-
const companyId = this.normalizeId(company.id);
|
|
127
|
-
if (!companyId || seen.has(companyId)) return;
|
|
128
|
-
|
|
129
|
-
seen.add(companyId);
|
|
130
|
-
unique.push(company);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
return unique;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
getStoreList() {
|
|
137
|
-
if (Array.isArray(this.stores)) {
|
|
138
|
-
return this.stores.filter(Boolean);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return this.stores ? [this.stores] : [];
|
|
95
|
+
return readStoreBucket(
|
|
96
|
+
this.translates,
|
|
97
|
+
companyId,
|
|
98
|
+
store,
|
|
99
|
+
createIfMissing,
|
|
100
|
+
language,
|
|
101
|
+
);
|
|
142
102
|
}
|
|
143
103
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (!messages || typeof messages !== "object" || Array.isArray(messages)) {
|
|
147
|
-
return {};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return messages;
|
|
104
|
+
getCompaniesToCache() {
|
|
105
|
+
return collectCompaniesToCache(this.defaultCompany, this.currentCompany);
|
|
151
106
|
}
|
|
152
107
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return pendingMessages[normalizedLanguage] || null;
|
|
108
|
+
// Always load current + main company so runtime resolution can apply
|
|
109
|
+
// current company -> main company -> global fallback (getMessageFromBuckets).
|
|
110
|
+
getResolveCompaniesToCache() {
|
|
111
|
+
return this.getCompaniesToCache();
|
|
159
112
|
}
|
|
160
113
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const languageBucket = this.getPendingLanguageBucket(language);
|
|
166
|
-
if (!languageBucket) return null;
|
|
167
|
-
|
|
168
|
-
const companiesBucket = languageBucket.companies;
|
|
169
|
-
if (!companiesBucket || typeof companiesBucket !== "object") {
|
|
170
|
-
return null;
|
|
114
|
+
getStoreList() {
|
|
115
|
+
if (Array.isArray(this.stores)) {
|
|
116
|
+
return this.stores.filter(Boolean);
|
|
171
117
|
}
|
|
172
|
-
|
|
173
|
-
return companiesBucket[normalizedCompanyId] || null;
|
|
118
|
+
return this.stores ? [this.stores] : [];
|
|
174
119
|
}
|
|
175
120
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (!normalizedStore) return null;
|
|
179
|
-
|
|
180
|
-
const companyBucket = this.getPendingCompanyBucket(companyId, language);
|
|
181
|
-
if (!companyBucket) return null;
|
|
182
|
-
|
|
183
|
-
return companyBucket[normalizedStore] || null;
|
|
121
|
+
getPendingMessages() {
|
|
122
|
+
return readPendingMessages(this.translateStore);
|
|
184
123
|
}
|
|
185
124
|
|
|
186
125
|
hasPendingTranslate(store, type, key) {
|
|
187
|
-
|
|
126
|
+
return readHasPendingTranslate(
|
|
127
|
+
this.translateStore,
|
|
188
128
|
this.defaultCompany?.id,
|
|
189
129
|
store,
|
|
130
|
+
type,
|
|
131
|
+
key,
|
|
190
132
|
this.language,
|
|
191
133
|
);
|
|
192
|
-
|
|
193
|
-
return Boolean(pendingStoreBucket?.[type]?.[key]);
|
|
194
134
|
}
|
|
195
135
|
|
|
196
136
|
getStoreDiscoveryToken(store) {
|
|
197
|
-
return
|
|
198
|
-
this.language,
|
|
199
|
-
String(store || ""),
|
|
200
|
-
].join("::");
|
|
137
|
+
return `discover:${store}:${this.language}`;
|
|
201
138
|
}
|
|
202
139
|
|
|
203
140
|
canDiscoverStore() {
|
|
204
|
-
return typeof this.translateActions?.
|
|
141
|
+
return typeof this.translateActions?.resolveQueuedMessages === "function";
|
|
205
142
|
}
|
|
206
143
|
|
|
207
144
|
hasCachedBootstrapStore(store) {
|
|
208
|
-
if (!store) return false;
|
|
209
|
-
|
|
210
145
|
const companies = this.getCompaniesToCache();
|
|
211
146
|
if (companies.length === 0) return false;
|
|
212
|
-
|
|
213
|
-
|
|
147
|
+
return companies.every(
|
|
148
|
+
(company) => this.getStoreBucket(company.id, store) != null,
|
|
149
|
+
);
|
|
214
150
|
}
|
|
215
151
|
|
|
216
152
|
hydrateDiscoveredStores() {
|
|
217
|
-
this.
|
|
153
|
+
this.getStoreList().forEach((store) => {
|
|
218
154
|
if (this.hasCachedBootstrapStore(store)) {
|
|
219
155
|
this.markStoreDiscovered(store);
|
|
220
156
|
}
|
|
@@ -222,96 +158,55 @@ export default class Translate {
|
|
|
222
158
|
}
|
|
223
159
|
|
|
224
160
|
hasDiscoveredStore(store) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const token = this.getStoreDiscoveryToken(store);
|
|
228
|
-
if (this.discoveredStores.has(token)) {
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (this.hasCachedBootstrapStore(store)) {
|
|
233
|
-
this.markStoreDiscovered(store);
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return false;
|
|
161
|
+
return this.discoveredStores.has(store);
|
|
238
162
|
}
|
|
239
163
|
|
|
240
164
|
markStoreDiscovered(store) {
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
this.discoveredStores.add(this.getStoreDiscoveryToken(store));
|
|
165
|
+
if (store) this.discoveredStores.add(store);
|
|
244
166
|
}
|
|
245
167
|
|
|
246
168
|
ensureStoreDiscovered(store) {
|
|
247
|
-
if (!store || !this.canDiscoverStore()) {
|
|
169
|
+
if (!store || this.hasDiscoveredStore(store) || !this.canDiscoverStore()) {
|
|
248
170
|
return Promise.resolve(this.translates);
|
|
249
171
|
}
|
|
250
172
|
|
|
251
|
-
if (this.
|
|
252
|
-
return
|
|
173
|
+
if (this.pendingStoreDiscoveries.has(store)) {
|
|
174
|
+
return this.pendingStoreDiscoveries.get(store);
|
|
253
175
|
}
|
|
254
176
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
return this.pendingStoreDiscoveries.get(token);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const discoveryPromise = Promise.resolve()
|
|
261
|
-
.then(() => this.discoveryStoreTranslate(store))
|
|
177
|
+
const discoveryPromise = this.discoveryStoreTranslate(store)
|
|
178
|
+
.catch(() => this.translates)
|
|
262
179
|
.finally(() => {
|
|
263
|
-
this.pendingStoreDiscoveries.delete(
|
|
180
|
+
this.pendingStoreDiscoveries.delete(store);
|
|
264
181
|
});
|
|
265
182
|
|
|
266
|
-
this.pendingStoreDiscoveries.set(
|
|
267
|
-
|
|
183
|
+
this.pendingStoreDiscoveries.set(store, discoveryPromise);
|
|
268
184
|
return discoveryPromise;
|
|
269
185
|
}
|
|
270
186
|
|
|
271
187
|
notifyTranslationsUpdated() {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (typeof globalObject?.refreshTranslationsUI === "function") {
|
|
276
|
-
globalObject.refreshTranslationsUI();
|
|
188
|
+
if (typeof this.translateActions?.setMessages === "function") {
|
|
189
|
+
this.translateActions.setMessages(this.translates);
|
|
277
190
|
}
|
|
278
191
|
}
|
|
279
192
|
|
|
280
193
|
syncCachedMessagesToStore() {
|
|
281
|
-
if (typeof this.translateActions?.setMessages
|
|
282
|
-
|
|
194
|
+
if (typeof this.translateActions?.setMessages === "function") {
|
|
195
|
+
this.translateActions.setMessages(this.translates);
|
|
283
196
|
}
|
|
284
|
-
|
|
285
|
-
this.translateActions.setMessages({
|
|
286
|
-
...this.translates,
|
|
287
|
-
});
|
|
288
197
|
}
|
|
289
198
|
|
|
290
199
|
getPersistRequestToken(store, type, key) {
|
|
291
|
-
return
|
|
292
|
-
this.language,
|
|
293
|
-
this.normalizeId(this.defaultCompany?.id),
|
|
294
|
-
String(store || "").trim(),
|
|
295
|
-
String(type || "").trim(),
|
|
296
|
-
String(key || "").trim(),
|
|
297
|
-
].join("::");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
resolveReferenceId(value) {
|
|
301
|
-
if (!value) return null;
|
|
302
|
-
|
|
303
|
-
if (typeof value === "object") {
|
|
304
|
-
return this.normalizeId(value.id || value["@id"] || value.people || value.language);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return this.normalizeId(value);
|
|
200
|
+
return `${this.language}:${this.normalizeId(this.defaultCompany?.id)}:${store}:${type}:${key}`;
|
|
308
201
|
}
|
|
309
202
|
|
|
310
203
|
cacheTranslateRecord(record, fallbackCompanyId = null, fallbackLanguage = this.language) {
|
|
311
|
-
const companyId =
|
|
204
|
+
const companyId =
|
|
205
|
+
this.resolveReferenceId(record?.people) || this.normalizeId(fallbackCompanyId);
|
|
312
206
|
const language =
|
|
313
|
-
this.normalizeLanguageCode(
|
|
314
|
-
|
|
207
|
+
this.normalizeLanguageCode(
|
|
208
|
+
record?.language?.language || record?.language?.locale || fallbackLanguage,
|
|
209
|
+
) || this.language;
|
|
315
210
|
const store = String(record?.store || "").trim();
|
|
316
211
|
const type = String(record?.type || "").trim();
|
|
317
212
|
const key = String(record?.key || "").trim();
|
|
@@ -321,42 +216,47 @@ export default class Translate {
|
|
|
321
216
|
return false;
|
|
322
217
|
}
|
|
323
218
|
|
|
324
|
-
const previousMessage = this.getStoreBucket(companyId, store, false, language)?.[
|
|
219
|
+
const previousMessage = this.getStoreBucket(companyId, store, false, language)?.[
|
|
220
|
+
type
|
|
221
|
+
]?.[key];
|
|
325
222
|
this.findMessage(store, type, key, message, companyId, language);
|
|
326
223
|
|
|
327
224
|
return previousMessage !== message;
|
|
328
225
|
}
|
|
329
|
-
|
|
330
|
-
getMessageFromBuckets(store, type, key) {
|
|
331
|
-
const companyIds = [
|
|
332
|
-
this.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
226
|
+
|
|
227
|
+
getMessageFromBuckets(store, type, key) {
|
|
228
|
+
const companyIds = [this.currentCompany?.id, this.defaultCompany?.id]
|
|
229
|
+
.map((value) => this.normalizeId(value))
|
|
230
|
+
.filter(Boolean);
|
|
231
|
+
|
|
232
|
+
// Prefer current company, then main/default company, then language-global bucket.
|
|
233
|
+
// Empty/whitespace values do not count as a hit so fallback can still apply.
|
|
234
|
+
for (const companyId of companyIds) {
|
|
235
|
+
const companyMessage = this.getStoreBucket(companyId, store)?.[type]?.[key];
|
|
236
|
+
if (isNonEmptyMessage(companyMessage)) {
|
|
237
|
+
return companyMessage;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const globalMessage = this.getStoreBucket(null, store)?.[type]?.[key];
|
|
242
|
+
if (isNonEmptyMessage(globalMessage)) {
|
|
243
|
+
return globalMessage;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return globalMessage;
|
|
247
|
+
}
|
|
248
|
+
|
|
350
249
|
persistMissingTranslate(store, type, key, translate) {
|
|
351
250
|
if (!store || !type || !key || !this.defaultCompany?.id) return;
|
|
352
251
|
|
|
353
|
-
// Skip writes when the default company is not available in the current access scope.
|
|
354
252
|
const defaultCompanyId = this.normalizeId(this.defaultCompany?.id);
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
)
|
|
253
|
+
// Queueing is a local pending state used to drive resolve/discovery.
|
|
254
|
+
// Do NOT require defaultCompany to appear in this.companies: non-superadmin
|
|
255
|
+
// tenants often lack membership on the main company, but still must be able
|
|
256
|
+
// to resolve and cache the main catalog (read path). Backend gates writes.
|
|
257
|
+
if (!defaultCompanyId) {
|
|
359
258
|
return;
|
|
259
|
+
}
|
|
360
260
|
|
|
361
261
|
if (this.hasPendingTranslate(store, type, key)) {
|
|
362
262
|
return;
|
|
@@ -397,10 +297,14 @@ export default class Translate {
|
|
|
397
297
|
key,
|
|
398
298
|
translate,
|
|
399
299
|
revised: false,
|
|
400
|
-
})
|
|
300
|
+
}),
|
|
401
301
|
)
|
|
402
302
|
.then((result) => {
|
|
403
|
-
const changed = this.cacheTranslateRecord(
|
|
303
|
+
const changed = this.cacheTranslateRecord(
|
|
304
|
+
result,
|
|
305
|
+
defaultCompanyId,
|
|
306
|
+
this.language,
|
|
307
|
+
);
|
|
404
308
|
this.removePendingTranslate(store, type, key);
|
|
405
309
|
this.persist();
|
|
406
310
|
if (changed) {
|
|
@@ -418,15 +322,13 @@ export default class Translate {
|
|
|
418
322
|
}
|
|
419
323
|
|
|
420
324
|
removePendingTranslate(store, type, key) {
|
|
421
|
-
if (!store || !type || !key || !this.defaultCompany?.id) return;
|
|
422
|
-
|
|
423
325
|
if (typeof this.translateActions?.removePendingTranslate !== "function") {
|
|
424
326
|
return;
|
|
425
327
|
}
|
|
426
328
|
|
|
427
329
|
this.translateActions.removePendingTranslate({
|
|
428
330
|
language: this.language,
|
|
429
|
-
companyId: this.defaultCompany
|
|
331
|
+
companyId: this.defaultCompany?.id,
|
|
430
332
|
store,
|
|
431
333
|
type,
|
|
432
334
|
key,
|
|
@@ -434,44 +336,20 @@ export default class Translate {
|
|
|
434
336
|
}
|
|
435
337
|
|
|
436
338
|
getQueuedTranslateGroups() {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (!companyBucket) {
|
|
444
|
-
return [];
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const requests = [];
|
|
448
|
-
Object.entries(companyBucket).forEach(([store, storeBucket]) => {
|
|
449
|
-
if (!storeBucket || typeof storeBucket !== "object") {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
Object.entries(storeBucket).forEach(([type, typeBucket]) => {
|
|
454
|
-
if (!typeBucket || typeof typeBucket !== "object") {
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const keys = Object.keys(typeBucket)
|
|
459
|
-
.map((value) => String(value).trim())
|
|
460
|
-
.filter(Boolean);
|
|
461
|
-
|
|
462
|
-
if (keys.length === 0) {
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
requests.push({
|
|
467
|
-
store,
|
|
468
|
-
type,
|
|
469
|
-
keys,
|
|
470
|
-
});
|
|
471
|
-
});
|
|
472
|
-
});
|
|
339
|
+
return readQueuedGroups(
|
|
340
|
+
this.translateStore,
|
|
341
|
+
this.defaultCompany?.id,
|
|
342
|
+
this.language,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
473
345
|
|
|
474
|
-
|
|
346
|
+
getQueuedTranslateGroupsForStore(store) {
|
|
347
|
+
return readQueuedGroupsForStore(
|
|
348
|
+
this.translateStore,
|
|
349
|
+
this.defaultCompany?.id,
|
|
350
|
+
store,
|
|
351
|
+
this.language,
|
|
352
|
+
);
|
|
475
353
|
}
|
|
476
354
|
|
|
477
355
|
scheduleQueuedTranslateResolution() {
|
|
@@ -479,7 +357,9 @@ export default class Translate {
|
|
|
479
357
|
return this.pendingTranslateResolutionPromise;
|
|
480
358
|
}
|
|
481
359
|
|
|
482
|
-
this.pendingTranslateResolutionPromise = Promise
|
|
360
|
+
this.pendingTranslateResolutionPromise = new Promise((resolve) => {
|
|
361
|
+
setTimeout(resolve, 0);
|
|
362
|
+
})
|
|
483
363
|
.then(() => this.resolveQueuedTranslations())
|
|
484
364
|
.catch(() => this.translates)
|
|
485
365
|
.finally(() => {
|
|
@@ -499,33 +379,44 @@ export default class Translate {
|
|
|
499
379
|
return this.translates;
|
|
500
380
|
}
|
|
501
381
|
|
|
502
|
-
|
|
503
|
-
|
|
382
|
+
// Resolve against every company we keep in cache (current + main) so fallback
|
|
383
|
+
// translations from the main company are available at runtime.
|
|
384
|
+
const companies = this.getCompaniesToCache();
|
|
385
|
+
if (companies.length === 0) {
|
|
504
386
|
return this.translates;
|
|
505
387
|
}
|
|
506
388
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
language: this.language,
|
|
510
|
-
requests,
|
|
511
|
-
});
|
|
389
|
+
let changed = false;
|
|
390
|
+
const allResolvedItems = [];
|
|
512
391
|
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
392
|
+
for (const company of companies) {
|
|
393
|
+
const companyId = this.normalizeId(company?.id);
|
|
394
|
+
if (!companyId) continue;
|
|
516
395
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
396
|
+
const response = await this.translateActions.resolveQueuedMessages({
|
|
397
|
+
people: "/people/" + companyId,
|
|
398
|
+
language: this.language,
|
|
399
|
+
requests,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const resolvedItems = Array.isArray(response)
|
|
403
|
+
? response
|
|
404
|
+
: response?.member || response?.["hydra:member"] || [];
|
|
405
|
+
|
|
406
|
+
resolvedItems.forEach((item) => {
|
|
407
|
+
changed =
|
|
408
|
+
this.cacheTranslateRecord(item, companyId, this.language) || changed;
|
|
409
|
+
this.removePendingTranslate(item.store, item.type, item.key);
|
|
410
|
+
allResolvedItems.push(item);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
522
413
|
|
|
523
414
|
if (changed) {
|
|
524
415
|
this.persist();
|
|
525
416
|
this.notifyTranslationsUpdated();
|
|
526
417
|
}
|
|
527
418
|
|
|
528
|
-
return
|
|
419
|
+
return allResolvedItems;
|
|
529
420
|
}
|
|
530
421
|
|
|
531
422
|
t(store, type, key) {
|
|
@@ -533,183 +424,76 @@ export default class Translate {
|
|
|
533
424
|
const fallbackTranslate = this.formatMessage(key);
|
|
534
425
|
|
|
535
426
|
if (!translate) {
|
|
536
|
-
|
|
427
|
+
const scheduleKey = `${store}:${type}:${key}`;
|
|
428
|
+
if (!this.pendingMissingTranslateSchedules.has(scheduleKey)) {
|
|
429
|
+
this.pendingMissingTranslateSchedules.add(scheduleKey);
|
|
430
|
+
setTimeout(() => {
|
|
431
|
+
this.pendingMissingTranslateSchedules.delete(scheduleKey);
|
|
432
|
+
this.persistMissingTranslate(store, type, key, fallbackTranslate);
|
|
433
|
+
}, 0);
|
|
434
|
+
}
|
|
537
435
|
|
|
436
|
+
this.ensureStoreDiscovered(store);
|
|
538
437
|
return fallbackTranslate;
|
|
539
438
|
}
|
|
540
439
|
|
|
541
440
|
return translate;
|
|
542
|
-
}
|
|
543
|
-
|
|
441
|
+
}
|
|
442
|
+
|
|
544
443
|
reload() {
|
|
545
|
-
this.
|
|
546
|
-
|
|
444
|
+
this.translates = loadStorageObject(TRANSLATES_STORAGE_KEY);
|
|
445
|
+
this.syncCachedMessagesToStore();
|
|
547
446
|
}
|
|
548
447
|
|
|
549
448
|
clear() {
|
|
550
449
|
this.translates = {};
|
|
551
450
|
this.discoveredStores.clear();
|
|
552
451
|
this.pendingStoreDiscoveries.clear();
|
|
553
|
-
this.pendingTranslateResolutionPromise = null;
|
|
554
|
-
this.pendingPersistRequests.clear();
|
|
555
|
-
if (typeof this.translateActions?.setPendingMessages === "function") {
|
|
556
|
-
this.translateActions.setPendingMessages({});
|
|
557
|
-
}
|
|
558
452
|
this.persist();
|
|
559
453
|
}
|
|
560
454
|
|
|
561
455
|
shouldFetchStoreForCompany(store, company, force = false) {
|
|
562
456
|
if (force) return true;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (!companyId || !store) return false;
|
|
566
|
-
|
|
567
|
-
const storeBucket = this.getStoreBucket(companyId, store, false, this.language);
|
|
568
|
-
return storeBucket == null;
|
|
457
|
+
if (!company?.id || !store) return false;
|
|
458
|
+
return this.getStoreBucket(company.id, store) == null;
|
|
569
459
|
}
|
|
570
460
|
|
|
571
461
|
async discoveryAll(options = {}) {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
for (const company of companies) {
|
|
575
|
-
for (const store of this.getStoreList()) {
|
|
576
|
-
await this.discoveryStoreTranslate(store, {
|
|
577
|
-
...options,
|
|
578
|
-
companies: [company],
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return this.translates;
|
|
462
|
+
return runDiscoveryAll(this, options);
|
|
584
463
|
}
|
|
585
464
|
|
|
586
465
|
async discoveryStoreTranslate(store, options = {}) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const force = options.force === true;
|
|
590
|
-
if (!force && this.hasDiscoveredStore(store)) {
|
|
591
|
-
return this.translates;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
const companies = Array.isArray(options.companies) && options.companies.length > 0
|
|
595
|
-
? options.companies
|
|
596
|
-
: this.getCompaniesToCache();
|
|
597
|
-
if (companies.length === 0) {
|
|
598
|
-
this.markStoreDiscovered(store);
|
|
599
|
-
return this.translates;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
let hasChanges = false;
|
|
603
|
-
for (const company of companies) {
|
|
604
|
-
const changed = await this.fetchTranslates(store, company, { force });
|
|
605
|
-
hasChanges = changed || hasChanges;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
if (this.hasCachedBootstrapStore(store)) {
|
|
609
|
-
this.markStoreDiscovered(store);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (hasChanges) {
|
|
613
|
-
this.persist();
|
|
614
|
-
this.notifyTranslationsUpdated();
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
return this.translates;
|
|
466
|
+
return runDiscoveryStoreTranslate(this, store, options);
|
|
618
467
|
}
|
|
619
468
|
|
|
620
469
|
async fetchTranslates(store, company, options = {}) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const force = options.force === true;
|
|
624
|
-
if (!this.shouldFetchStoreForCompany(store, company, force)) {
|
|
625
|
-
return false;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
const params = {
|
|
629
|
-
store: store,
|
|
630
|
-
"language.language": this.language,
|
|
631
|
-
people: "/people/" + company.id,
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
const storeTranslates = [];
|
|
635
|
-
let page = 1;
|
|
636
|
-
let firstPageSize = null;
|
|
637
|
-
const maxPages = 1000;
|
|
638
|
-
|
|
639
|
-
while (page <= maxPages) {
|
|
640
|
-
const pageItems = await this.translateActions.getItems({
|
|
641
|
-
...params,
|
|
642
|
-
page,
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
if (!Array.isArray(pageItems) || pageItems.length === 0) break;
|
|
646
|
-
|
|
647
|
-
storeTranslates.push(...pageItems);
|
|
648
|
-
if (firstPageSize == null) firstPageSize = pageItems.length;
|
|
649
|
-
if (pageItems.length < firstPageSize) break;
|
|
650
|
-
|
|
651
|
-
page += 1;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const companyId = this.normalizeId(company?.id);
|
|
655
|
-
const storeBucket = this.getStoreBucket(companyId, store, true);
|
|
656
|
-
let changed = false;
|
|
657
|
-
|
|
658
|
-
if (storeBucket) {
|
|
659
|
-
if (Object.keys(storeBucket).length > 0) {
|
|
660
|
-
changed = true;
|
|
661
|
-
}
|
|
662
|
-
Object.keys(storeBucket).forEach((type) => {
|
|
663
|
-
delete storeBucket[type];
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
storeTranslates.forEach((element) => {
|
|
668
|
-
changed = this.cacheTranslateRecord(
|
|
669
|
-
{
|
|
670
|
-
...element,
|
|
671
|
-
people: element.people || companyId,
|
|
672
|
-
},
|
|
673
|
-
companyId,
|
|
674
|
-
this.language,
|
|
675
|
-
) || changed;
|
|
676
|
-
this.removePendingTranslate(element.store, element.type, element.key);
|
|
677
|
-
});
|
|
470
|
+
return runFetchTranslates(this, store, company, options);
|
|
471
|
+
}
|
|
678
472
|
|
|
679
|
-
|
|
473
|
+
async resolveStoreTranslates(store, company, options = {}) {
|
|
474
|
+
return runResolveStoreTranslates(this, store, company, options);
|
|
680
475
|
}
|
|
681
476
|
|
|
682
477
|
persist() {
|
|
683
478
|
if (typeof localStorage !== "undefined") {
|
|
684
479
|
localStorage.setItem("translates", JSON.stringify(this.translates));
|
|
685
480
|
}
|
|
686
|
-
|
|
687
481
|
this.syncCachedMessagesToStore();
|
|
688
482
|
}
|
|
689
483
|
|
|
690
484
|
findMessage(store, type, key, message, companyId = null, language = this.language) {
|
|
691
|
-
const
|
|
692
|
-
|
|
485
|
+
const written = writeMessageToBucket(
|
|
486
|
+
this.translates,
|
|
487
|
+
store,
|
|
488
|
+
type,
|
|
489
|
+
key,
|
|
490
|
+
message,
|
|
491
|
+
companyId,
|
|
492
|
+
language,
|
|
493
|
+
);
|
|
494
|
+
if (written == null) {
|
|
693
495
|
return this.formatMessage(key);
|
|
694
496
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (message !== null)
|
|
701
|
-
storeBucket[type][key] = message;
|
|
702
|
-
|
|
703
|
-
return storeBucket[type][key] || this.formatMessage(key);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
formatMessage(key) {
|
|
707
|
-
if (!key) return "";
|
|
708
|
-
|
|
709
|
-
return key
|
|
710
|
-
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
711
|
-
.replace(/_/g, " ")
|
|
712
|
-
.replace(/-/g, " ")
|
|
713
|
-
.replace(/^\w/, (c) => c.toUpperCase());
|
|
714
|
-
}
|
|
715
|
-
}
|
|
497
|
+
return written || this.formatMessage(key);
|
|
498
|
+
}
|
|
499
|
+
}
|