@plentymarkets/shop-core 1.14.2 → 1.14.4
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/dist/module.json +1 -1
- package/dist/module.mjs +8 -4
- package/dist/runtime/composables/localization/helpers/loadKeysHelper.js +57 -41
- package/dist/runtime/composables/localization/helpers/messageHelper.d.ts +11 -1
- package/dist/runtime/composables/localization/helpers/messageHelper.js +18 -9
- package/dist/runtime/composables/localization/helpers/transformationHelper.d.ts +1 -1
- package/dist/runtime/composables/localization/helpers/transformationHelper.js +15 -9
- package/dist/runtime/composables/localization/useEditorLocalizationKeys.d.ts +2 -1
- package/dist/runtime/composables/localization/useEditorLocalizationKeys.js +133 -85
- package/dist/runtime/composables/localization/useEditorLocalizationLocales.js +10 -7
- package/dist/runtime/composables/sdk.client.d.ts +1 -0
- package/dist/runtime/composables/sdk.client.js +1 -1
- package/package.json +2 -2
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, addImports } from '@nuxt/kit';
|
|
2
2
|
import { ofetch } from 'ofetch';
|
|
3
|
-
import { mkdir, writeFile } from 'fs/promises';
|
|
3
|
+
import { access, mkdir, writeFile } from 'fs/promises';
|
|
4
4
|
import { join } from 'path';
|
|
5
|
-
import {
|
|
5
|
+
import { constants } from 'fs';
|
|
6
6
|
|
|
7
7
|
const fetchAllTranslations = async (url, configId) => {
|
|
8
8
|
return await ofetch(
|
|
@@ -35,7 +35,9 @@ const module$1 = defineNuxtModule({
|
|
|
35
35
|
};
|
|
36
36
|
const overridesDir = resolver.resolve("./runtime/lang/overrides");
|
|
37
37
|
const allLocalesForOverride = [];
|
|
38
|
-
|
|
38
|
+
try {
|
|
39
|
+
await access(overridesDir, constants.F_OK);
|
|
40
|
+
} catch {
|
|
39
41
|
await mkdir(overridesDir, { recursive: true });
|
|
40
42
|
}
|
|
41
43
|
if (_options.apiEndpoint && _options.configId) {
|
|
@@ -51,7 +53,9 @@ const module$1 = defineNuxtModule({
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
nuxt.hook("i18n:registerModule", (register) => {
|
|
54
|
-
console.log(
|
|
56
|
+
console.log(
|
|
57
|
+
`[shop-core] Hook(i18n:registerModule) Registering language overrides for locales: "${allLocalesForOverride.join(", ")}"`
|
|
58
|
+
);
|
|
55
59
|
if (allLocalesForOverride.length > 0) {
|
|
56
60
|
register({
|
|
57
61
|
langDir: resolver.resolve("./runtime/lang/overrides"),
|
|
@@ -1,41 +1,69 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "nuxt/app";
|
|
2
2
|
import { useSdk } from "../../useSdk.js";
|
|
3
3
|
import { flattenTranslations } from "./transformationHelper.js";
|
|
4
|
-
import { createEmptyMessage } from "./messageHelper.js";
|
|
4
|
+
import { createEmptyMessage, cloneEmptyMessage } from "./messageHelper.js";
|
|
5
5
|
import { extractKeysWithValues } from "./extractKeysWithValuesHelper.js";
|
|
6
|
+
const processKeysForLocale = (keysWithValues, keysMap, locale, emptyMessageTemplate) => {
|
|
7
|
+
const keys = Object.keys(keysWithValues);
|
|
8
|
+
for (let i = 0; i < keys.length; i++) {
|
|
9
|
+
const key = keys[i];
|
|
10
|
+
if (key.startsWith("translated.")) continue;
|
|
11
|
+
if (!keysMap.has(key)) keysMap.set(key, cloneEmptyMessage(emptyMessageTemplate));
|
|
12
|
+
const translations = keysMap.get(key);
|
|
13
|
+
translations[locale] = keysWithValues[key];
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const processTranslatedKeysForLocale = (keysWithValues, keysMap, locale, emptyMessageTemplate) => {
|
|
17
|
+
const keys = Object.keys(keysWithValues);
|
|
18
|
+
for (let i = 0; i < keys.length; i++) {
|
|
19
|
+
const key = keys[i];
|
|
20
|
+
if (!keysMap.has(key)) keysMap.set(key, cloneEmptyMessage(emptyMessageTemplate));
|
|
21
|
+
const translations = keysMap.get(key);
|
|
22
|
+
if (translations[locale]) {
|
|
23
|
+
translations[locale].value = keysWithValues[key].value;
|
|
24
|
+
translations[locale].input = keysWithValues[key].input;
|
|
25
|
+
} else {
|
|
26
|
+
translations[locale] = keysWithValues[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const processBackendTranslations = (translations, keysMap, lang, emptyMessageTemplate) => {
|
|
31
|
+
const keys = Object.keys(translations);
|
|
32
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33
|
+
const key = keys[i];
|
|
34
|
+
const value = translations[key];
|
|
35
|
+
if (!keysMap.has(key)) keysMap.set(key, cloneEmptyMessage(emptyMessageTemplate));
|
|
36
|
+
const localeTranslations = keysMap.get(key);
|
|
37
|
+
if (localeTranslations[lang]) {
|
|
38
|
+
localeTranslations[lang].isDeployed = localeTranslations[lang].value === value;
|
|
39
|
+
localeTranslations[lang].value = value;
|
|
40
|
+
localeTranslations[lang].input = value;
|
|
41
|
+
} else {
|
|
42
|
+
localeTranslations[lang] = { value, input: value, isDeployed: false };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
6
46
|
export const loadDefaultKeys = async (keysMap, $i18n) => {
|
|
7
|
-
|
|
47
|
+
const locales = $i18n.availableLocales;
|
|
48
|
+
const emptyMessageTemplate = createEmptyMessage(locales);
|
|
49
|
+
for (let i = 0; i < locales.length; i++) {
|
|
50
|
+
const locale = locales[i];
|
|
8
51
|
await $i18n.loadLocaleMessages(locale);
|
|
9
52
|
const messages = $i18n.getLocaleMessage(locale);
|
|
10
53
|
const keysWithValues = extractKeysWithValues(messages);
|
|
11
|
-
|
|
12
|
-
if (key.startsWith("translated.")) continue;
|
|
13
|
-
if (!keysMap.has(key)) {
|
|
14
|
-
keysMap.set(key, createEmptyMessage($i18n.availableLocales));
|
|
15
|
-
}
|
|
16
|
-
const translations = keysMap.get(key);
|
|
17
|
-
translations[locale] = value;
|
|
18
|
-
}
|
|
54
|
+
processKeysForLocale(keysWithValues, keysMap, locale, emptyMessageTemplate);
|
|
19
55
|
}
|
|
20
56
|
};
|
|
21
57
|
export const loadTranslatedKeys = (keysMap, $i18n) => {
|
|
22
|
-
|
|
58
|
+
const locales = $i18n.availableLocales;
|
|
59
|
+
const emptyMessageTemplate = createEmptyMessage(locales);
|
|
60
|
+
for (let i = 0; i < locales.length; i++) {
|
|
61
|
+
const locale = locales[i];
|
|
23
62
|
const messages = $i18n.getLocaleMessage(locale);
|
|
24
63
|
const translatedMessages = messages["translated"];
|
|
25
64
|
if (!translatedMessages) continue;
|
|
26
65
|
const keysWithValues = extractKeysWithValues(translatedMessages);
|
|
27
|
-
|
|
28
|
-
if (!keysMap.has(key)) {
|
|
29
|
-
keysMap.set(key, createEmptyMessage($i18n.availableLocales));
|
|
30
|
-
}
|
|
31
|
-
const translations = keysMap.get(key);
|
|
32
|
-
if (translations[locale]) {
|
|
33
|
-
translations[locale].value = value.value;
|
|
34
|
-
translations[locale].input = value.input;
|
|
35
|
-
} else {
|
|
36
|
-
translations[locale] = value;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
66
|
+
processTranslatedKeysForLocale(keysWithValues, keysMap, locale, emptyMessageTemplate);
|
|
39
67
|
}
|
|
40
68
|
};
|
|
41
69
|
export const loadKeysFromBackend = async (keysMap, $i18n, backendLanguages) => {
|
|
@@ -45,25 +73,13 @@ export const loadKeysFromBackend = async (keysMap, $i18n, backendLanguages) => {
|
|
|
45
73
|
if (!configId) return;
|
|
46
74
|
const { data } = await useSdk().plentysystems.getAllTranslations({ configId });
|
|
47
75
|
const flattened = flattenTranslations(data);
|
|
48
|
-
|
|
76
|
+
const emptyMessageTemplate = createEmptyMessage($i18n.availableLocales);
|
|
77
|
+
const langs = Object.keys(flattened);
|
|
78
|
+
for (let i = 0; i < langs.length; i++) {
|
|
79
|
+
const lang = langs[i];
|
|
80
|
+
const translations = flattened[lang];
|
|
49
81
|
if (backendLanguages && Object.keys(translations).length > 0) backendLanguages.add(lang);
|
|
50
|
-
|
|
51
|
-
if (!keysMap.has(key)) {
|
|
52
|
-
keysMap.set(key, createEmptyMessage($i18n.availableLocales));
|
|
53
|
-
}
|
|
54
|
-
const localeTranslations = keysMap.get(key);
|
|
55
|
-
if (localeTranslations[lang]) {
|
|
56
|
-
localeTranslations[lang].isDeployed = localeTranslations[lang].value === value;
|
|
57
|
-
localeTranslations[lang].value = value;
|
|
58
|
-
localeTranslations[lang].input = value;
|
|
59
|
-
} else {
|
|
60
|
-
localeTranslations[lang] = {
|
|
61
|
-
value,
|
|
62
|
-
input: value,
|
|
63
|
-
isDeployed: false
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
82
|
+
processBackendTranslations(translations, keysMap, lang, emptyMessageTemplate);
|
|
67
83
|
}
|
|
68
84
|
} catch {
|
|
69
85
|
}
|
|
@@ -8,4 +8,14 @@ export declare const isCompiledMessage: (value: unknown) => boolean;
|
|
|
8
8
|
* Create an empty message object for all locales
|
|
9
9
|
* @param availableLocales
|
|
10
10
|
*/
|
|
11
|
-
export declare const createEmptyMessage: (availableLocales: string[]) => {
|
|
11
|
+
export declare const createEmptyMessage: (availableLocales: string[]) => Record<string, {
|
|
12
|
+
value: string;
|
|
13
|
+
input: string;
|
|
14
|
+
isDeployed: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Clone an empty message template with all locale properties
|
|
18
|
+
* More efficient than creating a new message for each key
|
|
19
|
+
* @param template - The template message object to clone
|
|
20
|
+
*/
|
|
21
|
+
export declare const cloneEmptyMessage: (template: Record<string, any>) => Record<string, any>;
|
|
@@ -2,16 +2,25 @@ export const isCompiledMessage = (value) => {
|
|
|
2
2
|
return typeof value === "object" && value !== null && ("loc" in value && typeof value.loc === "object" && value.loc !== null && "source" in value.loc && typeof value.loc.source === "string" || "b" in value && typeof value.b === "object" && value.b !== null && "s" in value.b && typeof value.b.s === "string");
|
|
3
3
|
};
|
|
4
4
|
export const createEmptyMessage = (availableLocales) => {
|
|
5
|
-
|
|
5
|
+
const data = {};
|
|
6
6
|
for (const locale of availableLocales) {
|
|
7
|
-
data = {
|
|
8
|
-
...data,
|
|
9
|
-
[locale]: {
|
|
10
|
-
value: "",
|
|
11
|
-
input: "",
|
|
12
|
-
isDeployed: true
|
|
13
|
-
}
|
|
14
|
-
};
|
|
7
|
+
data[locale] = { value: "", input: "", isDeployed: true };
|
|
15
8
|
}
|
|
16
9
|
return data;
|
|
17
10
|
};
|
|
11
|
+
export const cloneEmptyMessage = (template) => {
|
|
12
|
+
const clone = {};
|
|
13
|
+
const locales = Object.keys(template);
|
|
14
|
+
const length = locales.length;
|
|
15
|
+
for (let i = 0; i < length; i++) {
|
|
16
|
+
const locale = locales[i];
|
|
17
|
+
const source = template[locale];
|
|
18
|
+
clone[locale] = {
|
|
19
|
+
value: source.value,
|
|
20
|
+
input: source.input,
|
|
21
|
+
isDeployed: source.isDeployed,
|
|
22
|
+
...source.default !== void 0 && { default: source.default }
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return clone;
|
|
26
|
+
};
|
|
@@ -17,18 +17,24 @@ export const flattenTranslations = (data) => {
|
|
|
17
17
|
}
|
|
18
18
|
return result;
|
|
19
19
|
};
|
|
20
|
+
const navigateAndCreatePath = (root, parts) => {
|
|
21
|
+
let current = root;
|
|
22
|
+
const lastIndex = parts.length - 1;
|
|
23
|
+
for (let i = 0; i < lastIndex; i++) {
|
|
24
|
+
const part = parts[i];
|
|
25
|
+
if (!current[part]) current[part] = {};
|
|
26
|
+
current = current[part];
|
|
27
|
+
}
|
|
28
|
+
return current;
|
|
29
|
+
};
|
|
20
30
|
export const unflattenTranslations = (flat) => {
|
|
21
31
|
const result = {};
|
|
22
|
-
|
|
32
|
+
const entries = Object.entries(flat);
|
|
33
|
+
for (let i = 0; i < entries.length; i++) {
|
|
34
|
+
const [key, value] = entries[i];
|
|
23
35
|
const parts = key.split(".");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const part = parts[i];
|
|
27
|
-
if (!current[part]) current[part] = {};
|
|
28
|
-
current = current[part];
|
|
29
|
-
}
|
|
30
|
-
const lastPart = parts[parts.length - 1];
|
|
31
|
-
current[lastPart] = value;
|
|
36
|
+
const target = navigateAndCreatePath(result, parts);
|
|
37
|
+
target[parts[parts.length - 1]] = value;
|
|
32
38
|
}
|
|
33
39
|
return result;
|
|
34
40
|
};
|
|
@@ -4,7 +4,7 @@ export declare const useEditorLocalizationKeys: () => {
|
|
|
4
4
|
checkHasUnsavedChanges: () => boolean;
|
|
5
5
|
collectChangedTranslations: () => Record<string, Record<string, string>>;
|
|
6
6
|
loadKeys: () => Promise<void>;
|
|
7
|
-
filterKeys: (searchTerm: string, activeLocales: string[]) => void;
|
|
7
|
+
filterKeys: (searchTerm: string, activeLocales: string[], showOnlyMissing?: boolean) => void;
|
|
8
8
|
getTranslatedCount: (locale: string) => {
|
|
9
9
|
translated: number;
|
|
10
10
|
undeployed: number;
|
|
@@ -14,6 +14,7 @@ export declare const useEditorLocalizationKeys: () => {
|
|
|
14
14
|
getKeyFromFullKey: (key: string) => string;
|
|
15
15
|
saveLocalizations: () => Promise<void>;
|
|
16
16
|
keys: import("vue").Ref<MultilingualKeysState[], MultilingualKeysState[]>;
|
|
17
|
+
keysMap: import("vue").Ref<Map<string, MultilingualKeysState>, Map<string, MultilingualKeysState>>;
|
|
17
18
|
filteredKeys: import("vue").Ref<MultilingualKeysState[] | null, MultilingualKeysState[] | null>;
|
|
18
19
|
drawerOpen: import("vue").Ref<boolean, boolean>;
|
|
19
20
|
hasChanges: import("vue").Ref<boolean, boolean>;
|
|
@@ -4,9 +4,114 @@ import { loadDefaultKeys, loadTranslatedKeys, loadKeysFromBackend } from "./help
|
|
|
4
4
|
import { useSdk, useNotification, useEditorLocalizationLocales } from "#imports";
|
|
5
5
|
import { allLanguages } from "./useEditorLocalizationLocales.js";
|
|
6
6
|
import { unflattenTranslations } from "./helpers/transformationHelper.js";
|
|
7
|
+
const containsSearchTerm = (text, searchQuery) => {
|
|
8
|
+
return text.toLowerCase().indexOf(searchQuery) !== -1;
|
|
9
|
+
};
|
|
10
|
+
const hasMatchingTranslation = (translations, searchQuery, activeLocalesSet) => {
|
|
11
|
+
for (const locale of activeLocalesSet) {
|
|
12
|
+
const translation = translations[locale];
|
|
13
|
+
if (translation && containsSearchTerm(translation.input, searchQuery)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
};
|
|
19
|
+
const matchesSearchQuery = (keyEntry, searchQuery, activeLocalesSet) => {
|
|
20
|
+
if (containsSearchTerm(keyEntry.key, searchQuery)) return true;
|
|
21
|
+
return hasMatchingTranslation(keyEntry.translations, searchQuery, activeLocalesSet);
|
|
22
|
+
};
|
|
23
|
+
const hasMissingTranslations = (keyEntry, activeLocalesSet) => {
|
|
24
|
+
for (const locale of activeLocalesSet) {
|
|
25
|
+
const translation = keyEntry.translations[locale];
|
|
26
|
+
if (!translation || !translation.value || translation.value.trim().length === 0) return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
};
|
|
30
|
+
const processKeyChanges = (keyState, selectedLocalesSet, translationsByLocale) => {
|
|
31
|
+
for (const locale in keyState.translations) {
|
|
32
|
+
if (!selectedLocalesSet.has(locale)) continue;
|
|
33
|
+
const translation = keyState.translations[locale];
|
|
34
|
+
if (translation && translation.input !== translation.value) {
|
|
35
|
+
if (!translationsByLocale[locale]) translationsByLocale[locale] = {};
|
|
36
|
+
translationsByLocale[locale][keyState.key] = translation.input;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const keyHasUnsavedChanges = (keyState, selectedLocalesSet) => {
|
|
41
|
+
for (const locale in keyState.translations) {
|
|
42
|
+
if (!selectedLocalesSet.has(locale)) continue;
|
|
43
|
+
const translation = keyState.translations[locale];
|
|
44
|
+
if (translation && translation.input !== translation.value) return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
const collectChanges = (keys, selectedLocalesSet) => {
|
|
49
|
+
const translationsByLocale = {};
|
|
50
|
+
for (let i = 0; i < keys.length; i++) {
|
|
51
|
+
processKeyChanges(keys[i], selectedLocalesSet, translationsByLocale);
|
|
52
|
+
}
|
|
53
|
+
return translationsByLocale;
|
|
54
|
+
};
|
|
55
|
+
const hasUnsavedChanges = (keys, selectedLocalesSet) => {
|
|
56
|
+
for (let i = 0; i < keys.length; i++) {
|
|
57
|
+
if (keyHasUnsavedChanges(keys[i], selectedLocalesSet)) return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
};
|
|
61
|
+
const saveLanguageTranslations = async (language, configId, translations, backendLanguages) => {
|
|
62
|
+
try {
|
|
63
|
+
const nestedTranslations = unflattenTranslations(translations);
|
|
64
|
+
const languageExistsInBackend = backendLanguages.has(language);
|
|
65
|
+
const sdk = useSdk().plentysystems;
|
|
66
|
+
const saveMethod = languageExistsInBackend ? sdk.patchMergeTranslations : sdk.doCreateTranslations;
|
|
67
|
+
await saveMethod({ configId, language, translations: nestedTranslations });
|
|
68
|
+
if (!languageExistsInBackend) backendLanguages.add(language);
|
|
69
|
+
return { language, success: true };
|
|
70
|
+
} catch (error) {
|
|
71
|
+
return { language, success: false, error };
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const updateLocalStateAfterSave = (keys, language, savedTranslations) => {
|
|
75
|
+
for (let i = 0; i < keys.length; i++) {
|
|
76
|
+
const keyState = keys[i];
|
|
77
|
+
const translation = keyState.translations[language];
|
|
78
|
+
if (translation && savedTranslations[keyState.key] !== void 0) {
|
|
79
|
+
translation.isDeployed = true;
|
|
80
|
+
translation.value = translation.input;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const notifySaveResults = (results) => {
|
|
85
|
+
const { send } = useNotification();
|
|
86
|
+
const successful = [];
|
|
87
|
+
const failed = [];
|
|
88
|
+
for (let i = 0; i < results.length; i++) {
|
|
89
|
+
const result = results[i];
|
|
90
|
+
const displayName = allLanguages[result.language] || result.language.toUpperCase();
|
|
91
|
+
if (result.success) {
|
|
92
|
+
successful.push(displayName);
|
|
93
|
+
} else {
|
|
94
|
+
failed.push(displayName);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (successful.length > 0) {
|
|
98
|
+
send({
|
|
99
|
+
message: `Translations for ${successful.join(", ")} saved successfully`,
|
|
100
|
+
type: "positive"
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (failed.length > 0) {
|
|
104
|
+
send({
|
|
105
|
+
message: `Failed to save translations for ${failed.join(", ")}`,
|
|
106
|
+
type: "negative",
|
|
107
|
+
persist: true
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
7
111
|
export const useEditorLocalizationKeys = () => {
|
|
8
112
|
const state = useState("useEditorLocalizationKeys", () => ({
|
|
9
113
|
keys: [],
|
|
114
|
+
keysMap: /* @__PURE__ */ new Map(),
|
|
10
115
|
filteredKeys: null,
|
|
11
116
|
drawerOpen: false,
|
|
12
117
|
hasChanges: false,
|
|
@@ -27,36 +132,40 @@ export const useEditorLocalizationKeys = () => {
|
|
|
27
132
|
key,
|
|
28
133
|
translations
|
|
29
134
|
})).sort((a, b) => a.key.localeCompare(b.key));
|
|
135
|
+
state.value.keysMap.clear();
|
|
136
|
+
for (let i = 0; i < state.value.keys.length; i++) {
|
|
137
|
+
const keyState = state.value.keys[i];
|
|
138
|
+
state.value.keysMap.set(keyState.key, keyState);
|
|
139
|
+
}
|
|
30
140
|
state.value.loading = false;
|
|
31
141
|
state.value.drawerOpen = true;
|
|
32
142
|
};
|
|
33
|
-
const filterKeys = (searchTerm, activeLocales) => {
|
|
34
|
-
if (searchTerm.trim().length === 0) {
|
|
143
|
+
const filterKeys = (searchTerm, activeLocales, showOnlyMissing = false) => {
|
|
144
|
+
if (searchTerm.trim().length === 0 && !showOnlyMissing) {
|
|
35
145
|
state.value.filteredKeys = null;
|
|
36
146
|
return;
|
|
37
147
|
}
|
|
38
148
|
const searchQuery = searchTerm.trim().toLowerCase();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
149
|
+
const activeLocalesSet = new Set(activeLocales);
|
|
150
|
+
const filtered = [];
|
|
151
|
+
const hasSearchTerm = searchQuery.length > 0;
|
|
152
|
+
for (let i = 0; i < state.value.keys.length; i++) {
|
|
153
|
+
const keyEntry = state.value.keys[i];
|
|
154
|
+
const matchesSearch = !hasSearchTerm || matchesSearchQuery(keyEntry, searchQuery, activeLocalesSet);
|
|
155
|
+
const matchesMissing = !showOnlyMissing || hasMissingTranslations(keyEntry, activeLocalesSet);
|
|
156
|
+
if (matchesSearch && matchesMissing) filtered.push(keyEntry);
|
|
157
|
+
}
|
|
158
|
+
state.value.filteredKeys = filtered;
|
|
49
159
|
};
|
|
50
160
|
const getTranslatedCount = (locale) => {
|
|
51
161
|
let translatedCount = 0;
|
|
52
162
|
let undeployedCount = 0;
|
|
53
|
-
|
|
54
|
-
for (
|
|
55
|
-
|
|
163
|
+
const totalCount = state.value.keys.length;
|
|
164
|
+
for (let i = 0; i < totalCount; i++) {
|
|
165
|
+
const translation = state.value.keys[i].translations[locale];
|
|
166
|
+
if (translation && translation.value.length > 0) {
|
|
56
167
|
translatedCount++;
|
|
57
|
-
if (!
|
|
58
|
-
undeployedCount++;
|
|
59
|
-
}
|
|
168
|
+
if (!translation.isDeployed) undeployedCount++;
|
|
60
169
|
}
|
|
61
170
|
}
|
|
62
171
|
return {
|
|
@@ -77,38 +186,17 @@ export const useEditorLocalizationKeys = () => {
|
|
|
77
186
|
const collectChangedTranslations = () => {
|
|
78
187
|
const { selectedLocales } = useEditorLocalizationLocales();
|
|
79
188
|
if (!selectedLocales.value || selectedLocales.value.length === 0) return {};
|
|
80
|
-
const translationsByLocale = {};
|
|
81
189
|
const selectedLocalesSet = new Set(selectedLocales.value);
|
|
82
|
-
|
|
83
|
-
const keyState = state.value.keys[i];
|
|
84
|
-
for (const locale in keyState.translations) {
|
|
85
|
-
if (!selectedLocalesSet.has(locale)) continue;
|
|
86
|
-
const translation = keyState.translations[locale];
|
|
87
|
-
if (translation && translation.input !== translation.value) {
|
|
88
|
-
if (!translationsByLocale[locale]) translationsByLocale[locale] = {};
|
|
89
|
-
translationsByLocale[locale][keyState.key] = translation.input;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return translationsByLocale;
|
|
190
|
+
return collectChanges(state.value.keys, selectedLocalesSet);
|
|
94
191
|
};
|
|
95
192
|
const checkHasUnsavedChanges = () => {
|
|
96
193
|
const { selectedLocales } = useEditorLocalizationLocales();
|
|
97
194
|
if (!selectedLocales.value || selectedLocales.value.length === 0) return false;
|
|
98
195
|
const selectedLocalesSet = new Set(selectedLocales.value);
|
|
99
|
-
|
|
100
|
-
for (const locale in keyState.translations) {
|
|
101
|
-
if (!selectedLocalesSet.has(locale)) continue;
|
|
102
|
-
const translation = keyState.translations[locale];
|
|
103
|
-
if (translation && translation.input !== translation.value) {
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
196
|
+
return hasUnsavedChanges(state.value.keys, selectedLocalesSet);
|
|
109
197
|
};
|
|
110
198
|
const updateTranslationInput = (key, locale, newInput) => {
|
|
111
|
-
const keyState = state.value.
|
|
199
|
+
const keyState = state.value.keysMap.get(key);
|
|
112
200
|
if (!keyState || !keyState.translations[locale]) return;
|
|
113
201
|
const translation = keyState.translations[locale];
|
|
114
202
|
translation.input = newInput;
|
|
@@ -118,47 +206,6 @@ export const useEditorLocalizationKeys = () => {
|
|
|
118
206
|
state.value.hasChanges = checkHasUnsavedChanges();
|
|
119
207
|
}
|
|
120
208
|
};
|
|
121
|
-
const saveLanguageTranslations = async (language, configId, translations) => {
|
|
122
|
-
try {
|
|
123
|
-
const nestedTranslations = unflattenTranslations(translations);
|
|
124
|
-
const languageExistsInBackend = state.value.backendLanguages.has(language);
|
|
125
|
-
const sdk = useSdk().plentysystems;
|
|
126
|
-
const saveMethod = languageExistsInBackend ? sdk.patchMergeTranslations : sdk.doCreateTranslations;
|
|
127
|
-
await saveMethod({ configId, language, translations: nestedTranslations });
|
|
128
|
-
if (!languageExistsInBackend) state.value.backendLanguages.add(language);
|
|
129
|
-
return { language, success: true };
|
|
130
|
-
} catch (error) {
|
|
131
|
-
return { language, success: false, error };
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
const updateLocalStateAfterSave = (language, savedTranslations) => {
|
|
135
|
-
for (let i = 0; i < state.value.keys.length; i++) {
|
|
136
|
-
const keyState = state.value.keys[i];
|
|
137
|
-
const translation = keyState.translations[language];
|
|
138
|
-
if (translation && savedTranslations[keyState.key] !== void 0) {
|
|
139
|
-
translation.isDeployed = true;
|
|
140
|
-
translation.value = translation.input;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
const notifySaveResults = (results) => {
|
|
145
|
-
const { send } = useNotification();
|
|
146
|
-
const successful = results.filter((r) => r.success).map((r) => allLanguages[r.language] || r.language.toUpperCase());
|
|
147
|
-
const failed = results.filter((r) => !r.success).map((r) => allLanguages[r.language] || r.language.toUpperCase());
|
|
148
|
-
if (successful.length > 0) {
|
|
149
|
-
send({
|
|
150
|
-
message: `Translations for ${successful.join(", ")} saved successfully`,
|
|
151
|
-
type: "positive"
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
if (failed.length > 0) {
|
|
155
|
-
send({
|
|
156
|
-
message: `Failed to save translations for ${failed.join(", ")}`,
|
|
157
|
-
type: "negative",
|
|
158
|
-
persist: true
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
209
|
const saveLocalizations = async () => {
|
|
163
210
|
const config = useRuntimeConfig().public.shopCore;
|
|
164
211
|
const configId = config?.configId;
|
|
@@ -169,11 +216,12 @@ export const useEditorLocalizationKeys = () => {
|
|
|
169
216
|
state.value.loading = true;
|
|
170
217
|
try {
|
|
171
218
|
const savePromises = localesToSave.map(
|
|
172
|
-
(language) => saveLanguageTranslations(language, configId, translationsByLocale[language])
|
|
219
|
+
(language) => saveLanguageTranslations(language, configId, translationsByLocale[language], state.value.backendLanguages)
|
|
173
220
|
);
|
|
174
221
|
const results = await Promise.all(savePromises);
|
|
175
222
|
results.forEach((result) => {
|
|
176
|
-
if (result.success)
|
|
223
|
+
if (result.success)
|
|
224
|
+
updateLocalStateAfterSave(state.value.keys, result.language, translationsByLocale[result.language]);
|
|
177
225
|
});
|
|
178
226
|
notifySaveResults(results);
|
|
179
227
|
state.value.hasChanges = false;
|
|
@@ -42,22 +42,25 @@ export const useEditorLocalizationLocales = () => {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
const removeLocale = (locale) => {
|
|
45
|
-
|
|
45
|
+
const index = state.value.selectedLocales.indexOf(locale);
|
|
46
|
+
if (index > -1) state.value.selectedLocales.splice(index, 1);
|
|
46
47
|
saveLocales();
|
|
47
48
|
};
|
|
48
49
|
const toggleLocale = (locale) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} else {
|
|
52
|
-
selectLocale(locale);
|
|
53
|
-
}
|
|
50
|
+
const index = state.value.selectedLocales.indexOf(locale);
|
|
51
|
+
index > -1 ? state.value.selectedLocales.splice(index, 1) : state.value.selectedLocales.push(locale);
|
|
54
52
|
saveLocales();
|
|
55
53
|
};
|
|
56
54
|
const initializeLocales = () => {
|
|
57
55
|
const selectedLocales = localStorage.getItem("editorLocalizationSelectedLocales");
|
|
58
56
|
if (selectedLocales) {
|
|
59
57
|
const parsedLocales = JSON.parse(selectedLocales);
|
|
60
|
-
|
|
58
|
+
const validLanguages = new Set(Object.keys(allLanguages));
|
|
59
|
+
const validLocales = [];
|
|
60
|
+
for (let i = 0; i < parsedLocales.length; i++) {
|
|
61
|
+
if (validLanguages.has(parsedLocales[i])) validLocales.push(parsedLocales[i]);
|
|
62
|
+
}
|
|
63
|
+
state.value.selectedLocales = validLocales;
|
|
61
64
|
} else {
|
|
62
65
|
state.value.selectedLocales = ["de", "en"];
|
|
63
66
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { ApiError } from "@plentymarkets/shop-api";
|
|
3
|
-
const handleHttpError = (error) => {
|
|
3
|
+
export const handleHttpError = (error) => {
|
|
4
4
|
const axiosError = error;
|
|
5
5
|
const data = axiosError?.response?.data?.data || axiosError?.response?.data;
|
|
6
6
|
const events = axiosError?.response?.data?.events;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plentymarkets/shop-core",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.4",
|
|
4
4
|
"description": "Core module for PlentyONE Shop",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"test:types": "vue-tsc --noEmit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@plentymarkets/shop-api": "^0.
|
|
48
|
+
"@plentymarkets/shop-api": "^0.149.0",
|
|
49
49
|
"@vue-storefront/sdk": "^3.4.1",
|
|
50
50
|
"cookie": "^1.0.2",
|
|
51
51
|
"js-sha256": "^0.11.0",
|