@roxxel/payload-multilang 0.0.4 → 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/LICENSE.md +7 -0
- package/README.md +50 -14
- 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 +4 -1
- package/dist/constants.js +4 -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 +4 -1
- package/dist/index.js +27 -3
- 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 +72 -35
- package/dist/lib/data.js +118 -60
- 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 +75 -10
- package/docs/helpers.md +95 -121
- package/docs/usage.md +115 -120
- package/package.json +1 -1
- package/dist/payload-config.d.js +0 -2
- package/dist/payload-config.d.js.map +0 -1
package/docs/usage.md
CHANGED
|
@@ -27,14 +27,69 @@ payloadMultilang({
|
|
|
27
27
|
|
|
28
28
|
Editors can then choose the document language in the sidebar and create or connect translations from the collection list, document sidebar, or `Translations` edit tab.
|
|
29
29
|
|
|
30
|
+
## App-Level Convenience Helpers
|
|
31
|
+
|
|
32
|
+
Create one app-owned helper module when you want short helper calls in RSCs, route handlers, or other trusted server code.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// src/lib/multilang.ts
|
|
36
|
+
import { createMultilangHelpers } from '@roxxel/payload-multilang'
|
|
37
|
+
import { getPayload } from 'payload'
|
|
38
|
+
import config from '@payload-config'
|
|
39
|
+
|
|
40
|
+
export const multilang = createMultilangHelpers({
|
|
41
|
+
getPayload: () => getPayload({ config }),
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then use it from your app:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { multilang } from '@/lib/multilang'
|
|
49
|
+
|
|
50
|
+
export const LanguageSwitcher = async ({ id }: { id: string }) => {
|
|
51
|
+
const state = await multilang.getDocumentTranslations<{ id: string; slug?: string }>({
|
|
52
|
+
collection: 'posts',
|
|
53
|
+
id,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<nav>
|
|
58
|
+
{Object.entries(state.translations).map(([language, doc]) => (
|
|
59
|
+
<a href={`/${language}/posts/${doc.slug || doc.id}`} key={language}>
|
|
60
|
+
{language.toUpperCase()}
|
|
61
|
+
</a>
|
|
62
|
+
))}
|
|
63
|
+
</nav>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Request-Scoped Code
|
|
69
|
+
|
|
70
|
+
Use `WithPayload` helpers when you already have `req.payload` or need user-scoped access control.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { getDocumentTranslationsWithPayload } from '@roxxel/payload-multilang'
|
|
74
|
+
|
|
75
|
+
export const getPostTranslations = async (req: PayloadRequest, id: string) =>
|
|
76
|
+
getDocumentTranslationsWithPayload({
|
|
77
|
+
collection: 'posts',
|
|
78
|
+
id,
|
|
79
|
+
payload: req.payload,
|
|
80
|
+
req,
|
|
81
|
+
overrideAccess: false,
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
30
85
|
## Query Documents for One Language
|
|
31
86
|
|
|
32
87
|
Use `withLanguage()` with the Local API.
|
|
33
88
|
|
|
34
89
|
```ts
|
|
90
|
+
import { withLanguage } from '@roxxel/payload-multilang'
|
|
35
91
|
import { getPayload } from 'payload'
|
|
36
92
|
import config from '@payload-config'
|
|
37
|
-
import { withLanguage } from '@roxxel/payload-multilang'
|
|
38
93
|
|
|
39
94
|
export const getPosts = async (language: string) => {
|
|
40
95
|
const payload = await getPayload({ config })
|
|
@@ -73,62 +128,64 @@ const where = withLanguage({
|
|
|
73
128
|
})
|
|
74
129
|
```
|
|
75
130
|
|
|
76
|
-
##
|
|
131
|
+
## Localized Slug Lookups
|
|
77
132
|
|
|
78
|
-
Use `
|
|
133
|
+
Use `localizedSlugQuery()` for the common language + slug + status lookup.
|
|
79
134
|
|
|
80
135
|
```ts
|
|
81
|
-
import {
|
|
136
|
+
import { localizedSlugQuery } from '@roxxel/payload-multilang'
|
|
82
137
|
|
|
83
|
-
const
|
|
138
|
+
const { docs } = await payload.find({
|
|
84
139
|
collection: 'posts',
|
|
85
|
-
|
|
140
|
+
limit: 1,
|
|
141
|
+
...localizedSlugQuery({
|
|
142
|
+
slug: 'hello-world',
|
|
143
|
+
language: 'uk',
|
|
144
|
+
status: 'published',
|
|
145
|
+
}),
|
|
86
146
|
})
|
|
87
|
-
|
|
88
|
-
const english = state.translations.en
|
|
89
|
-
const ukrainian = state.translations.uk
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
The returned object is keyed by language code:
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
type TranslationState<TDoc> = {
|
|
96
|
-
group?: string
|
|
97
|
-
language?: string
|
|
98
|
-
source?: TDoc
|
|
99
|
-
translations: Record<string, TDoc>
|
|
100
|
-
}
|
|
101
147
|
```
|
|
102
148
|
|
|
103
|
-
|
|
149
|
+
For draft previews:
|
|
104
150
|
|
|
105
151
|
```ts
|
|
106
|
-
|
|
152
|
+
const query = localizedSlugQuery({
|
|
153
|
+
slug,
|
|
154
|
+
language,
|
|
155
|
+
status: 'draft',
|
|
156
|
+
})
|
|
107
157
|
|
|
108
|
-
|
|
158
|
+
await payload.find({
|
|
109
159
|
collection: 'posts',
|
|
110
|
-
|
|
111
|
-
|
|
160
|
+
limit: 1,
|
|
161
|
+
...query,
|
|
112
162
|
})
|
|
113
163
|
```
|
|
114
164
|
|
|
115
|
-
##
|
|
165
|
+
## Admin Preview URLs
|
|
116
166
|
|
|
117
|
-
|
|
167
|
+
Use `getMultilangDocumentLanguage()` to derive the selected language from unsaved form data first, then the saved document.
|
|
118
168
|
|
|
119
169
|
```ts
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
170
|
+
import { getMultilangDocumentLanguage } from '@roxxel/payload-multilang'
|
|
171
|
+
|
|
172
|
+
export const Posts = {
|
|
173
|
+
slug: 'posts',
|
|
174
|
+
admin: {
|
|
175
|
+
preview: ({ data, doc }) => {
|
|
176
|
+
const language = getMultilangDocumentLanguage({
|
|
177
|
+
data,
|
|
178
|
+
doc,
|
|
179
|
+
fallback: 'en',
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
return `/${language}/posts/${data?.slug || doc?.slug}`
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
fields: [],
|
|
186
|
+
}
|
|
128
187
|
```
|
|
129
188
|
|
|
130
|
-
Use the global `WithPayload` helpers when you need request-aware access control for localized globals.
|
|
131
|
-
|
|
132
189
|
## Enable a Global
|
|
133
190
|
|
|
134
191
|
Add the global slug to the plugin config.
|
|
@@ -142,30 +199,12 @@ payloadMultilang({
|
|
|
142
199
|
})
|
|
143
200
|
```
|
|
144
201
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
```ts
|
|
148
|
-
import { findGlobalByLanguage } from '@roxxel/payload-multilang'
|
|
149
|
-
|
|
150
|
-
const settings = await findGlobalByLanguage({
|
|
151
|
-
slug: 'site-settings',
|
|
152
|
-
language: 'uk',
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
console.log(settings.title)
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Update one language without replacing other language values:
|
|
202
|
+
Read one language as flat data:
|
|
159
203
|
|
|
160
204
|
```ts
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
await updateGlobalByLanguage({
|
|
205
|
+
const settings = await multilang.findGlobalByLanguage({
|
|
164
206
|
slug: 'site-settings',
|
|
165
207
|
language: 'uk',
|
|
166
|
-
data: {
|
|
167
|
-
title: 'Ukrainian title',
|
|
168
|
-
},
|
|
169
208
|
})
|
|
170
209
|
```
|
|
171
210
|
|
|
@@ -177,88 +216,44 @@ import { findGlobalByLanguageWithPayload } from '@roxxel/payload-multilang'
|
|
|
177
216
|
const settings = await findGlobalByLanguageWithPayload({
|
|
178
217
|
slug: 'site-settings',
|
|
179
218
|
language: 'uk',
|
|
180
|
-
overrideAccess: false,
|
|
181
219
|
payload: req.payload,
|
|
182
220
|
req,
|
|
221
|
+
overrideAccess: false,
|
|
183
222
|
})
|
|
184
223
|
```
|
|
185
224
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Use `synchronizedFields` for top-level fields that should be identical across translations, such as author, featured image, or canonical taxonomy.
|
|
225
|
+
Update one language without replacing other language values:
|
|
189
226
|
|
|
190
227
|
```ts
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
228
|
+
await multilang.updateGlobalByLanguage({
|
|
229
|
+
slug: 'site-settings',
|
|
230
|
+
language: 'uk',
|
|
231
|
+
data: {
|
|
232
|
+
title: 'Ukrainian title',
|
|
196
233
|
},
|
|
197
|
-
languages,
|
|
198
234
|
})
|
|
199
235
|
```
|
|
200
236
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
## Create Empty Translations
|
|
204
|
-
|
|
205
|
-
By default, creating a translation duplicates compatible top-level source fields. To make new translations start empty:
|
|
206
|
-
|
|
207
|
-
```ts
|
|
208
|
-
payloadMultilang({
|
|
209
|
-
collections: {
|
|
210
|
-
posts: {
|
|
211
|
-
duplicate: false,
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
languages,
|
|
215
|
-
})
|
|
216
|
-
```
|
|
237
|
+
## Keep Shared Fields in Sync
|
|
217
238
|
|
|
218
|
-
|
|
239
|
+
Set `custom.multilang.synchronize` on top-level fields that should be identical across translations, such as author, featured image, or canonical taxonomy.
|
|
219
240
|
|
|
220
241
|
```ts
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
242
|
+
{
|
|
243
|
+
name: 'featuredImage',
|
|
244
|
+
type: 'upload',
|
|
245
|
+
relationTo: 'media',
|
|
246
|
+
custom: {
|
|
247
|
+
multilang: {
|
|
248
|
+
synchronize: true,
|
|
225
249
|
},
|
|
226
250
|
},
|
|
227
|
-
languages,
|
|
228
|
-
})
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
## Build Language Switchers
|
|
232
|
-
|
|
233
|
-
Use `getDocumentTranslations()` to render links to available translations.
|
|
234
|
-
|
|
235
|
-
```tsx
|
|
236
|
-
import { getDocumentTranslations } from '@roxxel/payload-multilang'
|
|
237
|
-
|
|
238
|
-
export const LanguageSwitcher = async ({ collection, id }: { collection: 'posts'; id: string }) => {
|
|
239
|
-
type PostLink = {
|
|
240
|
-
id?: number | string
|
|
241
|
-
slug?: string
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const state = await getDocumentTranslations<PostLink>({
|
|
245
|
-
collection,
|
|
246
|
-
id,
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
<nav>
|
|
251
|
-
{Object.entries(state.translations).map(([language, doc]) => (
|
|
252
|
-
<a href={`/${language}/posts/${doc.slug || doc.id}`} key={language}>
|
|
253
|
-
{language.toUpperCase()}
|
|
254
|
-
</a>
|
|
255
|
-
))}
|
|
256
|
-
</nav>
|
|
257
|
-
)
|
|
258
251
|
}
|
|
259
252
|
```
|
|
260
253
|
|
|
261
|
-
|
|
254
|
+
When an editor changes one of those fields on a saved translation, the plugin updates the same field on the other linked translations.
|
|
255
|
+
|
|
256
|
+
For `array` and `blocks` fields, synchronization keeps the outer shell aligned only. The row IDs, order, block types, and block names sync across translations, but nested field values inside each row or block stay language-specific.
|
|
262
257
|
|
|
263
258
|
## Generate Types
|
|
264
259
|
|
package/package.json
CHANGED
package/dist/payload-config.d.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/payload-config.d.ts"],"names":[],"mappings":""}
|