@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.
Files changed (43) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +50 -14
  3. package/dist/components/LanguageListToolbar.js +4 -3
  4. package/dist/components/LanguageListToolbar.js.map +1 -1
  5. package/dist/components/LanguageMetabox.js +18 -17
  6. package/dist/components/LanguageMetabox.js.map +1 -1
  7. package/dist/components/TranslationActionsClient.js +16 -14
  8. package/dist/components/TranslationActionsClient.js.map +1 -1
  9. package/dist/components/TranslationColumnCell.js +4 -1
  10. package/dist/components/TranslationColumnCell.js.map +1 -1
  11. package/dist/components/TranslationColumnCellClient.js +16 -7
  12. package/dist/components/TranslationColumnCellClient.js.map +1 -1
  13. package/dist/components/TranslationsTab.js +9 -8
  14. package/dist/components/TranslationsTab.js.map +1 -1
  15. package/dist/constants.d.ts +4 -1
  16. package/dist/constants.js +4 -1
  17. package/dist/constants.js.map +1 -1
  18. package/dist/endpoints/translations.js +4 -6
  19. package/dist/endpoints/translations.js.map +1 -1
  20. package/dist/hooks/translatedCollection.d.ts +2 -1
  21. package/dist/hooks/translatedCollection.js +122 -15
  22. package/dist/hooks/translatedCollection.js.map +1 -1
  23. package/dist/hooks/translatedGlobal.js +5 -1
  24. package/dist/hooks/translatedGlobal.js.map +1 -1
  25. package/dist/index.d.ts +4 -1
  26. package/dist/index.js +27 -3
  27. package/dist/index.js.map +1 -1
  28. package/dist/lib/config.js +2 -10
  29. package/dist/lib/config.js.map +1 -1
  30. package/dist/lib/data.d.ts +72 -35
  31. package/dist/lib/data.js +118 -60
  32. package/dist/lib/data.js.map +1 -1
  33. package/dist/translations.d.ts +72 -0
  34. package/dist/translations.js +72 -0
  35. package/dist/translations.js.map +1 -0
  36. package/dist/types.d.ts +0 -19
  37. package/dist/types.js.map +1 -1
  38. package/docs/configuration.md +75 -10
  39. package/docs/helpers.md +95 -121
  40. package/docs/usage.md +115 -120
  41. package/package.json +1 -1
  42. package/dist/payload-config.d.js +0 -2
  43. 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
- ## Get Translations for a Document
131
+ ## Localized Slug Lookups
77
132
 
78
- Use `getDocumentTranslations()` when you have a document ID and need every linked translation.
133
+ Use `localizedSlugQuery()` for the common language + slug + status lookup.
79
134
 
80
135
  ```ts
81
- import { getDocumentTranslations } from '@roxxel/payload-multilang'
136
+ import { localizedSlugQuery } from '@roxxel/payload-multilang'
82
137
 
83
- const state = await getDocumentTranslations({
138
+ const { docs } = await payload.find({
84
139
  collection: 'posts',
85
- id: post.id,
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
- Use `getDocumentTranslation()` when you only need one target language.
149
+ For draft previews:
104
150
 
105
151
  ```ts
106
- import { getDocumentTranslation } from '@roxxel/payload-multilang'
152
+ const query = localizedSlugQuery({
153
+ slug,
154
+ language,
155
+ status: 'draft',
156
+ })
107
157
 
108
- const ukrainianPost = await getDocumentTranslation({
158
+ await payload.find({
109
159
  collection: 'posts',
110
- id: post.id,
111
- language: 'uk',
160
+ limit: 1,
161
+ ...query,
112
162
  })
113
163
  ```
114
164
 
115
- ## Access Control in Server Code
165
+ ## Admin Preview URLs
116
166
 
117
- The document translation helpers resolve Payload from `@payload-config` and use the default Local API access behavior. When you need user-scoped collection reads, query with Payload directly and pass `overrideAccess: false`.
167
+ Use `getMultilangDocumentLanguage()` to derive the selected language from unsaved form data first, then the saved document.
118
168
 
119
169
  ```ts
120
- const { docs } = await req.payload.find({
121
- collection: 'posts',
122
- overrideAccess: false,
123
- req,
124
- where: withLanguage({
125
- language: 'uk',
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
- Then read one language as flat data:
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
- import { updateGlobalByLanguage } from '@roxxel/payload-multilang'
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
- ## Keep Shared Fields in Sync
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
- payloadMultilang({
192
- collections: {
193
- posts: {
194
- synchronizedFields: ['featuredImage', 'author', 'category'],
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
- When an editor changes one of those fields on a saved translation, the plugin updates the same field on the other linked translations.
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
- To duplicate most fields but exclude specific ones:
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
- payloadMultilang({
222
- collections: {
223
- posts: {
224
- duplicateExcludeFields: ['slug', 'seoTitle', 'seoDescription'],
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
- Adjust the URL shape to match your frontend routing.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roxxel/payload-multilang",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Polylang-style document localization for Payload CMS",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,2 +0,0 @@
1
-
2
- //# sourceMappingURL=payload-config.d.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/payload-config.d.ts"],"names":[],"mappings":""}