@roxxel/payload-multilang 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -0
- package/dist/components/LanguageListToolbar.d.ts +6 -0
- package/dist/components/LanguageListToolbar.js +69 -0
- package/dist/components/LanguageListToolbar.js.map +1 -0
- package/dist/components/LanguageMetabox.d.ts +2 -0
- package/dist/components/LanguageMetabox.js +275 -0
- package/dist/components/LanguageMetabox.js.map +1 -0
- package/dist/components/TranslationActionsClient.d.ts +10 -0
- package/dist/components/TranslationActionsClient.js +166 -0
- package/dist/components/TranslationActionsClient.js.map +1 -0
- package/dist/components/TranslationColumnCell.d.ts +2 -0
- package/dist/components/TranslationColumnCell.js +69 -0
- package/dist/components/TranslationColumnCell.js.map +1 -0
- package/dist/components/TranslationColumnCellClient.d.ts +12 -0
- package/dist/components/TranslationColumnCellClient.js +107 -0
- package/dist/components/TranslationColumnCellClient.js.map +1 -0
- package/dist/components/TranslationsTab.d.ts +2 -0
- package/dist/components/TranslationsTab.js +118 -0
- package/dist/components/TranslationsTab.js.map +1 -0
- package/dist/components/config.d.ts +40 -0
- package/dist/components/config.js +31 -0
- package/dist/components/config.js.map +1 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +24 -0
- package/dist/constants.js.map +1 -0
- package/dist/endpoints/translations.d.ts +19 -0
- package/dist/endpoints/translations.js +301 -0
- package/dist/endpoints/translations.js.map +1 -0
- package/dist/exports/client.d.ts +4 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +2 -0
- package/dist/exports/rsc.js +4 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/hooks/translatedCollection.d.ts +26 -0
- package/dist/hooks/translatedCollection.js +290 -0
- package/dist/hooks/translatedCollection.js.map +1 -0
- package/dist/hooks/translatedGlobal.d.ts +16 -0
- package/dist/hooks/translatedGlobal.js +71 -0
- package/dist/hooks/translatedGlobal.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.js +96 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/data.d.ts +107 -0
- package/dist/lib/data.js +307 -0
- package/dist/lib/data.js.map +1 -0
- package/dist/payload-config.d.js +2 -0
- package/dist/payload-config.d.js.map +1 -0
- package/dist/styles/admin.css +316 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/docs/assets/admin-ui/collection-list-language-shortcuts.png +0 -0
- package/docs/assets/admin-ui/english-post-translations.png +0 -0
- package/docs/assets/admin-ui/ukrainian-post-translations.png +0 -0
- package/docs/configuration.md +192 -0
- package/docs/helpers.md +231 -0
- package/docs/usage.md +269 -0
- package/package.json +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# @roxxel/payload-multilang
|
|
2
|
+
|
|
3
|
+
Polylang-style document localization for Payload CMS
|
|
4
|
+
|
|
5
|
+
Plugin lets editors manage each translation as its own Payload document.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @roxxel/payload-multilang
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
Add the plugin to `payload.config.ts` and list the collections or globals that should be localized.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { buildConfig } from 'payload'
|
|
19
|
+
import { payloadMultilang } from '@roxxel/payload-multilang'
|
|
20
|
+
|
|
21
|
+
export default buildConfig({
|
|
22
|
+
localization: false,
|
|
23
|
+
plugins: [
|
|
24
|
+
payloadMultilang({
|
|
25
|
+
collections: {
|
|
26
|
+
posts: {
|
|
27
|
+
synchronizedFields: ['featuredImage', 'author'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
globals: {
|
|
31
|
+
'site-settings': {
|
|
32
|
+
label: 'Settings translations',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
languages: [
|
|
36
|
+
{
|
|
37
|
+
code: 'en',
|
|
38
|
+
flagLabel: 'EN',
|
|
39
|
+
isDefault: true,
|
|
40
|
+
locale: 'en_US',
|
|
41
|
+
name: 'English',
|
|
42
|
+
order: 0,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
code: 'uk',
|
|
46
|
+
flagLabel: 'UK',
|
|
47
|
+
locale: 'uk_UA',
|
|
48
|
+
name: 'Ukrainian',
|
|
49
|
+
order: 1,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
In the Payload admin panel, enabled collections get:
|
|
58
|
+
|
|
59
|
+
- a language selector in the document sidebar
|
|
60
|
+
- translation status/actions for each configured language
|
|
61
|
+
- a `Translations` edit view for creating, connecting, and disconnecting translations
|
|
62
|
+
- a language filter and translation shortcuts in the collection list
|
|
63
|
+
|
|
64
|
+
Enabled globals get one tab per configured language.
|
|
65
|
+
|
|
66
|
+
## Admin UI
|
|
67
|
+
|
|
68
|
+
Collection lists include a language filter and translation shortcuts for each configured language.
|
|
69
|
+
|
|
70
|
+
<img src="./docs/assets/admin-ui/collection-list-language-shortcuts.png" alt="Payload collection list with language filter and translation shortcut columns" width="100%">
|
|
71
|
+
|
|
72
|
+
Document edit views show the current language, linked translations, and quick actions for opening or creating missing translations.
|
|
73
|
+
|
|
74
|
+
<img src="./docs/assets/admin-ui/english-post-translations.png" alt="English post edit view showing linked Ukrainian translation action in the language sidebar" width="100%">
|
|
75
|
+
|
|
76
|
+
<img src="./docs/assets/admin-ui/ukrainian-post-translations.png" alt="Ukrainian post edit view showing linked English translation action in the language sidebar" width="100%">
|
|
77
|
+
|
|
78
|
+
## Query by Language
|
|
79
|
+
|
|
80
|
+
Use `withLanguage` when querying localized collections.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { withLanguage } from '@roxxel/payload-multilang'
|
|
84
|
+
|
|
85
|
+
const posts = await payload.find({
|
|
86
|
+
collection: 'posts',
|
|
87
|
+
where: withLanguage({
|
|
88
|
+
language: 'uk',
|
|
89
|
+
where: {
|
|
90
|
+
_status: {
|
|
91
|
+
equals: 'published',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Translation Helpers
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import {
|
|
102
|
+
findGlobalByLanguage,
|
|
103
|
+
getDocumentTranslation,
|
|
104
|
+
getDocumentTranslations,
|
|
105
|
+
getLanguages,
|
|
106
|
+
updateGlobalByLanguage,
|
|
107
|
+
withLanguage,
|
|
108
|
+
} from '@roxxel/payload-multilang'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Common examples:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const languages = await getLanguages()
|
|
115
|
+
|
|
116
|
+
const state = await getDocumentTranslations({
|
|
117
|
+
collection: 'posts',
|
|
118
|
+
id: post.id,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const ukrainianPost = await getDocumentTranslation({
|
|
122
|
+
collection: 'posts',
|
|
123
|
+
id: post.id,
|
|
124
|
+
language: 'uk',
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const settings = await findGlobalByLanguage({
|
|
128
|
+
slug: 'site-settings',
|
|
129
|
+
language: 'uk',
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
await updateGlobalByLanguage({
|
|
133
|
+
slug: 'site-settings',
|
|
134
|
+
language: 'uk',
|
|
135
|
+
data: {
|
|
136
|
+
title: 'Localized title',
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
- [Configuration](./docs/configuration.md)
|
|
144
|
+
- [Developer Usage](./docs/usage.md)
|
|
145
|
+
- [Helper API](./docs/helpers.md)
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
The `./dev` app uses SQLite. By default it writes `dev/payload.dev.sqlite`; tests use isolated temporary SQLite files under `/tmp`.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pnpm install
|
|
153
|
+
pnpm run generate:types
|
|
154
|
+
pnpm run test:int
|
|
155
|
+
pnpm run test:e2e
|
|
156
|
+
pnpm run build
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
162
|
+
|
|
163
|
+
## Notice
|
|
164
|
+
|
|
165
|
+
This plugin was built entirely by an LLM.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useConfig, useRouteTransition } from '@payloadcms/ui';
|
|
4
|
+
import { usePathname, useRouter, useSearchParams } from 'next/navigation.js';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import '../styles/admin.css';
|
|
7
|
+
import { getCollectionFieldNames, getConfiguredLanguagesFromConfig } from './config.js';
|
|
8
|
+
export const LanguageListToolbar = ({ collectionSlug })=>{
|
|
9
|
+
const { config } = useConfig();
|
|
10
|
+
const pathname = usePathname();
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
const searchParams = useSearchParams();
|
|
13
|
+
const { isTransitioning, startRouteTransition } = useRouteTransition();
|
|
14
|
+
const languages = getConfiguredLanguagesFromConfig(config);
|
|
15
|
+
const fieldNames = useMemo(()=>getCollectionFieldNames({
|
|
16
|
+
collectionSlug,
|
|
17
|
+
config
|
|
18
|
+
}), [
|
|
19
|
+
collectionSlug,
|
|
20
|
+
config
|
|
21
|
+
]);
|
|
22
|
+
const languageParam = `where[${fieldNames.language}][equals]`;
|
|
23
|
+
const currentLanguage = searchParams.get(languageParam) || '';
|
|
24
|
+
if (!languages.length) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const handleLanguageChange = (language)=>{
|
|
28
|
+
const nextParams = new URLSearchParams(searchParams.toString());
|
|
29
|
+
if (language) {
|
|
30
|
+
nextParams.set(languageParam, language);
|
|
31
|
+
} else {
|
|
32
|
+
nextParams.delete(languageParam);
|
|
33
|
+
}
|
|
34
|
+
nextParams.set('page', '1');
|
|
35
|
+
const query = nextParams.toString();
|
|
36
|
+
const href = query ? `${pathname}?${query}` : pathname;
|
|
37
|
+
startRouteTransition(()=>{
|
|
38
|
+
router.push(href);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
42
|
+
className: "payload-multilang-toolbar",
|
|
43
|
+
"data-testid": "multilang-list-toolbar",
|
|
44
|
+
children: /*#__PURE__*/ _jsx("label", {
|
|
45
|
+
className: "payload-multilang-toolbar__select",
|
|
46
|
+
children: /*#__PURE__*/ _jsxs("select", {
|
|
47
|
+
"aria-label": "Language",
|
|
48
|
+
disabled: isTransitioning,
|
|
49
|
+
onChange: (event)=>handleLanguageChange(event.target.value),
|
|
50
|
+
value: currentLanguage,
|
|
51
|
+
children: [
|
|
52
|
+
/*#__PURE__*/ _jsx("option", {
|
|
53
|
+
value: "",
|
|
54
|
+
children: "All languages"
|
|
55
|
+
}),
|
|
56
|
+
languages.map((language)=>/*#__PURE__*/ _jsxs("option", {
|
|
57
|
+
value: language.code,
|
|
58
|
+
children: [
|
|
59
|
+
language.flagLabel ? `${language.flagLabel} ` : '',
|
|
60
|
+
language.name
|
|
61
|
+
]
|
|
62
|
+
}, language.code))
|
|
63
|
+
]
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=LanguageListToolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/LanguageListToolbar.tsx"],"sourcesContent":["'use client'\n\nimport { useConfig, useRouteTransition } from '@payloadcms/ui'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation.js'\nimport { useMemo } from 'react'\n\nimport '../styles/admin.css'\nimport { getCollectionFieldNames, getConfiguredLanguagesFromConfig } from './config.js'\n\ntype Props = {\n collectionSlug: string\n}\n\nexport const LanguageListToolbar = ({ collectionSlug }: Props) => {\n const { config } = useConfig()\n const pathname = usePathname()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { isTransitioning, startRouteTransition } = useRouteTransition()\n const languages = getConfiguredLanguagesFromConfig(config)\n const fieldNames = useMemo(\n () => getCollectionFieldNames({ collectionSlug, config }),\n [collectionSlug, config],\n )\n const languageParam = `where[${fieldNames.language}][equals]`\n const currentLanguage = searchParams.get(languageParam) || ''\n\n if (!languages.length) {\n return null\n }\n\n const handleLanguageChange = (language: string) => {\n const nextParams = new URLSearchParams(searchParams.toString())\n\n if (language) {\n nextParams.set(languageParam, language)\n } else {\n nextParams.delete(languageParam)\n }\n\n nextParams.set('page', '1')\n\n const query = nextParams.toString()\n const href = query ? `${pathname}?${query}` : pathname\n\n startRouteTransition(() => {\n router.push(href)\n })\n }\n\n return (\n <div className=\"payload-multilang-toolbar\" data-testid=\"multilang-list-toolbar\">\n <label className=\"payload-multilang-toolbar__select\">\n <select\n aria-label=\"Language\"\n disabled={isTransitioning}\n onChange={(event) => handleLanguageChange(event.target.value)}\n value={currentLanguage}\n >\n <option value=\"\">All languages</option>\n {languages.map((language) => (\n <option key={language.code} value={language.code}>\n {language.flagLabel ? `${language.flagLabel} ` : ''}\n {language.name}\n </option>\n ))}\n </select>\n </label>\n </div>\n )\n}\n"],"names":["useConfig","useRouteTransition","usePathname","useRouter","useSearchParams","useMemo","getCollectionFieldNames","getConfiguredLanguagesFromConfig","LanguageListToolbar","collectionSlug","config","pathname","router","searchParams","isTransitioning","startRouteTransition","languages","fieldNames","languageParam","language","currentLanguage","get","length","handleLanguageChange","nextParams","URLSearchParams","toString","set","delete","query","href","push","div","className","data-testid","label","select","aria-label","disabled","onChange","event","target","value","option","map","code","flagLabel","name"],"mappings":"AAAA;;AAEA,SAASA,SAAS,EAAEC,kBAAkB,QAAQ,iBAAgB;AAC9D,SAASC,WAAW,EAAEC,SAAS,EAAEC,eAAe,QAAQ,qBAAoB;AAC5E,SAASC,OAAO,QAAQ,QAAO;AAE/B,OAAO,sBAAqB;AAC5B,SAASC,uBAAuB,EAAEC,gCAAgC,QAAQ,cAAa;AAMvF,OAAO,MAAMC,sBAAsB,CAAC,EAAEC,cAAc,EAAS;IAC3D,MAAM,EAAEC,MAAM,EAAE,GAAGV;IACnB,MAAMW,WAAWT;IACjB,MAAMU,SAAST;IACf,MAAMU,eAAeT;IACrB,MAAM,EAAEU,eAAe,EAAEC,oBAAoB,EAAE,GAAGd;IAClD,MAAMe,YAAYT,iCAAiCG;IACnD,MAAMO,aAAaZ,QACjB,IAAMC,wBAAwB;YAAEG;YAAgBC;QAAO,IACvD;QAACD;QAAgBC;KAAO;IAE1B,MAAMQ,gBAAgB,CAAC,MAAM,EAAED,WAAWE,QAAQ,CAAC,SAAS,CAAC;IAC7D,MAAMC,kBAAkBP,aAAaQ,GAAG,CAACH,kBAAkB;IAE3D,IAAI,CAACF,UAAUM,MAAM,EAAE;QACrB,OAAO;IACT;IAEA,MAAMC,uBAAuB,CAACJ;QAC5B,MAAMK,aAAa,IAAIC,gBAAgBZ,aAAaa,QAAQ;QAE5D,IAAIP,UAAU;YACZK,WAAWG,GAAG,CAACT,eAAeC;QAChC,OAAO;YACLK,WAAWI,MAAM,CAACV;QACpB;QAEAM,WAAWG,GAAG,CAAC,QAAQ;QAEvB,MAAME,QAAQL,WAAWE,QAAQ;QACjC,MAAMI,OAAOD,QAAQ,GAAGlB,SAAS,CAAC,EAAEkB,OAAO,GAAGlB;QAE9CI,qBAAqB;YACnBH,OAAOmB,IAAI,CAACD;QACd;IACF;IAEA,qBACE,KAACE;QAAIC,WAAU;QAA4BC,eAAY;kBACrD,cAAA,KAACC;YAAMF,WAAU;sBACf,cAAA,MAACG;gBACCC,cAAW;gBACXC,UAAUxB;gBACVyB,UAAU,CAACC,QAAUjB,qBAAqBiB,MAAMC,MAAM,CAACC,KAAK;gBAC5DA,OAAOtB;;kCAEP,KAACuB;wBAAOD,OAAM;kCAAG;;oBAChB1B,UAAU4B,GAAG,CAAC,CAACzB,yBACd,MAACwB;4BAA2BD,OAAOvB,SAAS0B,IAAI;;gCAC7C1B,SAAS2B,SAAS,GAAG,GAAG3B,SAAS2B,SAAS,CAAC,CAAC,CAAC,GAAG;gCAChD3B,SAAS4B,IAAI;;2BAFH5B,SAAS0B,IAAI;;;;;AAStC,EAAC"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { toast, useConfig, useDocumentInfo, useField, useRouteTransition } from '@payloadcms/ui';
|
|
4
|
+
import { useRouter } from 'next/navigation.js';
|
|
5
|
+
import { formatAdminURL } from 'payload/shared';
|
|
6
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import '../styles/admin.css';
|
|
8
|
+
import { getCollectionFieldNames, getConfiguredLanguagesFromConfig, getDefaultLanguageFromConfig } from './config.js';
|
|
9
|
+
const PenIcon = ()=>/*#__PURE__*/ _jsxs("svg", {
|
|
10
|
+
"aria-hidden": "true",
|
|
11
|
+
className: "payload-multilang-pen-icon",
|
|
12
|
+
fill: "none",
|
|
13
|
+
height: "16",
|
|
14
|
+
viewBox: "0 0 16 16",
|
|
15
|
+
width: "16",
|
|
16
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
17
|
+
children: [
|
|
18
|
+
/*#__PURE__*/ _jsx("path", {
|
|
19
|
+
d: "M9.9 2.6 13.4 6 5.7 13.7 2.2 14.4 2.9 10.9 9.9 2.6Z",
|
|
20
|
+
stroke: "currentColor",
|
|
21
|
+
strokeLinejoin: "round",
|
|
22
|
+
strokeWidth: "1.4"
|
|
23
|
+
}),
|
|
24
|
+
/*#__PURE__*/ _jsx("path", {
|
|
25
|
+
d: "M8.8 3.9 12.1 7.2",
|
|
26
|
+
stroke: "currentColor",
|
|
27
|
+
strokeLinecap: "round",
|
|
28
|
+
strokeWidth: "1.4"
|
|
29
|
+
})
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
const getDocumentID = (doc)=>{
|
|
33
|
+
const id = doc?.id;
|
|
34
|
+
return typeof id === 'number' || typeof id === 'string' ? id : undefined;
|
|
35
|
+
};
|
|
36
|
+
const isSameDocumentID = (left, right)=>left !== undefined && right !== undefined && String(left) === String(right);
|
|
37
|
+
export const LanguageMetabox = ()=>{
|
|
38
|
+
const { config } = useConfig();
|
|
39
|
+
const { collectionSlug, id } = useDocumentInfo();
|
|
40
|
+
const router = useRouter();
|
|
41
|
+
const { isTransitioning, startRouteTransition } = useRouteTransition();
|
|
42
|
+
const fieldNames = useMemo(()=>getCollectionFieldNames({
|
|
43
|
+
collectionSlug,
|
|
44
|
+
config
|
|
45
|
+
}), [
|
|
46
|
+
collectionSlug,
|
|
47
|
+
config
|
|
48
|
+
]);
|
|
49
|
+
const languageField = useField({
|
|
50
|
+
path: fieldNames.language
|
|
51
|
+
});
|
|
52
|
+
const languages = getConfiguredLanguagesFromConfig(config);
|
|
53
|
+
const defaultLanguage = getDefaultLanguageFromConfig(config);
|
|
54
|
+
const [busyLanguage, setBusyLanguage] = useState();
|
|
55
|
+
const [translationState, setTranslationState] = useState();
|
|
56
|
+
const groupField = useField({
|
|
57
|
+
path: fieldNames.group
|
|
58
|
+
});
|
|
59
|
+
const docID = typeof id === 'number' || typeof id === 'string' ? id : undefined;
|
|
60
|
+
const groupValue = groupField.value;
|
|
61
|
+
const isCreation = !docID;
|
|
62
|
+
const localizedLanguageCodes = useMemo(()=>new Set(Object.keys(translationState?.translations || {})), [
|
|
63
|
+
translationState?.translations
|
|
64
|
+
]);
|
|
65
|
+
const configuredLanguageCodes = useMemo(()=>new Set(languages.map((language)=>language.code)), [
|
|
66
|
+
languages
|
|
67
|
+
]);
|
|
68
|
+
const availableCreationLanguage = useMemo(()=>languages.find((language)=>!localizedLanguageCodes.has(language.code)), [
|
|
69
|
+
languages,
|
|
70
|
+
localizedLanguageCodes
|
|
71
|
+
]);
|
|
72
|
+
const preferredCreationLanguage = (defaultLanguage && !localizedLanguageCodes.has(defaultLanguage.code) ? defaultLanguage : availableCreationLanguage)?.code || '';
|
|
73
|
+
useEffect(()=>{
|
|
74
|
+
if (isCreation) {
|
|
75
|
+
if (!preferredCreationLanguage) {
|
|
76
|
+
if (languageField.value && localizedLanguageCodes.has(languageField.value)) {
|
|
77
|
+
languageField.setValue('');
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!languageField.value || localizedLanguageCodes.has(languageField.value)) {
|
|
82
|
+
languageField.setValue(preferredCreationLanguage);
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (languageField.value && !configuredLanguageCodes.has(languageField.value)) {
|
|
87
|
+
languageField.setValue('');
|
|
88
|
+
}
|
|
89
|
+
}, [
|
|
90
|
+
configuredLanguageCodes,
|
|
91
|
+
isCreation,
|
|
92
|
+
languageField,
|
|
93
|
+
localizedLanguageCodes,
|
|
94
|
+
preferredCreationLanguage
|
|
95
|
+
]);
|
|
96
|
+
useEffect(()=>{
|
|
97
|
+
if (!collectionSlug || !docID && !groupValue) {
|
|
98
|
+
setTranslationState(undefined);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const loadState = async ()=>{
|
|
102
|
+
const query = docID ? `id=${encodeURIComponent(String(docID))}` : `group=${encodeURIComponent(groupValue)}`;
|
|
103
|
+
const response = await fetch(formatAdminURL({
|
|
104
|
+
apiRoute: config.routes.api,
|
|
105
|
+
path: `/${collectionSlug}/multilang/state?${query}`
|
|
106
|
+
}), {
|
|
107
|
+
credentials: 'include'
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const result = await response.json();
|
|
113
|
+
setTranslationState(result);
|
|
114
|
+
};
|
|
115
|
+
void loadState();
|
|
116
|
+
}, [
|
|
117
|
+
collectionSlug,
|
|
118
|
+
config.routes.api,
|
|
119
|
+
docID,
|
|
120
|
+
groupValue
|
|
121
|
+
]);
|
|
122
|
+
const selectedLanguageCode = isCreation ? languageField.value || preferredCreationLanguage : languageField.value && configuredLanguageCodes.has(languageField.value) ? languageField.value : '';
|
|
123
|
+
const selectedLanguage = languages.find((language)=>language.code === selectedLanguageCode);
|
|
124
|
+
const persistedLanguage = !isCreation ? translationState?.language : undefined;
|
|
125
|
+
const hasAssignedConfiguredLanguage = Boolean(!isCreation && (translationState ? persistedLanguage && configuredLanguageCodes.has(persistedLanguage) : selectedLanguage));
|
|
126
|
+
const collectionAdminURL = (documentID)=>formatAdminURL({
|
|
127
|
+
adminRoute: config.routes.admin,
|
|
128
|
+
path: `/collections/${collectionSlug}/${documentID}`
|
|
129
|
+
});
|
|
130
|
+
const navigateToTranslation = (documentID)=>{
|
|
131
|
+
startRouteTransition(()=>{
|
|
132
|
+
router.push(collectionAdminURL(documentID));
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const createTranslation = async (targetLanguage)=>{
|
|
136
|
+
if (!collectionSlug || !docID) {
|
|
137
|
+
toast.error('Save the document before creating translations.');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
setBusyLanguage(targetLanguage);
|
|
141
|
+
const response = await fetch(formatAdminURL({
|
|
142
|
+
apiRoute: config.routes.api,
|
|
143
|
+
path: `/${collectionSlug}/multilang/create`
|
|
144
|
+
}), {
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
sourceId: docID,
|
|
147
|
+
targetLanguage
|
|
148
|
+
}),
|
|
149
|
+
credentials: 'include',
|
|
150
|
+
headers: {
|
|
151
|
+
'Content-Type': 'application/json'
|
|
152
|
+
},
|
|
153
|
+
method: 'POST'
|
|
154
|
+
});
|
|
155
|
+
setBusyLanguage(undefined);
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
const result = await response.json().catch(()=>undefined);
|
|
158
|
+
toast.error(result?.message || 'Translation could not be created.');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const result = await response.json();
|
|
162
|
+
if (result.doc?.id) {
|
|
163
|
+
startRouteTransition(()=>{
|
|
164
|
+
router.push(collectionAdminURL(result.doc.id));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
169
|
+
className: "payload-multilang-metabox",
|
|
170
|
+
children: [
|
|
171
|
+
/*#__PURE__*/ _jsx("div", {
|
|
172
|
+
className: "payload-multilang-metabox__header",
|
|
173
|
+
children: /*#__PURE__*/ _jsx("h3", {
|
|
174
|
+
children: "Language"
|
|
175
|
+
})
|
|
176
|
+
}),
|
|
177
|
+
/*#__PURE__*/ _jsxs("label", {
|
|
178
|
+
className: "payload-multilang-field",
|
|
179
|
+
children: [
|
|
180
|
+
/*#__PURE__*/ _jsx("span", {
|
|
181
|
+
children: "Language"
|
|
182
|
+
}),
|
|
183
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
184
|
+
className: "payload-multilang-language-select",
|
|
185
|
+
children: [
|
|
186
|
+
/*#__PURE__*/ _jsx("span", {
|
|
187
|
+
className: "payload-multilang-language-select__flag",
|
|
188
|
+
children: selectedLanguage?.flagLabel || '--'
|
|
189
|
+
}),
|
|
190
|
+
/*#__PURE__*/ _jsxs("select", {
|
|
191
|
+
disabled: hasAssignedConfiguredLanguage,
|
|
192
|
+
onChange: (event)=>languageField.setValue(event.target.value),
|
|
193
|
+
value: selectedLanguageCode,
|
|
194
|
+
children: [
|
|
195
|
+
/*#__PURE__*/ _jsx("option", {
|
|
196
|
+
value: "",
|
|
197
|
+
children: "Unassigned"
|
|
198
|
+
}),
|
|
199
|
+
languages.map((language)=>{
|
|
200
|
+
const isCurrent = selectedLanguageCode === language.code;
|
|
201
|
+
const isLocalized = localizedLanguageCodes.has(language.code);
|
|
202
|
+
const disabled = isCreation || !hasAssignedConfiguredLanguage ? isLocalized && !isCurrent : true;
|
|
203
|
+
return /*#__PURE__*/ _jsxs("option", {
|
|
204
|
+
disabled: disabled,
|
|
205
|
+
value: language.code,
|
|
206
|
+
children: [
|
|
207
|
+
language.flagLabel ? `${language.flagLabel} ` : '',
|
|
208
|
+
language.name
|
|
209
|
+
]
|
|
210
|
+
}, language.code);
|
|
211
|
+
})
|
|
212
|
+
]
|
|
213
|
+
})
|
|
214
|
+
]
|
|
215
|
+
})
|
|
216
|
+
]
|
|
217
|
+
}),
|
|
218
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
219
|
+
className: "payload-multilang-translations",
|
|
220
|
+
"data-testid": "multilang-sidebar-translations",
|
|
221
|
+
children: [
|
|
222
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
223
|
+
children: "Translations"
|
|
224
|
+
}),
|
|
225
|
+
languages.map((language)=>{
|
|
226
|
+
const translation = translationState?.translations[language.code];
|
|
227
|
+
const translationID = typeof translation?.id === 'number' || typeof translation?.id === 'string' ? translation.id : undefined;
|
|
228
|
+
const isCurrent = isSameDocumentID(translationID, docID);
|
|
229
|
+
const isUnavailableForCreate = isCreation && Boolean(translationID) && selectedLanguageCode !== language.code;
|
|
230
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
231
|
+
className: "payload-multilang-translation-row",
|
|
232
|
+
children: [
|
|
233
|
+
/*#__PURE__*/ _jsx("span", {
|
|
234
|
+
className: "payload-multilang-translation-row__flag",
|
|
235
|
+
children: language.flagLabel || language.code.toUpperCase()
|
|
236
|
+
}),
|
|
237
|
+
/*#__PURE__*/ _jsx("span", {
|
|
238
|
+
className: "payload-multilang-translation-row__name",
|
|
239
|
+
children: language.name
|
|
240
|
+
}),
|
|
241
|
+
isCurrent ? /*#__PURE__*/ _jsx("span", {
|
|
242
|
+
className: "payload-multilang-translation-row__current",
|
|
243
|
+
children: "Current"
|
|
244
|
+
}) : isUnavailableForCreate ? /*#__PURE__*/ _jsx("span", {
|
|
245
|
+
className: "payload-multilang-translation-row__current",
|
|
246
|
+
children: "Localized"
|
|
247
|
+
}) : translationID ? /*#__PURE__*/ _jsx("button", {
|
|
248
|
+
"aria-label": `Edit ${language.name} translation`,
|
|
249
|
+
className: "payload-multilang-link-button",
|
|
250
|
+
disabled: isTransitioning,
|
|
251
|
+
onClick: ()=>navigateToTranslation(translationID),
|
|
252
|
+
type: "button",
|
|
253
|
+
children: /*#__PURE__*/ _jsx(PenIcon, {})
|
|
254
|
+
}) : /*#__PURE__*/ _jsx("button", {
|
|
255
|
+
"aria-label": `Create ${language.name} translation`,
|
|
256
|
+
className: "payload-multilang-icon-button",
|
|
257
|
+
disabled: !docID || busyLanguage === language.code || isTransitioning,
|
|
258
|
+
onClick: ()=>void createTranslation(language.code),
|
|
259
|
+
type: "button",
|
|
260
|
+
children: "+"
|
|
261
|
+
})
|
|
262
|
+
]
|
|
263
|
+
}, language.code);
|
|
264
|
+
}),
|
|
265
|
+
!docID ? /*#__PURE__*/ _jsx("div", {
|
|
266
|
+
className: "payload-multilang-muted",
|
|
267
|
+
children: "Save before adding translations."
|
|
268
|
+
}) : null
|
|
269
|
+
]
|
|
270
|
+
})
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
//# sourceMappingURL=LanguageMetabox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/LanguageMetabox.tsx"],"sourcesContent":["'use client'\n\nimport { toast, useConfig, useDocumentInfo, useField, useRouteTransition } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation.js'\nimport { formatAdminURL } from 'payload/shared'\nimport { useEffect, useMemo, useState } from 'react'\n\nimport '../styles/admin.css'\nimport type { TranslationState } from '../types.js'\nimport {\n getCollectionFieldNames,\n getConfiguredLanguagesFromConfig,\n getDefaultLanguageFromConfig,\n} from './config.js'\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 d=\"M8.8 3.9 12.1 7.2\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeWidth=\"1.4\" />\n </svg>\n)\n\nconst getDocumentID = (doc: Record<string, unknown> | undefined): number | string | undefined => {\n const id = doc?.id\n\n return typeof id === 'number' || typeof id === 'string' ? id : undefined\n}\n\nconst isSameDocumentID = (\n left: number | string | undefined,\n right: number | string | undefined,\n): boolean => left !== undefined && right !== undefined && String(left) === String(right)\n\nexport const LanguageMetabox = () => {\n const { config } = useConfig()\n const { collectionSlug, id } = useDocumentInfo()\n const router = useRouter()\n const { isTransitioning, startRouteTransition } = useRouteTransition()\n const fieldNames = useMemo(\n () => getCollectionFieldNames({ collectionSlug, config }),\n [collectionSlug, config],\n )\n const languageField = useField<string>({ path: fieldNames.language })\n const languages = getConfiguredLanguagesFromConfig(config)\n const defaultLanguage = getDefaultLanguageFromConfig(config)\n const [busyLanguage, setBusyLanguage] = useState<string>()\n const [translationState, setTranslationState] = useState<TranslationState>()\n const groupField = useField<string>({ path: fieldNames.group })\n const docID = typeof id === 'number' || typeof id === 'string' ? id : undefined\n const groupValue = groupField.value\n const isCreation = !docID\n const localizedLanguageCodes = useMemo(\n () => new Set(Object.keys(translationState?.translations || {})),\n [translationState?.translations],\n )\n const configuredLanguageCodes = useMemo(\n () => new Set(languages.map((language) => language.code)),\n [languages],\n )\n const availableCreationLanguage = useMemo(\n () => languages.find((language) => !localizedLanguageCodes.has(language.code)),\n [languages, localizedLanguageCodes],\n )\n const preferredCreationLanguage =\n (defaultLanguage && !localizedLanguageCodes.has(defaultLanguage.code)\n ? defaultLanguage\n : availableCreationLanguage\n )?.code || ''\n\n useEffect(() => {\n if (isCreation) {\n if (!preferredCreationLanguage) {\n if (languageField.value && localizedLanguageCodes.has(languageField.value)) {\n languageField.setValue('')\n }\n\n return\n }\n\n if (!languageField.value || localizedLanguageCodes.has(languageField.value)) {\n languageField.setValue(preferredCreationLanguage)\n }\n\n return\n }\n\n if (\n languageField.value &&\n !configuredLanguageCodes.has(languageField.value)\n ) {\n languageField.setValue('')\n }\n }, [\n configuredLanguageCodes,\n isCreation,\n languageField,\n localizedLanguageCodes,\n preferredCreationLanguage,\n ])\n\n useEffect(() => {\n if (!collectionSlug || (!docID && !groupValue)) {\n setTranslationState(undefined)\n return\n }\n\n const loadState = async () => {\n const query = docID\n ? `id=${encodeURIComponent(String(docID))}`\n : `group=${encodeURIComponent(groupValue!)}`\n\n const response = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: `/${collectionSlug}/multilang/state?${query}`,\n }),\n {\n credentials: 'include',\n },\n )\n\n if (!response.ok) {\n return\n }\n\n const result = (await response.json()) as TranslationState\n setTranslationState(result)\n }\n\n void loadState()\n }, [collectionSlug, config.routes.api, docID, groupValue])\n\n const selectedLanguageCode = isCreation\n ? languageField.value || preferredCreationLanguage\n : languageField.value && configuredLanguageCodes.has(languageField.value)\n ? languageField.value\n : ''\n const selectedLanguage = languages.find((language) => language.code === selectedLanguageCode)\n const persistedLanguage = !isCreation ? translationState?.language : undefined\n const hasAssignedConfiguredLanguage = Boolean(\n !isCreation &&\n (translationState\n ? persistedLanguage && configuredLanguageCodes.has(persistedLanguage)\n : selectedLanguage),\n )\n\n const collectionAdminURL = (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(collectionAdminURL(documentID))\n })\n }\n\n const createTranslation = async (targetLanguage: string) => {\n if (!collectionSlug || !docID) {\n toast.error('Save the document before creating translations.')\n return\n }\n\n setBusyLanguage(targetLanguage)\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: docID,\n targetLanguage,\n }),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n },\n )\n\n setBusyLanguage(undefined)\n\n if (!response.ok) {\n const result = (await response.json().catch(() => undefined)) as\n { message?: string } | undefined\n toast.error(result?.message || 'Translation could not be created.')\n return\n }\n\n const result = (await response.json()) as {\n doc?: { id?: number | string }\n }\n\n if (result.doc?.id) {\n startRouteTransition(() => {\n router.push(collectionAdminURL(result.doc!.id!))\n })\n }\n }\n\n return (\n <section className=\"payload-multilang-metabox\">\n <div className=\"payload-multilang-metabox__header\">\n <h3>Language</h3>\n </div>\n\n <label className=\"payload-multilang-field\">\n <span>Language</span>\n <div className=\"payload-multilang-language-select\">\n <span className=\"payload-multilang-language-select__flag\">\n {selectedLanguage?.flagLabel || '--'}\n </span>\n <select\n disabled={hasAssignedConfiguredLanguage}\n onChange={(event) => languageField.setValue(event.target.value)}\n value={selectedLanguageCode}\n >\n <option value=\"\">Unassigned</option>\n {languages.map((language) => {\n const isCurrent = selectedLanguageCode === language.code\n const isLocalized = localizedLanguageCodes.has(language.code)\n const disabled =\n isCreation || !hasAssignedConfiguredLanguage\n ? isLocalized && !isCurrent\n : true\n\n return (\n <option disabled={disabled} key={language.code} value={language.code}>\n {language.flagLabel ? `${language.flagLabel} ` : ''}\n {language.name}\n </option>\n )\n })}\n </select>\n </div>\n </label>\n\n <div className=\"payload-multilang-translations\" data-testid=\"multilang-sidebar-translations\">\n <h4>Translations</h4>\n {languages.map((language) => {\n const translation = translationState?.translations[language.code]\n const translationID =\n typeof translation?.id === 'number' || typeof translation?.id === 'string'\n ? translation.id\n : undefined\n const isCurrent = isSameDocumentID(translationID, docID)\n const isUnavailableForCreate =\n isCreation && Boolean(translationID) && selectedLanguageCode !== language.code\n\n return (\n <div className=\"payload-multilang-translation-row\" key={language.code}>\n <span className=\"payload-multilang-translation-row__flag\">\n {language.flagLabel || language.code.toUpperCase()}\n </span>\n <span className=\"payload-multilang-translation-row__name\">{language.name}</span>\n {isCurrent ? (\n <span className=\"payload-multilang-translation-row__current\">Current</span>\n ) : isUnavailableForCreate ? (\n <span className=\"payload-multilang-translation-row__current\">Localized</span>\n ) : translationID ? (\n <button\n aria-label={`Edit ${language.name} translation`}\n className=\"payload-multilang-link-button\"\n disabled={isTransitioning}\n onClick={() => navigateToTranslation(translationID)}\n type=\"button\"\n >\n <PenIcon />\n </button>\n ) : (\n <button\n aria-label={`Create ${language.name} translation`}\n className=\"payload-multilang-icon-button\"\n disabled={!docID || busyLanguage === language.code || isTransitioning}\n onClick={() => void createTranslation(language.code)}\n type=\"button\"\n >\n +\n </button>\n )}\n </div>\n )\n })}\n {!docID ? (\n <div className=\"payload-multilang-muted\">Save before adding translations.</div>\n ) : null}\n </div>\n </section>\n )\n}\n"],"names":["toast","useConfig","useDocumentInfo","useField","useRouteTransition","useRouter","formatAdminURL","useEffect","useMemo","useState","getCollectionFieldNames","getConfiguredLanguagesFromConfig","getDefaultLanguageFromConfig","PenIcon","svg","aria-hidden","className","fill","height","viewBox","width","xmlns","path","d","stroke","strokeLinejoin","strokeWidth","strokeLinecap","getDocumentID","doc","id","undefined","isSameDocumentID","left","right","String","LanguageMetabox","config","collectionSlug","router","isTransitioning","startRouteTransition","fieldNames","languageField","language","languages","defaultLanguage","busyLanguage","setBusyLanguage","translationState","setTranslationState","groupField","group","docID","groupValue","value","isCreation","localizedLanguageCodes","Set","Object","keys","translations","configuredLanguageCodes","map","code","availableCreationLanguage","find","has","preferredCreationLanguage","setValue","loadState","query","encodeURIComponent","response","fetch","apiRoute","routes","api","credentials","ok","result","json","selectedLanguageCode","selectedLanguage","persistedLanguage","hasAssignedConfiguredLanguage","Boolean","collectionAdminURL","documentID","adminRoute","admin","navigateToTranslation","push","createTranslation","targetLanguage","error","body","JSON","stringify","sourceId","headers","method","catch","message","section","div","h3","label","span","flagLabel","select","disabled","onChange","event","target","option","isCurrent","isLocalized","name","data-testid","h4","translation","translationID","isUnavailableForCreate","toUpperCase","button","aria-label","onClick","type"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import '../styles/admin.css';
|
|
2
|
+
import type { MultilangLanguage, TranslationState } from '../types.js';
|
|
3
|
+
type Props = {
|
|
4
|
+
collectionSlug: string;
|
|
5
|
+
docID: number | string;
|
|
6
|
+
languages: MultilangLanguage[];
|
|
7
|
+
state: TranslationState;
|
|
8
|
+
};
|
|
9
|
+
export declare const TranslationActionsClient: ({ collectionSlug, docID, languages, state, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|