@roxxel/payload-multilang 0.0.5 → 0.0.6
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 +16 -3
- package/dist/components/LanguageListToolbar.js +4 -3
- package/dist/components/LanguageListToolbar.js.map +1 -1
- package/dist/components/LanguageMetabox.js +18 -17
- package/dist/components/LanguageMetabox.js.map +1 -1
- package/dist/components/TranslationActionsClient.js +16 -14
- package/dist/components/TranslationActionsClient.js.map +1 -1
- package/dist/components/TranslationColumnCell.js +4 -1
- package/dist/components/TranslationColumnCell.js.map +1 -1
- package/dist/components/TranslationColumnCellClient.js +16 -7
- package/dist/components/TranslationColumnCellClient.js.map +1 -1
- package/dist/components/TranslationsTab.js +9 -8
- package/dist/components/TranslationsTab.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/endpoints/translations.js +4 -6
- package/dist/endpoints/translations.js.map +1 -1
- package/dist/hooks/translatedCollection.d.ts +2 -1
- package/dist/hooks/translatedCollection.js +122 -15
- package/dist/hooks/translatedCollection.js.map +1 -1
- package/dist/hooks/translatedGlobal.js +5 -1
- package/dist/hooks/translatedGlobal.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/config.js +2 -10
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/data.d.ts +1 -2
- package/dist/lib/data.js +32 -4
- package/dist/lib/data.js.map +1 -1
- package/dist/translations.d.ts +72 -0
- package/dist/translations.js +72 -0
- package/dist/translations.js.map +1 -0
- package/dist/types.d.ts +0 -19
- package/dist/types.js.map +1 -1
- package/docs/configuration.md +43 -9
- package/docs/usage.md +10 -34
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TranslationColumnCellClient.tsx"],"sourcesContent":["'use client'\n\nimport { toast, useConfig, useRouteTransition } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation.js'\nimport { formatAdminURL } from 'payload/shared'\nimport { useState } from 'react'\n\nimport '../styles/admin.css'\n\ntype Props = {\n collectionSlug: string\n current?: boolean\n flagLabel?: string\n sourceID?: number | string\n targetLanguage: string\n targetLanguageName: string\n translationID?: number | string\n}\n\nconst PenIcon = () => (\n <svg\n aria-hidden=\"true\"\n className=\"payload-multilang-pen-icon\"\n fill=\"none\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n width=\"16\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M9.9 2.6 13.4 6 5.7 13.7 2.2 14.4 2.9 10.9 9.9 2.6Z\"\n stroke=\"currentColor\"\n strokeLinejoin=\"round\"\n strokeWidth=\"1.4\"\n />\n <path\n d=\"M8.8 3.9 12.1 7.2\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth=\"1.4\"\n />\n </svg>\n)\n\nexport const TranslationColumnCellClient = ({\n collectionSlug,\n current,\n flagLabel,\n sourceID,\n targetLanguage,\n targetLanguageName,\n translationID,\n}: Props) => {\n const { config } = useConfig()\n const router = useRouter()\n const { isTransitioning, startRouteTransition } = useRouteTransition()\n const [isBusy, setIsBusy] = useState(false)\n\n const documentURL = (documentID: number | string) =>\n formatAdminURL({\n adminRoute: config.routes.admin,\n path: `/collections/${collectionSlug}/${documentID}`,\n })\n\n const navigateToTranslation = (documentID: number | string) => {\n startRouteTransition(() => {\n router.push(documentURL(documentID))\n })\n }\n\n const createTranslation = async () => {\n if (!sourceID) {\n toast.error('
|
|
1
|
+
{"version":3,"sources":["../../src/components/TranslationColumnCellClient.tsx"],"sourcesContent":["'use client'\n\nimport { toast, useConfig, useRouteTransition, useTranslation } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation.js'\nimport { formatAdminURL } from 'payload/shared'\nimport { useState } from 'react'\n\nimport '../styles/admin.css'\nimport type {\n PayloadMultilangTranslationKey,\n PayloadMultilangTranslations,\n} from '../translations.js'\n\ntype Props = {\n collectionSlug: string\n current?: boolean\n flagLabel?: string\n sourceID?: number | string\n targetLanguage: string\n targetLanguageName: string\n translationID?: number | string\n}\n\nconst PenIcon = () => (\n <svg\n aria-hidden=\"true\"\n className=\"payload-multilang-pen-icon\"\n fill=\"none\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n width=\"16\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M9.9 2.6 13.4 6 5.7 13.7 2.2 14.4 2.9 10.9 9.9 2.6Z\"\n stroke=\"currentColor\"\n strokeLinejoin=\"round\"\n strokeWidth=\"1.4\"\n />\n <path\n d=\"M8.8 3.9 12.1 7.2\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth=\"1.4\"\n />\n </svg>\n)\n\nexport const TranslationColumnCellClient = ({\n collectionSlug,\n current,\n flagLabel,\n sourceID,\n targetLanguage,\n targetLanguageName,\n translationID,\n}: Props) => {\n const { config } = useConfig()\n const router = useRouter()\n const { isTransitioning, startRouteTransition } = useRouteTransition()\n const { t } = useTranslation<\n PayloadMultilangTranslations,\n PayloadMultilangTranslationKey\n >()\n const [isBusy, setIsBusy] = useState(false)\n\n const documentURL = (documentID: number | string) =>\n formatAdminURL({\n adminRoute: config.routes.admin,\n path: `/collections/${collectionSlug}/${documentID}`,\n })\n\n const navigateToTranslation = (documentID: number | string) => {\n startRouteTransition(() => {\n router.push(documentURL(documentID))\n })\n }\n\n const createTranslation = async () => {\n if (!sourceID) {\n toast.error(t('payloadMultilang:saveBeforeCreatingTranslations'))\n return\n }\n\n setIsBusy(true)\n\n const response = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: `/${collectionSlug}/multilang/create`,\n }),\n {\n body: JSON.stringify({\n sourceId: sourceID,\n targetLanguage,\n }),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n },\n )\n\n setIsBusy(false)\n\n if (!response.ok) {\n const result = (await response.json().catch(() => undefined)) as\n | { message?: string }\n | undefined\n toast.error(result?.message || t('payloadMultilang:translationCouldNotBeCreated'))\n return\n }\n\n const result = (await response.json()) as {\n doc?: { id?: number | string }\n }\n\n if (result.doc?.id) {\n navigateToTranslation(result.doc.id)\n }\n }\n\n if (current) {\n return (\n <span\n className=\"payload-multilang-flag-cell payload-multilang-flag-cell--current\"\n title={targetLanguageName}\n >\n {flagLabel || targetLanguage.toUpperCase()}\n </span>\n )\n }\n\n if (translationID) {\n return (\n <a\n aria-label={t('payloadMultilang:editTranslationFor', {\n language: targetLanguageName,\n })}\n className=\"payload-multilang-cell-action\"\n href={documentURL(translationID)}\n onClick={(event) => {\n event.preventDefault()\n navigateToTranslation(translationID)\n }}\n title={t('payloadMultilang:editTranslationFor', {\n language: targetLanguageName,\n })}\n >\n <PenIcon />\n </a>\n )\n }\n\n return (\n <button\n aria-label={t('payloadMultilang:createTranslationFor', {\n language: targetLanguageName,\n })}\n className=\"payload-multilang-icon-button\"\n disabled={!sourceID || isBusy || isTransitioning}\n onClick={() => void createTranslation()}\n title={t('payloadMultilang:createTranslationFor', {\n language: targetLanguageName,\n })}\n type=\"button\"\n >\n +\n </button>\n )\n}\n"],"names":["toast","useConfig","useRouteTransition","useTranslation","useRouter","formatAdminURL","useState","PenIcon","svg","aria-hidden","className","fill","height","viewBox","width","xmlns","path","d","stroke","strokeLinejoin","strokeWidth","strokeLinecap","TranslationColumnCellClient","collectionSlug","current","flagLabel","sourceID","targetLanguage","targetLanguageName","translationID","config","router","isTransitioning","startRouteTransition","t","isBusy","setIsBusy","documentURL","documentID","adminRoute","routes","admin","navigateToTranslation","push","createTranslation","error","response","fetch","apiRoute","api","body","JSON","stringify","sourceId","credentials","headers","method","ok","result","json","catch","undefined","message","doc","id","span","title","toUpperCase","a","aria-label","language","href","onClick","event","preventDefault","button","disabled","type"],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ const getLanguages = (props)=>{
|
|
|
14
14
|
const languages = getPayloadMultilangCustom(props.initPageResult.collectionConfig?.admin?.custom).languages;
|
|
15
15
|
return Array.isArray(languages) ? languages : [];
|
|
16
16
|
};
|
|
17
|
+
const translate = (props, key, options)=>props.i18n?.t ? props.i18n.t(key, options) : key;
|
|
17
18
|
export const TranslationsTab = async (props)=>{
|
|
18
19
|
const collectionSlug = props.initPageResult.collectionConfig?.slug;
|
|
19
20
|
const translationsSegmentIndex = props.routeSegments.lastIndexOf('translations');
|
|
@@ -24,10 +25,10 @@ export const TranslationsTab = async (props)=>{
|
|
|
24
25
|
className: "payload-multilang-page",
|
|
25
26
|
children: [
|
|
26
27
|
/*#__PURE__*/ _jsx("h1", {
|
|
27
|
-
children:
|
|
28
|
+
children: translate(props, 'payloadMultilang:translations')
|
|
28
29
|
}),
|
|
29
30
|
/*#__PURE__*/ _jsx("p", {
|
|
30
|
-
children:
|
|
31
|
+
children: translate(props, 'payloadMultilang:saveBeforeManagingTranslations')
|
|
31
32
|
})
|
|
32
33
|
]
|
|
33
34
|
});
|
|
@@ -49,10 +50,10 @@ export const TranslationsTab = async (props)=>{
|
|
|
49
50
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
50
51
|
children: [
|
|
51
52
|
/*#__PURE__*/ _jsx("h1", {
|
|
52
|
-
children:
|
|
53
|
+
children: translate(props, 'payloadMultilang:translations')
|
|
53
54
|
}),
|
|
54
55
|
/*#__PURE__*/ _jsx("p", {
|
|
55
|
-
children:
|
|
56
|
+
children: translate(props, 'payloadMultilang:createOrConnectDocuments')
|
|
56
57
|
})
|
|
57
58
|
]
|
|
58
59
|
})
|
|
@@ -64,7 +65,7 @@ export const TranslationsTab = async (props)=>{
|
|
|
64
65
|
className: "payload-multilang-panel payload-multilang-panel--wide",
|
|
65
66
|
children: [
|
|
66
67
|
/*#__PURE__*/ _jsx("h2", {
|
|
67
|
-
children:
|
|
68
|
+
children: translate(props, 'payloadMultilang:linkedDocuments')
|
|
68
69
|
}),
|
|
69
70
|
/*#__PURE__*/ _jsxs("table", {
|
|
70
71
|
className: "payload-multilang-table",
|
|
@@ -73,10 +74,10 @@ export const TranslationsTab = async (props)=>{
|
|
|
73
74
|
children: /*#__PURE__*/ _jsxs("tr", {
|
|
74
75
|
children: [
|
|
75
76
|
/*#__PURE__*/ _jsx("th", {
|
|
76
|
-
children:
|
|
77
|
+
children: translate(props, 'payloadMultilang:language')
|
|
77
78
|
}),
|
|
78
79
|
/*#__PURE__*/ _jsx("th", {
|
|
79
|
-
children:
|
|
80
|
+
children: translate(props, 'payloadMultilang:documentID')
|
|
80
81
|
})
|
|
81
82
|
]
|
|
82
83
|
})
|
|
@@ -93,7 +94,7 @@ export const TranslationsTab = async (props)=>{
|
|
|
93
94
|
]
|
|
94
95
|
}),
|
|
95
96
|
/*#__PURE__*/ _jsx("td", {
|
|
96
|
-
children: translation?.id ? String(translation.id) : '
|
|
97
|
+
children: translation?.id ? String(translation.id) : translate(props, 'payloadMultilang:missing')
|
|
97
98
|
})
|
|
98
99
|
]
|
|
99
100
|
}, language.code);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TranslationsTab.tsx"],"sourcesContent":["import type { DocumentViewServerProps } from 'payload'\n\nimport type { MultilangFieldNames, MultilangLanguage } from '../types.js'\n\nimport { DEFAULT_FIELD_NAMES } from '../constants.js'\nimport {\n asDocument,\n getDocumentTranslationsWithPayload,\n getID,\n} from '../lib/data.js'\nimport { getPayloadMultilangCustom } from './config.js'\nimport { TranslationActionsClient } from './TranslationActionsClient.js'\n\nconst getFieldNames = (\n props: DocumentViewServerProps,\n): MultilangFieldNames => {\n const custom = props.initPageResult.collectionConfig?.admin?.custom as\n | { payloadMultilang?: { fieldNames?: Partial<MultilangFieldNames> } }\n | undefined\n\n return {\n ...DEFAULT_FIELD_NAMES,\n ...(custom?.payloadMultilang?.fieldNames || {}),\n }\n}\n\nconst getLanguages = (props: DocumentViewServerProps): MultilangLanguage[] => {\n const languages = getPayloadMultilangCustom(\n props.initPageResult.collectionConfig?.admin?.custom,\n ).languages\n\n return Array.isArray(languages) ? languages : []\n}\n\nexport const TranslationsTab = async (props: DocumentViewServerProps) => {\n const collectionSlug = props.initPageResult.collectionConfig?.slug\n const translationsSegmentIndex = props.routeSegments.lastIndexOf('translations')\n const routeDocID =\n translationsSegmentIndex > 0 ? props.routeSegments[translationsSegmentIndex - 1] : undefined\n const docID = getID(asDocument(props.doc).id || routeDocID || props.id)\n\n if (!collectionSlug || !docID) {\n return (\n <section className=\"payload-multilang-page\">\n <h1>
|
|
1
|
+
{"version":3,"sources":["../../src/components/TranslationsTab.tsx"],"sourcesContent":["import type { DocumentViewServerProps } from 'payload'\n\nimport type { PayloadMultilangTranslationKey } from '../translations.js'\nimport type { MultilangFieldNames, MultilangLanguage } from '../types.js'\n\nimport { DEFAULT_FIELD_NAMES } from '../constants.js'\nimport {\n asDocument,\n getDocumentTranslationsWithPayload,\n getID,\n} from '../lib/data.js'\nimport { getPayloadMultilangCustom } from './config.js'\nimport { TranslationActionsClient } from './TranslationActionsClient.js'\n\nconst getFieldNames = (\n props: DocumentViewServerProps,\n): MultilangFieldNames => {\n const custom = props.initPageResult.collectionConfig?.admin?.custom as\n | { payloadMultilang?: { fieldNames?: Partial<MultilangFieldNames> } }\n | undefined\n\n return {\n ...DEFAULT_FIELD_NAMES,\n ...(custom?.payloadMultilang?.fieldNames || {}),\n }\n}\n\nconst getLanguages = (props: DocumentViewServerProps): MultilangLanguage[] => {\n const languages = getPayloadMultilangCustom(\n props.initPageResult.collectionConfig?.admin?.custom,\n ).languages\n\n return Array.isArray(languages) ? languages : []\n}\n\nconst translate = (\n props: DocumentViewServerProps,\n key: PayloadMultilangTranslationKey,\n options?: Record<string, unknown>,\n): string => props.i18n?.t ? props.i18n.t(key as never, options) : key\n\nexport const TranslationsTab = async (props: DocumentViewServerProps) => {\n const collectionSlug = props.initPageResult.collectionConfig?.slug\n const translationsSegmentIndex = props.routeSegments.lastIndexOf('translations')\n const routeDocID =\n translationsSegmentIndex > 0 ? props.routeSegments[translationsSegmentIndex - 1] : undefined\n const docID = getID(asDocument(props.doc).id || routeDocID || props.id)\n\n if (!collectionSlug || !docID) {\n return (\n <section className=\"payload-multilang-page\">\n <h1>{translate(props, 'payloadMultilang:translations')}</h1>\n <p>{translate(props, 'payloadMultilang:saveBeforeManagingTranslations')}</p>\n </section>\n )\n }\n\n const fieldNames = getFieldNames(props)\n const languages = getLanguages(props)\n const state = await getDocumentTranslationsWithPayload({\n id: docID,\n collection: collectionSlug,\n fieldNames,\n payload: props.payload,\n })\n\n return (\n <div className=\"payload-multilang-page\" data-testid=\"multilang-translations-tab\">\n <header className=\"payload-multilang-page__header\">\n <div>\n <h1>{translate(props, 'payloadMultilang:translations')}</h1>\n <p>{translate(props, 'payloadMultilang:createOrConnectDocuments')}</p>\n </div>\n </header>\n\n <div className=\"payload-multilang-layout\">\n <section className=\"payload-multilang-panel payload-multilang-panel--wide\">\n <h2>{translate(props, 'payloadMultilang:linkedDocuments')}</h2>\n <table className=\"payload-multilang-table\">\n <thead>\n <tr>\n <th>{translate(props, 'payloadMultilang:language')}</th>\n <th>{translate(props, 'payloadMultilang:documentID')}</th>\n </tr>\n </thead>\n <tbody>\n {languages.map((language) => {\n const translation = state.translations[language.code]\n return (\n <tr key={language.code}>\n <td>\n {language.flagLabel ? `${language.flagLabel} ` : ''}\n {language.name}\n </td>\n <td>\n {translation?.id\n ? String(translation.id)\n : translate(props, 'payloadMultilang:missing')}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </section>\n\n <TranslationActionsClient\n collectionSlug={collectionSlug}\n docID={docID}\n languages={languages}\n state={state}\n />\n </div>\n </div>\n )\n}\n"],"names":["DEFAULT_FIELD_NAMES","asDocument","getDocumentTranslationsWithPayload","getID","getPayloadMultilangCustom","TranslationActionsClient","getFieldNames","props","custom","initPageResult","collectionConfig","admin","payloadMultilang","fieldNames","getLanguages","languages","Array","isArray","translate","key","options","i18n","t","TranslationsTab","collectionSlug","slug","translationsSegmentIndex","routeSegments","lastIndexOf","routeDocID","undefined","docID","doc","id","section","className","h1","p","state","collection","payload","div","data-testid","header","h2","table","thead","tr","th","tbody","map","language","translation","translations","code","td","flagLabel","name","String"],"mappings":";AAKA,SAASA,mBAAmB,QAAQ,kBAAiB;AACrD,SACEC,UAAU,EACVC,kCAAkC,EAClCC,KAAK,QACA,iBAAgB;AACvB,SAASC,yBAAyB,QAAQ,cAAa;AACvD,SAASC,wBAAwB,QAAQ,gCAA+B;AAExE,MAAMC,gBAAgB,CACpBC;IAEA,MAAMC,SAASD,MAAME,cAAc,CAACC,gBAAgB,EAAEC,OAAOH;IAI7D,OAAO;QACL,GAAGR,mBAAmB;QACtB,GAAIQ,QAAQI,kBAAkBC,cAAc,CAAC,CAAC;IAChD;AACF;AAEA,MAAMC,eAAe,CAACP;IACpB,MAAMQ,YAAYX,0BAChBG,MAAME,cAAc,CAACC,gBAAgB,EAAEC,OAAOH,QAC9CO,SAAS;IAEX,OAAOC,MAAMC,OAAO,CAACF,aAAaA,YAAY,EAAE;AAClD;AAEA,MAAMG,YAAY,CAChBX,OACAY,KACAC,UACWb,MAAMc,IAAI,EAAEC,IAAIf,MAAMc,IAAI,CAACC,CAAC,CAACH,KAAcC,WAAWD;AAEnE,OAAO,MAAMI,kBAAkB,OAAOhB;IACpC,MAAMiB,iBAAiBjB,MAAME,cAAc,CAACC,gBAAgB,EAAEe;IAC9D,MAAMC,2BAA2BnB,MAAMoB,aAAa,CAACC,WAAW,CAAC;IACjE,MAAMC,aACJH,2BAA2B,IAAInB,MAAMoB,aAAa,CAACD,2BAA2B,EAAE,GAAGI;IACrF,MAAMC,QAAQ5B,MAAMF,WAAWM,MAAMyB,GAAG,EAAEC,EAAE,IAAIJ,cAActB,MAAM0B,EAAE;IAEtE,IAAI,CAACT,kBAAkB,CAACO,OAAO;QAC7B,qBACE,MAACG;YAAQC,WAAU;;8BACjB,KAACC;8BAAIlB,UAAUX,OAAO;;8BACtB,KAAC8B;8BAAGnB,UAAUX,OAAO;;;;IAG3B;IAEA,MAAMM,aAAaP,cAAcC;IACjC,MAAMQ,YAAYD,aAAaP;IAC/B,MAAM+B,QAAQ,MAAMpC,mCAAmC;QACrD+B,IAAIF;QACJQ,YAAYf;QACZX;QACA2B,SAASjC,MAAMiC,OAAO;IACxB;IAEA,qBACE,MAACC;QAAIN,WAAU;QAAyBO,eAAY;;0BAClD,KAACC;gBAAOR,WAAU;0BAChB,cAAA,MAACM;;sCACC,KAACL;sCAAIlB,UAAUX,OAAO;;sCACtB,KAAC8B;sCAAGnB,UAAUX,OAAO;;;;;0BAIzB,MAACkC;gBAAIN,WAAU;;kCACb,MAACD;wBAAQC,WAAU;;0CACjB,KAACS;0CAAI1B,UAAUX,OAAO;;0CACtB,MAACsC;gCAAMV,WAAU;;kDACf,KAACW;kDACC,cAAA,MAACC;;8DACC,KAACC;8DAAI9B,UAAUX,OAAO;;8DACtB,KAACyC;8DAAI9B,UAAUX,OAAO;;;;;kDAG1B,KAAC0C;kDACElC,UAAUmC,GAAG,CAAC,CAACC;4CACd,MAAMC,cAAcd,MAAMe,YAAY,CAACF,SAASG,IAAI,CAAC;4CACrD,qBACE,MAACP;;kEACC,MAACQ;;4DACEJ,SAASK,SAAS,GAAG,GAAGL,SAASK,SAAS,CAAC,CAAC,CAAC,GAAG;4DAChDL,SAASM,IAAI;;;kEAEhB,KAACF;kEACEH,aAAanB,KACVyB,OAAON,YAAYnB,EAAE,IACrBf,UAAUX,OAAO;;;+CARhB4C,SAASG,IAAI;wCAY1B;;;;;;kCAKN,KAACjD;wBACCmB,gBAAgBA;wBAChBO,OAAOA;wBACPhB,WAAWA;wBACXuB,OAAOA;;;;;;AAKjB,EAAC"}
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export declare const MULTILANG_GROUP_FIELD: string;
|
|
|
4
4
|
export declare const MULTILANG_LANGUAGE_FIELD: string;
|
|
5
5
|
export declare const MULTILANG_META_FIELD: string;
|
|
6
6
|
export declare const DEFAULT_GLOBAL_TABS_LABEL = "Localized content";
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const SYSTEM_DUPLICATION_EXCLUDED_FIELDS: string[];
|
|
8
8
|
export declare const MULTILANG_SKIP_HOOK = "payloadMultilangSkip";
|
package/dist/constants.js
CHANGED
|
@@ -7,7 +7,7 @@ export const MULTILANG_GROUP_FIELD = DEFAULT_FIELD_NAMES.group;
|
|
|
7
7
|
export const MULTILANG_LANGUAGE_FIELD = DEFAULT_FIELD_NAMES.language;
|
|
8
8
|
export const MULTILANG_META_FIELD = DEFAULT_FIELD_NAMES.meta;
|
|
9
9
|
export const DEFAULT_GLOBAL_TABS_LABEL = 'Localized content';
|
|
10
|
-
export const
|
|
10
|
+
export const SYSTEM_DUPLICATION_EXCLUDED_FIELDS = [
|
|
11
11
|
'id',
|
|
12
12
|
'createdAt',
|
|
13
13
|
'updatedAt',
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import type { MultilangFieldNames } from './types.js'\n\nexport const DEFAULT_FIELD_NAMES: MultilangFieldNames = {\n group: '_multilangGroup',\n language: '_multilangLanguage',\n meta: '_multilangMeta',\n}\n\nexport const MULTILANG_GROUP_FIELD = DEFAULT_FIELD_NAMES.group\nexport const MULTILANG_LANGUAGE_FIELD = DEFAULT_FIELD_NAMES.language\nexport const MULTILANG_META_FIELD = DEFAULT_FIELD_NAMES.meta\n\nexport const DEFAULT_GLOBAL_TABS_LABEL = 'Localized content'\n\nexport const
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import type { MultilangFieldNames } from './types.js'\n\nexport const DEFAULT_FIELD_NAMES: MultilangFieldNames = {\n group: '_multilangGroup',\n language: '_multilangLanguage',\n meta: '_multilangMeta',\n}\n\nexport const MULTILANG_GROUP_FIELD = DEFAULT_FIELD_NAMES.group\nexport const MULTILANG_LANGUAGE_FIELD = DEFAULT_FIELD_NAMES.language\nexport const MULTILANG_META_FIELD = DEFAULT_FIELD_NAMES.meta\n\nexport const DEFAULT_GLOBAL_TABS_LABEL = 'Localized content'\n\nexport const SYSTEM_DUPLICATION_EXCLUDED_FIELDS = [\n 'id',\n 'createdAt',\n 'updatedAt',\n 'deletedAt',\n '_status',\n 'sizes',\n 'filename',\n 'filesize',\n 'height',\n 'mimeType',\n 'thumbnailURL',\n 'url',\n 'width',\n]\n\nexport const MULTILANG_SKIP_HOOK = 'payloadMultilangSkip'\n"],"names":["DEFAULT_FIELD_NAMES","group","language","meta","MULTILANG_GROUP_FIELD","MULTILANG_LANGUAGE_FIELD","MULTILANG_META_FIELD","DEFAULT_GLOBAL_TABS_LABEL","SYSTEM_DUPLICATION_EXCLUDED_FIELDS","MULTILANG_SKIP_HOOK"],"mappings":"AAEA,OAAO,MAAMA,sBAA2C;IACtDC,OAAO;IACPC,UAAU;IACVC,MAAM;AACR,EAAC;AAED,OAAO,MAAMC,wBAAwBJ,oBAAoBC,KAAK,CAAA;AAC9D,OAAO,MAAMI,2BAA2BL,oBAAoBE,QAAQ,CAAA;AACpE,OAAO,MAAMI,uBAAuBN,oBAAoBG,IAAI,CAAA;AAE5D,OAAO,MAAMI,4BAA4B,oBAAmB;AAE5D,OAAO,MAAMC,qCAAqC;IAChD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAA;AAED,OAAO,MAAMC,sBAAsB,uBAAsB"}
|
|
@@ -97,15 +97,15 @@ export const createTranslationStateEndpoint = ({ collection })=>({
|
|
|
97
97
|
id,
|
|
98
98
|
collection: collection.slug,
|
|
99
99
|
fieldNames: collection.fieldNames,
|
|
100
|
-
payload: req.payload,
|
|
101
100
|
overrideAccess: false,
|
|
101
|
+
payload: req.payload,
|
|
102
102
|
req
|
|
103
103
|
}) : await getGroupTranslationsWithPayload({
|
|
104
104
|
collection: collection.slug,
|
|
105
105
|
fieldNames: collection.fieldNames,
|
|
106
106
|
group: group,
|
|
107
|
-
payload: req.payload,
|
|
108
107
|
overrideAccess: false,
|
|
108
|
+
payload: req.payload,
|
|
109
109
|
req
|
|
110
110
|
});
|
|
111
111
|
return json(state);
|
|
@@ -153,14 +153,12 @@ export const createCreateTranslationEndpoint = ({ collection, collectionConfig }
|
|
|
153
153
|
if (duplicateError) {
|
|
154
154
|
return duplicateError;
|
|
155
155
|
}
|
|
156
|
-
const
|
|
157
|
-
const data = shouldDuplicate ? duplicateDocumentData({
|
|
156
|
+
const data = duplicateDocumentData({
|
|
158
157
|
collection: collectionConfig,
|
|
159
158
|
doc: source,
|
|
160
|
-
duplicateExcludeFields: collection.duplicateExcludeFields,
|
|
161
159
|
fieldNames: collection.fieldNames,
|
|
162
160
|
targetLanguage
|
|
163
|
-
})
|
|
161
|
+
});
|
|
164
162
|
const doc = await req.payload.create({
|
|
165
163
|
collection: collection.slug,
|
|
166
164
|
data: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/translations.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint, PayloadRequest, Where } from 'payload'\n\nimport { randomUUID } from 'crypto'\n\nimport type { ResolvedMultilangCollection } from '../types.js'\n\nimport { MULTILANG_SKIP_HOOK } from '../constants.js'\nimport {\n asDocument,\n duplicateDocumentData,\n getDocumentTranslationsWithPayload,\n getGroupTranslationsWithPayload,\n getID,\n getStringValue,\n} from '../lib/data.js'\n\nconst json = (body: unknown, init?: ResponseInit): Response =>\n Response.json(body, init)\n\nconst parseBody = async (req: PayloadRequest): Promise<Record<string, unknown>> => {\n if (typeof req.json !== 'function') {\n return {}\n }\n\n return asDocument(await req.json())\n}\n\nconst getLanguageCode = (value: unknown): string | undefined => {\n const language = getStringValue(value)?.trim().toLowerCase()\n\n return language || undefined\n}\n\nconst validateLanguage = ({\n collection,\n language,\n}: {\n collection: ResolvedMultilangCollection\n language: string\n}): Response | undefined => {\n if (collection.languages.some((candidate) => candidate.code === language)) {\n return undefined\n }\n\n return json(\n {\n message: `Language \"${language}\" is not configured for ${collection.slug}.`,\n },\n { status: 400 },\n )\n}\n\nconst ensureGroup = async ({\n collection,\n doc,\n req,\n}: {\n collection: ResolvedMultilangCollection\n doc: Record<string, unknown>\n req: PayloadRequest\n}): Promise<string> => {\n const existingGroup = getStringValue(doc[collection.fieldNames.group])\n\n if (existingGroup) {\n return existingGroup\n }\n\n const id = getID(doc.id)\n\n if (!id) {\n return randomUUID()\n }\n\n const group = randomUUID()\n\n await req.payload.update({\n id,\n collection: collection.slug,\n context: {\n [MULTILANG_SKIP_HOOK]: true,\n },\n data: {\n [collection.fieldNames.group]: group,\n },\n overrideAccess: false,\n req,\n })\n\n return group\n}\n\nconst checkDuplicateLanguage = async ({\n collection,\n excludeID,\n group,\n language,\n req,\n}: {\n collection: ResolvedMultilangCollection\n excludeID?: number | string\n group: string\n language: string\n req: PayloadRequest\n}): Promise<Response | undefined> => {\n const and: Where[] = [\n {\n [collection.fieldNames.group]: {\n equals: group,\n },\n },\n {\n [collection.fieldNames.language]: {\n equals: language,\n },\n },\n ]\n\n if (excludeID) {\n and.push({\n id: {\n not_equals: excludeID,\n },\n })\n }\n\n const { totalDocs } = await req.payload.count({\n collection: collection.slug,\n overrideAccess: false,\n req,\n where: {\n and,\n },\n })\n\n if (totalDocs > 0) {\n return json(\n {\n message: `A translation already exists for language \"${language}\".`,\n },\n { status: 409 },\n )\n }\n}\n\nexport const createTranslationStateEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const id = getID(req.query?.id)\n const group = getStringValue(req.query?.group)\n\n if (!id && !group) {\n return json({ message: 'Missing document id or translation group.' }, { status: 400 })\n }\n\n const state = id\n ? await getDocumentTranslationsWithPayload({\n id,\n collection: collection.slug,\n fieldNames: collection.fieldNames,\n payload: req.payload,\n overrideAccess: false,\n req,\n })\n : await getGroupTranslationsWithPayload({\n collection: collection.slug,\n fieldNames: collection.fieldNames,\n group: group!,\n payload: req.payload,\n overrideAccess: false,\n req,\n })\n\n return json(state)\n },\n method: 'get',\n path: '/multilang/state',\n})\n\nexport const createCreateTranslationEndpoint = ({\n collection,\n collectionConfig,\n}: {\n collection: ResolvedMultilangCollection\n collectionConfig: CollectionConfig\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const sourceID = getID(body.sourceId)\n const targetLanguage = getLanguageCode(body.targetLanguage)\n\n if (!sourceID || !targetLanguage) {\n return json(\n { message: 'sourceId and targetLanguage are required.' },\n { status: 400 },\n )\n }\n\n const languageError = validateLanguage({\n collection,\n language: targetLanguage,\n })\n\n if (languageError) {\n return languageError\n }\n\n const source = asDocument(\n await req.payload.findByID({\n id: sourceID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const group = await ensureGroup({ collection, doc: source, req })\n const duplicateError = await checkDuplicateLanguage({\n collection,\n group,\n language: targetLanguage,\n req,\n })\n\n if (duplicateError) {\n return duplicateError\n }\n\n const shouldDuplicate = body.duplicate !== false && collection.duplicate\n const data = shouldDuplicate\n ? duplicateDocumentData({\n collection: collectionConfig,\n doc: source,\n duplicateExcludeFields: collection.duplicateExcludeFields,\n fieldNames: collection.fieldNames,\n targetLanguage,\n })\n : {}\n\n const doc = await req.payload.create({\n collection: collection.slug,\n data: {\n ...data,\n [collection.fieldNames.group]: group,\n [collection.fieldNames.language]: targetLanguage,\n [collection.fieldNames.meta]: {\n createdFrom: sourceID,\n },\n },\n overrideAccess: false,\n req,\n })\n\n return json({\n doc,\n })\n },\n method: 'post',\n path: '/multilang/create',\n})\n\nexport const createConnectTranslationEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const sourceID = getID(body.sourceId)\n const targetID = getID(body.targetId)\n const targetLanguage = getLanguageCode(body.targetLanguage)\n\n if (!sourceID || !targetID || sourceID === targetID) {\n return json({ message: 'sourceId and a different targetId are required.' }, { status: 400 })\n }\n\n const source = asDocument(\n await req.payload.findByID({\n id: sourceID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const target = asDocument(\n await req.payload.findByID({\n id: targetID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const group = await ensureGroup({ collection, doc: source, req })\n const language =\n targetLanguage ||\n getLanguageCode(target[collection.fieldNames.language]) ||\n getLanguageCode(source[collection.fieldNames.language])\n\n if (!language) {\n return json({ message: 'Target language is required.' }, { status: 400 })\n }\n\n const languageError = validateLanguage({\n collection,\n language,\n })\n\n if (languageError) {\n return languageError\n }\n\n const duplicateError = await checkDuplicateLanguage({\n collection,\n excludeID: targetID,\n group,\n language,\n req,\n })\n\n if (duplicateError) {\n return duplicateError\n }\n\n const doc = await req.payload.update({\n id: targetID,\n collection: collection.slug,\n data: {\n [collection.fieldNames.group]: group,\n [collection.fieldNames.language]: language,\n },\n overrideAccess: false,\n req,\n })\n\n return json({ doc })\n },\n method: 'post',\n path: '/multilang/connect',\n})\n\nexport const createDisconnectTranslationEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const id = getID(body.id)\n\n if (!id) {\n return json({ message: 'Missing document id.' }, { status: 400 })\n }\n\n const doc = await req.payload.update({\n id,\n collection: collection.slug,\n data: {\n [collection.fieldNames.group]: randomUUID(),\n },\n overrideAccess: false,\n req,\n })\n\n return json({ doc })\n },\n method: 'post',\n path: '/multilang/disconnect',\n})\n\nexport const createTranslationEndpoints = ({\n collection,\n collectionConfig,\n}: {\n collection: ResolvedMultilangCollection\n collectionConfig: CollectionConfig\n}): Endpoint[] => [\n createTranslationStateEndpoint({ collection }),\n createCreateTranslationEndpoint({ collection, collectionConfig }),\n createConnectTranslationEndpoint({ collection }),\n createDisconnectTranslationEndpoint({ collection }),\n]\n"],"names":["randomUUID","MULTILANG_SKIP_HOOK","asDocument","duplicateDocumentData","getDocumentTranslationsWithPayload","getGroupTranslationsWithPayload","getID","getStringValue","json","body","init","Response","parseBody","req","getLanguageCode","value","language","trim","toLowerCase","undefined","validateLanguage","collection","languages","some","candidate","code","message","slug","status","ensureGroup","doc","existingGroup","fieldNames","group","id","payload","update","context","data","overrideAccess","checkDuplicateLanguage","excludeID","and","equals","push","not_equals","totalDocs","count","where","createTranslationStateEndpoint","handler","query","state","method","path","createCreateTranslationEndpoint","collectionConfig","sourceID","sourceId","targetLanguage","languageError","source","findByID","depth","duplicateError","shouldDuplicate","duplicate","duplicateExcludeFields","create","meta","createdFrom","createConnectTranslationEndpoint","targetID","targetId","target","createDisconnectTranslationEndpoint","createTranslationEndpoints"],"mappings":"AAEA,SAASA,UAAU,QAAQ,SAAQ;AAInC,SAASC,mBAAmB,QAAQ,kBAAiB;AACrD,SACEC,UAAU,EACVC,qBAAqB,EACrBC,kCAAkC,EAClCC,+BAA+B,EAC/BC,KAAK,EACLC,cAAc,QACT,iBAAgB;AAEvB,MAAMC,OAAO,CAACC,MAAeC,OAC3BC,SAASH,IAAI,CAACC,MAAMC;AAEtB,MAAME,YAAY,OAAOC;IACvB,IAAI,OAAOA,IAAIL,IAAI,KAAK,YAAY;QAClC,OAAO,CAAC;IACV;IAEA,OAAON,WAAW,MAAMW,IAAIL,IAAI;AAClC;AAEA,MAAMM,kBAAkB,CAACC;IACvB,MAAMC,WAAWT,eAAeQ,QAAQE,OAAOC;IAE/C,OAAOF,YAAYG;AACrB;AAEA,MAAMC,mBAAmB,CAAC,EACxBC,UAAU,EACVL,QAAQ,EAIT;IACC,IAAIK,WAAWC,SAAS,CAACC,IAAI,CAAC,CAACC,YAAcA,UAAUC,IAAI,KAAKT,WAAW;QACzE,OAAOG;IACT;IAEA,OAAOX,KACL;QACEkB,SAAS,CAAC,UAAU,EAAEV,SAAS,wBAAwB,EAAEK,WAAWM,IAAI,CAAC,CAAC,CAAC;IAC7E,GACA;QAAEC,QAAQ;IAAI;AAElB;AAEA,MAAMC,cAAc,OAAO,EACzBR,UAAU,EACVS,GAAG,EACHjB,GAAG,EAKJ;IACC,MAAMkB,gBAAgBxB,eAAeuB,GAAG,CAACT,WAAWW,UAAU,CAACC,KAAK,CAAC;IAErE,IAAIF,eAAe;QACjB,OAAOA;IACT;IAEA,MAAMG,KAAK5B,MAAMwB,IAAII,EAAE;IAEvB,IAAI,CAACA,IAAI;QACP,OAAOlC;IACT;IAEA,MAAMiC,QAAQjC;IAEd,MAAMa,IAAIsB,OAAO,CAACC,MAAM,CAAC;QACvBF;QACAb,YAAYA,WAAWM,IAAI;QAC3BU,SAAS;YACP,CAACpC,oBAAoB,EAAE;QACzB;QACAqC,MAAM;YACJ,CAACjB,WAAWW,UAAU,CAACC,KAAK,CAAC,EAAEA;QACjC;QACAM,gBAAgB;QAChB1B;IACF;IAEA,OAAOoB;AACT;AAEA,MAAMO,yBAAyB,OAAO,EACpCnB,UAAU,EACVoB,SAAS,EACTR,KAAK,EACLjB,QAAQ,EACRH,GAAG,EAOJ;IACC,MAAM6B,MAAe;QACnB;YACE,CAACrB,WAAWW,UAAU,CAACC,KAAK,CAAC,EAAE;gBAC7BU,QAAQV;YACV;QACF;QACA;YACE,CAACZ,WAAWW,UAAU,CAAChB,QAAQ,CAAC,EAAE;gBAChC2B,QAAQ3B;YACV;QACF;KACD;IAED,IAAIyB,WAAW;QACbC,IAAIE,IAAI,CAAC;YACPV,IAAI;gBACFW,YAAYJ;YACd;QACF;IACF;IAEA,MAAM,EAAEK,SAAS,EAAE,GAAG,MAAMjC,IAAIsB,OAAO,CAACY,KAAK,CAAC;QAC5C1B,YAAYA,WAAWM,IAAI;QAC3BY,gBAAgB;QAChB1B;QACAmC,OAAO;YACLN;QACF;IACF;IAEA,IAAII,YAAY,GAAG;QACjB,OAAOtC,KACL;YACEkB,SAAS,CAAC,2CAA2C,EAAEV,SAAS,EAAE,CAAC;QACrE,GACA;YAAEY,QAAQ;QAAI;IAElB;AACF;AAEA,OAAO,MAAMqB,iCAAiC,CAAC,EAC7C5B,UAAU,EAGX,GAAgB,CAAA;QACf6B,SAAS,OAAOrC;YACd,MAAMqB,KAAK5B,MAAMO,IAAIsC,KAAK,EAAEjB;YAC5B,MAAMD,QAAQ1B,eAAeM,IAAIsC,KAAK,EAAElB;YAExC,IAAI,CAACC,MAAM,CAACD,OAAO;gBACjB,OAAOzB,KAAK;oBAAEkB,SAAS;gBAA4C,GAAG;oBAAEE,QAAQ;gBAAI;YACtF;YAEA,MAAMwB,QAAQlB,KACV,MAAM9B,mCAAmC;gBACvC8B;gBACAb,YAAYA,WAAWM,IAAI;gBAC3BK,YAAYX,WAAWW,UAAU;gBACjCG,SAAStB,IAAIsB,OAAO;gBACpBI,gBAAgB;gBAChB1B;YACF,KACA,MAAMR,gCAAgC;gBACpCgB,YAAYA,WAAWM,IAAI;gBAC3BK,YAAYX,WAAWW,UAAU;gBACjCC,OAAOA;gBACPE,SAAStB,IAAIsB,OAAO;gBACpBI,gBAAgB;gBAChB1B;YACF;YAEJ,OAAOL,KAAK4C;QACd;QACAC,QAAQ;QACRC,MAAM;IACR,CAAA,EAAE;AAEF,OAAO,MAAMC,kCAAkC,CAAC,EAC9ClC,UAAU,EACVmC,gBAAgB,EAIjB,GAAgB,CAAA;QACfN,SAAS,OAAOrC;YACd,MAAMJ,OAAO,MAAMG,UAAUC;YAC7B,MAAM4C,WAAWnD,MAAMG,KAAKiD,QAAQ;YACpC,MAAMC,iBAAiB7C,gBAAgBL,KAAKkD,cAAc;YAE1D,IAAI,CAACF,YAAY,CAACE,gBAAgB;gBAChC,OAAOnD,KACL;oBAAEkB,SAAS;gBAA4C,GACvD;oBAAEE,QAAQ;gBAAI;YAElB;YAEA,MAAMgC,gBAAgBxC,iBAAiB;gBACrCC;gBACAL,UAAU2C;YACZ;YAEA,IAAIC,eAAe;gBACjB,OAAOA;YACT;YAEA,MAAMC,SAAS3D,WACb,MAAMW,IAAIsB,OAAO,CAAC2B,QAAQ,CAAC;gBACzB5B,IAAIuB;gBACJpC,YAAYA,WAAWM,IAAI;gBAC3BoC,OAAO;gBACPxB,gBAAgB;gBAChB1B;YACF;YAEF,MAAMoB,QAAQ,MAAMJ,YAAY;gBAAER;gBAAYS,KAAK+B;gBAAQhD;YAAI;YAC/D,MAAMmD,iBAAiB,MAAMxB,uBAAuB;gBAClDnB;gBACAY;gBACAjB,UAAU2C;gBACV9C;YACF;YAEA,IAAImD,gBAAgB;gBAClB,OAAOA;YACT;YAEA,MAAMC,kBAAkBxD,KAAKyD,SAAS,KAAK,SAAS7C,WAAW6C,SAAS;YACxE,MAAM5B,OAAO2B,kBACT9D,sBAAsB;gBACpBkB,YAAYmC;gBACZ1B,KAAK+B;gBACLM,wBAAwB9C,WAAW8C,sBAAsB;gBACzDnC,YAAYX,WAAWW,UAAU;gBACjC2B;YACF,KACA,CAAC;YAEL,MAAM7B,MAAM,MAAMjB,IAAIsB,OAAO,CAACiC,MAAM,CAAC;gBACnC/C,YAAYA,WAAWM,IAAI;gBAC3BW,MAAM;oBACJ,GAAGA,IAAI;oBACP,CAACjB,WAAWW,UAAU,CAACC,KAAK,CAAC,EAAEA;oBAC/B,CAACZ,WAAWW,UAAU,CAAChB,QAAQ,CAAC,EAAE2C;oBAClC,CAACtC,WAAWW,UAAU,CAACqC,IAAI,CAAC,EAAE;wBAC5BC,aAAab;oBACf;gBACF;gBACAlB,gBAAgB;gBAChB1B;YACF;YAEA,OAAOL,KAAK;gBACVsB;YACF;QACF;QACAuB,QAAQ;QACRC,MAAM;IACR,CAAA,EAAE;AAEF,OAAO,MAAMiB,mCAAmC,CAAC,EAC/ClD,UAAU,EAGX,GAAgB,CAAA;QACf6B,SAAS,OAAOrC;YACd,MAAMJ,OAAO,MAAMG,UAAUC;YAC7B,MAAM4C,WAAWnD,MAAMG,KAAKiD,QAAQ;YACpC,MAAMc,WAAWlE,MAAMG,KAAKgE,QAAQ;YACpC,MAAMd,iBAAiB7C,gBAAgBL,KAAKkD,cAAc;YAE1D,IAAI,CAACF,YAAY,CAACe,YAAYf,aAAae,UAAU;gBACnD,OAAOhE,KAAK;oBAAEkB,SAAS;gBAAkD,GAAG;oBAAEE,QAAQ;gBAAI;YAC5F;YAEA,MAAMiC,SAAS3D,WACb,MAAMW,IAAIsB,OAAO,CAAC2B,QAAQ,CAAC;gBACzB5B,IAAIuB;gBACJpC,YAAYA,WAAWM,IAAI;gBAC3BoC,OAAO;gBACPxB,gBAAgB;gBAChB1B;YACF;YAEF,MAAM6D,SAASxE,WACb,MAAMW,IAAIsB,OAAO,CAAC2B,QAAQ,CAAC;gBACzB5B,IAAIsC;gBACJnD,YAAYA,WAAWM,IAAI;gBAC3BoC,OAAO;gBACPxB,gBAAgB;gBAChB1B;YACF;YAEF,MAAMoB,QAAQ,MAAMJ,YAAY;gBAAER;gBAAYS,KAAK+B;gBAAQhD;YAAI;YAC/D,MAAMG,WACJ2C,kBACA7C,gBAAgB4D,MAAM,CAACrD,WAAWW,UAAU,CAAChB,QAAQ,CAAC,KACtDF,gBAAgB+C,MAAM,CAACxC,WAAWW,UAAU,CAAChB,QAAQ,CAAC;YAExD,IAAI,CAACA,UAAU;gBACb,OAAOR,KAAK;oBAAEkB,SAAS;gBAA+B,GAAG;oBAAEE,QAAQ;gBAAI;YACzE;YAEA,MAAMgC,gBAAgBxC,iBAAiB;gBACrCC;gBACAL;YACF;YAEA,IAAI4C,eAAe;gBACjB,OAAOA;YACT;YAEA,MAAMI,iBAAiB,MAAMxB,uBAAuB;gBAClDnB;gBACAoB,WAAW+B;gBACXvC;gBACAjB;gBACAH;YACF;YAEA,IAAImD,gBAAgB;gBAClB,OAAOA;YACT;YAEA,MAAMlC,MAAM,MAAMjB,IAAIsB,OAAO,CAACC,MAAM,CAAC;gBACnCF,IAAIsC;gBACJnD,YAAYA,WAAWM,IAAI;gBAC3BW,MAAM;oBACJ,CAACjB,WAAWW,UAAU,CAACC,KAAK,CAAC,EAAEA;oBAC/B,CAACZ,WAAWW,UAAU,CAAChB,QAAQ,CAAC,EAAEA;gBACpC;gBACAuB,gBAAgB;gBAChB1B;YACF;YAEA,OAAOL,KAAK;gBAAEsB;YAAI;QACpB;QACAuB,QAAQ;QACRC,MAAM;IACR,CAAA,EAAE;AAEF,OAAO,MAAMqB,sCAAsC,CAAC,EAClDtD,UAAU,EAGX,GAAgB,CAAA;QACf6B,SAAS,OAAOrC;YACd,MAAMJ,OAAO,MAAMG,UAAUC;YAC7B,MAAMqB,KAAK5B,MAAMG,KAAKyB,EAAE;YAExB,IAAI,CAACA,IAAI;gBACP,OAAO1B,KAAK;oBAAEkB,SAAS;gBAAuB,GAAG;oBAAEE,QAAQ;gBAAI;YACjE;YAEA,MAAME,MAAM,MAAMjB,IAAIsB,OAAO,CAACC,MAAM,CAAC;gBACnCF;gBACAb,YAAYA,WAAWM,IAAI;gBAC3BW,MAAM;oBACJ,CAACjB,WAAWW,UAAU,CAACC,KAAK,CAAC,EAAEjC;gBACjC;gBACAuC,gBAAgB;gBAChB1B;YACF;YAEA,OAAOL,KAAK;gBAAEsB;YAAI;QACpB;QACAuB,QAAQ;QACRC,MAAM;IACR,CAAA,EAAE;AAEF,OAAO,MAAMsB,6BAA6B,CAAC,EACzCvD,UAAU,EACVmC,gBAAgB,EAIjB,GAAiB;QAChBP,+BAA+B;YAAE5B;QAAW;QAC5CkC,gCAAgC;YAAElC;YAAYmC;QAAiB;QAC/De,iCAAiC;YAAElD;QAAW;QAC9CsD,oCAAoC;YAAEtD;QAAW;KAClD,CAAA"}
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/translations.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint, PayloadRequest, Where } from 'payload'\n\nimport { randomUUID } from 'crypto'\n\nimport type { ResolvedMultilangCollection } from '../types.js'\n\nimport { MULTILANG_SKIP_HOOK } from '../constants.js'\nimport {\n asDocument,\n duplicateDocumentData,\n getDocumentTranslationsWithPayload,\n getGroupTranslationsWithPayload,\n getID,\n getStringValue,\n} from '../lib/data.js'\n\nconst json = (body: unknown, init?: ResponseInit): Response =>\n Response.json(body, init)\n\nconst parseBody = async (req: PayloadRequest): Promise<Record<string, unknown>> => {\n if (typeof req.json !== 'function') {\n return {}\n }\n\n return asDocument(await req.json())\n}\n\nconst getLanguageCode = (value: unknown): string | undefined => {\n const language = getStringValue(value)?.trim().toLowerCase()\n\n return language || undefined\n}\n\nconst validateLanguage = ({\n collection,\n language,\n}: {\n collection: ResolvedMultilangCollection\n language: string\n}): Response | undefined => {\n if (collection.languages.some((candidate) => candidate.code === language)) {\n return undefined\n }\n\n return json(\n {\n message: `Language \"${language}\" is not configured for ${collection.slug}.`,\n },\n { status: 400 },\n )\n}\n\nconst ensureGroup = async ({\n collection,\n doc,\n req,\n}: {\n collection: ResolvedMultilangCollection\n doc: Record<string, unknown>\n req: PayloadRequest\n}): Promise<string> => {\n const existingGroup = getStringValue(doc[collection.fieldNames.group])\n\n if (existingGroup) {\n return existingGroup\n }\n\n const id = getID(doc.id)\n\n if (!id) {\n return randomUUID()\n }\n\n const group = randomUUID()\n\n await req.payload.update({\n id,\n collection: collection.slug,\n context: {\n [MULTILANG_SKIP_HOOK]: true,\n },\n data: {\n [collection.fieldNames.group]: group,\n },\n overrideAccess: false,\n req,\n })\n\n return group\n}\n\nconst checkDuplicateLanguage = async ({\n collection,\n excludeID,\n group,\n language,\n req,\n}: {\n collection: ResolvedMultilangCollection\n excludeID?: number | string\n group: string\n language: string\n req: PayloadRequest\n}): Promise<Response | undefined> => {\n const and: Where[] = [\n {\n [collection.fieldNames.group]: {\n equals: group,\n },\n },\n {\n [collection.fieldNames.language]: {\n equals: language,\n },\n },\n ]\n\n if (excludeID) {\n and.push({\n id: {\n not_equals: excludeID,\n },\n })\n }\n\n const { totalDocs } = await req.payload.count({\n collection: collection.slug,\n overrideAccess: false,\n req,\n where: {\n and,\n },\n })\n\n if (totalDocs > 0) {\n return json(\n {\n message: `A translation already exists for language \"${language}\".`,\n },\n { status: 409 },\n )\n }\n}\n\nexport const createTranslationStateEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const id = getID(req.query?.id)\n const group = getStringValue(req.query?.group)\n\n if (!id && !group) {\n return json({ message: 'Missing document id or translation group.' }, { status: 400 })\n }\n\n const state = id\n ? await getDocumentTranslationsWithPayload({\n id,\n collection: collection.slug,\n fieldNames: collection.fieldNames,\n overrideAccess: false,\n payload: req.payload,\n req,\n })\n : await getGroupTranslationsWithPayload({\n collection: collection.slug,\n fieldNames: collection.fieldNames,\n group: group!,\n overrideAccess: false,\n payload: req.payload,\n req,\n })\n\n return json(state)\n },\n method: 'get',\n path: '/multilang/state',\n})\n\nexport const createCreateTranslationEndpoint = ({\n collection,\n collectionConfig,\n}: {\n collection: ResolvedMultilangCollection\n collectionConfig: CollectionConfig\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const sourceID = getID(body.sourceId)\n const targetLanguage = getLanguageCode(body.targetLanguage)\n\n if (!sourceID || !targetLanguage) {\n return json(\n { message: 'sourceId and targetLanguage are required.' },\n { status: 400 },\n )\n }\n\n const languageError = validateLanguage({\n collection,\n language: targetLanguage,\n })\n\n if (languageError) {\n return languageError\n }\n\n const source = asDocument(\n await req.payload.findByID({\n id: sourceID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const group = await ensureGroup({ collection, doc: source, req })\n const duplicateError = await checkDuplicateLanguage({\n collection,\n group,\n language: targetLanguage,\n req,\n })\n\n if (duplicateError) {\n return duplicateError\n }\n\n const data = duplicateDocumentData({\n collection: collectionConfig,\n doc: source,\n fieldNames: collection.fieldNames,\n targetLanguage,\n })\n\n const doc = await req.payload.create({\n collection: collection.slug,\n data: {\n ...data,\n [collection.fieldNames.group]: group,\n [collection.fieldNames.language]: targetLanguage,\n [collection.fieldNames.meta]: {\n createdFrom: sourceID,\n },\n },\n overrideAccess: false,\n req,\n })\n\n return json({\n doc,\n })\n },\n method: 'post',\n path: '/multilang/create',\n})\n\nexport const createConnectTranslationEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const sourceID = getID(body.sourceId)\n const targetID = getID(body.targetId)\n const targetLanguage = getLanguageCode(body.targetLanguage)\n\n if (!sourceID || !targetID || sourceID === targetID) {\n return json({ message: 'sourceId and a different targetId are required.' }, { status: 400 })\n }\n\n const source = asDocument(\n await req.payload.findByID({\n id: sourceID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const target = asDocument(\n await req.payload.findByID({\n id: targetID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const group = await ensureGroup({ collection, doc: source, req })\n const language =\n targetLanguage ||\n getLanguageCode(target[collection.fieldNames.language]) ||\n getLanguageCode(source[collection.fieldNames.language])\n\n if (!language) {\n return json({ message: 'Target language is required.' }, { status: 400 })\n }\n\n const languageError = validateLanguage({\n collection,\n language,\n })\n\n if (languageError) {\n return languageError\n }\n\n const duplicateError = await checkDuplicateLanguage({\n collection,\n excludeID: targetID,\n group,\n language,\n req,\n })\n\n if (duplicateError) {\n return duplicateError\n }\n\n const doc = await req.payload.update({\n id: targetID,\n collection: collection.slug,\n data: {\n [collection.fieldNames.group]: group,\n [collection.fieldNames.language]: language,\n },\n overrideAccess: false,\n req,\n })\n\n return json({ doc })\n },\n method: 'post',\n path: '/multilang/connect',\n})\n\nexport const createDisconnectTranslationEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const id = getID(body.id)\n\n if (!id) {\n return json({ message: 'Missing document id.' }, { status: 400 })\n }\n\n const doc = await req.payload.update({\n id,\n collection: collection.slug,\n data: {\n [collection.fieldNames.group]: randomUUID(),\n },\n overrideAccess: false,\n req,\n })\n\n return json({ doc })\n },\n method: 'post',\n path: '/multilang/disconnect',\n})\n\nexport const createTranslationEndpoints = ({\n collection,\n collectionConfig,\n}: {\n collection: ResolvedMultilangCollection\n collectionConfig: CollectionConfig\n}): Endpoint[] => [\n createTranslationStateEndpoint({ collection }),\n createCreateTranslationEndpoint({ collection, collectionConfig }),\n createConnectTranslationEndpoint({ collection }),\n createDisconnectTranslationEndpoint({ collection }),\n]\n"],"names":["randomUUID","MULTILANG_SKIP_HOOK","asDocument","duplicateDocumentData","getDocumentTranslationsWithPayload","getGroupTranslationsWithPayload","getID","getStringValue","json","body","init","Response","parseBody","req","getLanguageCode","value","language","trim","toLowerCase","undefined","validateLanguage","collection","languages","some","candidate","code","message","slug","status","ensureGroup","doc","existingGroup","fieldNames","group","id","payload","update","context","data","overrideAccess","checkDuplicateLanguage","excludeID","and","equals","push","not_equals","totalDocs","count","where","createTranslationStateEndpoint","handler","query","state","method","path","createCreateTranslationEndpoint","collectionConfig","sourceID","sourceId","targetLanguage","languageError","source","findByID","depth","duplicateError","create","meta","createdFrom","createConnectTranslationEndpoint","targetID","targetId","target","createDisconnectTranslationEndpoint","createTranslationEndpoints"],"mappings":"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"}
|
|
@@ -3,8 +3,9 @@ import type { ResolvedMultilangCollection } from '../types.js';
|
|
|
3
3
|
export declare const createTranslatedCollectionBeforeChangeHook: ({ collection }: {
|
|
4
4
|
collection: ResolvedMultilangCollection;
|
|
5
5
|
}) => CollectionBeforeChangeHook;
|
|
6
|
-
export declare const createSynchronizedFieldsAfterChangeHook: ({ collection }: {
|
|
6
|
+
export declare const createSynchronizedFieldsAfterChangeHook: ({ collection, fields, }: {
|
|
7
7
|
collection: ResolvedMultilangCollection;
|
|
8
|
+
fields?: Field[];
|
|
8
9
|
}) => CollectionAfterChangeHook;
|
|
9
10
|
export declare const createHiddenFields: ({ collection, }: {
|
|
10
11
|
collection: ResolvedMultilangCollection;
|
|
@@ -1,13 +1,99 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { MULTILANG_SKIP_HOOK } from '../constants.js';
|
|
3
3
|
import { asDocument, getID, getStringValue } from '../lib/data.js';
|
|
4
|
+
const LANGUAGE_LABEL = {
|
|
5
|
+
en: 'Language',
|
|
6
|
+
uk: 'Мова'
|
|
7
|
+
};
|
|
8
|
+
const TRANSLATIONS_LABEL = {
|
|
9
|
+
en: 'Translations',
|
|
10
|
+
uk: 'Переклади'
|
|
11
|
+
};
|
|
4
12
|
const hasSkipContext = (context)=>context?.[MULTILANG_SKIP_HOOK] === true;
|
|
13
|
+
const isObject = (value)=>typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
|
+
const toArray = (value)=>Array.isArray(value) ? value.filter(isObject) : [];
|
|
5
15
|
const valuesAreEqual = (left, right)=>{
|
|
6
16
|
if (left === right) {
|
|
7
17
|
return true;
|
|
8
18
|
}
|
|
9
19
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
10
20
|
};
|
|
21
|
+
const fieldAffectsData = (field)=>'name' in field && typeof field.name === 'string' && field.type !== 'ui';
|
|
22
|
+
const fieldSynchronizes = (field)=>{
|
|
23
|
+
const custom = isObject(field.custom) ? field.custom : {};
|
|
24
|
+
const multilang = isObject(custom.multilang) ? custom.multilang : {};
|
|
25
|
+
return multilang.synchronize === true;
|
|
26
|
+
};
|
|
27
|
+
const getSynchronizedFields = (fields = [])=>fields.flatMap((field)=>{
|
|
28
|
+
if (!fieldAffectsData(field) || !fieldSynchronizes(field)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
name: field.name,
|
|
34
|
+
type: field.type,
|
|
35
|
+
strategy: field.type === 'array' || field.type === 'blocks' ? 'shell' : 'value'
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
});
|
|
39
|
+
const getRowKey = (row, index)=>{
|
|
40
|
+
const id = getID(row.id);
|
|
41
|
+
return id === undefined ? `index:${index}` : `id:${String(id)}`;
|
|
42
|
+
};
|
|
43
|
+
const getShellSignature = (value, field)=>JSON.stringify(toArray(value).map((row, index)=>({
|
|
44
|
+
blockName: field.type === 'blocks' ? getStringValue(row.blockName) : undefined,
|
|
45
|
+
blockType: field.type === 'blocks' ? getStringValue(row.blockType) : undefined,
|
|
46
|
+
key: getRowKey(row, index)
|
|
47
|
+
})));
|
|
48
|
+
const shellChanged = (field, nextDoc, previous)=>{
|
|
49
|
+
if (!(field.name in nextDoc)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return getShellSignature(nextDoc[field.name], field) !== getShellSignature(previous[field.name], field);
|
|
53
|
+
};
|
|
54
|
+
const createShellRow = (sourceRow, targetRow, field)=>{
|
|
55
|
+
const row = targetRow ? {
|
|
56
|
+
...targetRow
|
|
57
|
+
} : {};
|
|
58
|
+
if (field.type === 'blocks' && 'blockType' in sourceRow) {
|
|
59
|
+
row.blockType = sourceRow.blockType;
|
|
60
|
+
}
|
|
61
|
+
if (field.type === 'blocks') {
|
|
62
|
+
if ('blockName' in sourceRow) {
|
|
63
|
+
row.blockName = sourceRow.blockName;
|
|
64
|
+
} else {
|
|
65
|
+
delete row.blockName;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return row;
|
|
69
|
+
};
|
|
70
|
+
const synchronizeShellValue = ({ field, previousSourceValue, sourceValue, targetValue })=>{
|
|
71
|
+
const targetRows = toArray(targetValue);
|
|
72
|
+
const previousSourceIndexesByKey = new Map(toArray(previousSourceValue).map((row, index)=>[
|
|
73
|
+
getRowKey(row, index),
|
|
74
|
+
index
|
|
75
|
+
]));
|
|
76
|
+
return toArray(sourceValue).map((sourceRow, index)=>{
|
|
77
|
+
const previousIndex = previousSourceIndexesByKey.get(getRowKey(sourceRow, index));
|
|
78
|
+
return createShellRow(sourceRow, previousIndex === undefined ? undefined : targetRows[previousIndex], field);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const getSynchronizedDataForTranslation = ({ fields, nextDoc, previous, translation })=>fields.reduce((acc, field)=>{
|
|
82
|
+
if (field.strategy === 'value') {
|
|
83
|
+
acc[field.name] = nextDoc[field.name];
|
|
84
|
+
return acc;
|
|
85
|
+
}
|
|
86
|
+
const value = synchronizeShellValue({
|
|
87
|
+
field,
|
|
88
|
+
previousSourceValue: previous[field.name],
|
|
89
|
+
sourceValue: nextDoc[field.name],
|
|
90
|
+
targetValue: translation[field.name]
|
|
91
|
+
});
|
|
92
|
+
if (!valuesAreEqual(value, translation[field.name])) {
|
|
93
|
+
acc[field.name] = value;
|
|
94
|
+
}
|
|
95
|
+
return acc;
|
|
96
|
+
}, {});
|
|
11
97
|
const isConfiguredLanguage = (collection, language)=>Boolean(language && collection.languages.some((configuredLanguage)=>configuredLanguage.code === language));
|
|
12
98
|
const getDuplicateLanguageIDs = async ({ collection, excludeID, group, language, req })=>{
|
|
13
99
|
const and = [
|
|
@@ -85,8 +171,9 @@ export const createTranslatedCollectionBeforeChangeHook = ({ collection })=>asyn
|
|
|
85
171
|
}
|
|
86
172
|
return nextData;
|
|
87
173
|
};
|
|
88
|
-
export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async ({ context, doc, operation, previousDoc, req })=>{
|
|
89
|
-
|
|
174
|
+
export const createSynchronizedFieldsAfterChangeHook = ({ collection, fields })=>async ({ context, doc, operation, previousDoc, req })=>{
|
|
175
|
+
const synchronizedFields = getSynchronizedFields(fields);
|
|
176
|
+
if (hasSkipContext(context) || operation !== 'update' || synchronizedFields.length === 0) {
|
|
90
177
|
return doc;
|
|
91
178
|
}
|
|
92
179
|
const nextDoc = asDocument(doc);
|
|
@@ -96,13 +183,13 @@ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async (
|
|
|
96
183
|
if (!group || !currentID) {
|
|
97
184
|
return doc;
|
|
98
185
|
}
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
|
|
186
|
+
const changedFields = synchronizedFields.filter((field)=>{
|
|
187
|
+
if (field.strategy === 'shell') {
|
|
188
|
+
return shellChanged(field, nextDoc, previous);
|
|
102
189
|
}
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
if (
|
|
190
|
+
return field.name in nextDoc && !valuesAreEqual(nextDoc[field.name], previous[field.name]);
|
|
191
|
+
});
|
|
192
|
+
if (changedFields.length === 0) {
|
|
106
193
|
return doc;
|
|
107
194
|
}
|
|
108
195
|
const translations = await req.payload.find({
|
|
@@ -126,13 +213,32 @@ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async (
|
|
|
126
213
|
]
|
|
127
214
|
}
|
|
128
215
|
});
|
|
129
|
-
await Promise.all(translations.docs.map((translation)=>
|
|
216
|
+
await Promise.all(translations.docs.map((translation)=>{
|
|
217
|
+
const translationDoc = asDocument(translation);
|
|
218
|
+
const id = getID(translationDoc.id);
|
|
219
|
+
if (id === undefined) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const synchronizedData = getSynchronizedDataForTranslation({
|
|
223
|
+
fields: changedFields,
|
|
224
|
+
nextDoc,
|
|
225
|
+
previous,
|
|
226
|
+
translation: translationDoc
|
|
227
|
+
});
|
|
228
|
+
if (Object.keys(synchronizedData).length === 0) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
id,
|
|
233
|
+
data: synchronizedData
|
|
234
|
+
};
|
|
235
|
+
}).filter((update)=>update !== undefined).map(({ id, data })=>req.payload.update({
|
|
130
236
|
id,
|
|
131
237
|
collection: collection.slug,
|
|
132
238
|
context: {
|
|
133
239
|
[MULTILANG_SKIP_HOOK]: true
|
|
134
240
|
},
|
|
135
|
-
data
|
|
241
|
+
data,
|
|
136
242
|
overrideAccess: true,
|
|
137
243
|
req
|
|
138
244
|
})));
|
|
@@ -147,7 +253,7 @@ export const createHiddenFields = ({ collection })=>[
|
|
|
147
253
|
hidden: true
|
|
148
254
|
},
|
|
149
255
|
index: true,
|
|
150
|
-
label:
|
|
256
|
+
label: LANGUAGE_LABEL
|
|
151
257
|
},
|
|
152
258
|
{
|
|
153
259
|
name: collection.fieldNames.group,
|
|
@@ -157,7 +263,7 @@ export const createHiddenFields = ({ collection })=>[
|
|
|
157
263
|
hidden: true
|
|
158
264
|
},
|
|
159
265
|
index: true,
|
|
160
|
-
label:
|
|
266
|
+
label: TRANSLATIONS_LABEL
|
|
161
267
|
},
|
|
162
268
|
{
|
|
163
269
|
name: collection.fieldNames.meta,
|
|
@@ -192,7 +298,7 @@ export const createLanguageColumnFields = ({ collection })=>{
|
|
|
192
298
|
disableListColumn: false,
|
|
193
299
|
width: '132px'
|
|
194
300
|
},
|
|
195
|
-
label: label ||
|
|
301
|
+
label: label || TRANSLATIONS_LABEL
|
|
196
302
|
}
|
|
197
303
|
];
|
|
198
304
|
};
|
|
@@ -242,7 +348,7 @@ export const withTranslatedCollection = ({ collection, config })=>{
|
|
|
242
348
|
Component: '@roxxel/payload-multilang/rsc#TranslationsTab',
|
|
243
349
|
path: '/translations',
|
|
244
350
|
tab: {
|
|
245
|
-
label: '
|
|
351
|
+
label: ({ t })=>t('payloadMultilang:translations'),
|
|
246
352
|
order: 80
|
|
247
353
|
}
|
|
248
354
|
}
|
|
@@ -273,7 +379,8 @@ export const withTranslatedCollection = ({ collection, config })=>{
|
|
|
273
379
|
...config.hooks,
|
|
274
380
|
afterChange: [
|
|
275
381
|
createSynchronizedFieldsAfterChangeHook({
|
|
276
|
-
collection
|
|
382
|
+
collection,
|
|
383
|
+
fields: config.fields
|
|
277
384
|
}),
|
|
278
385
|
...config.hooks?.afterChange || []
|
|
279
386
|
],
|