@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/translations.ts"],"sourcesContent":["export const payloadMultilangTranslations = {\n en: {\n payloadMultilang: {\n allLanguages: 'All languages',\n chooseLanguage: 'Choose language',\n chooseTargetLanguage: 'Choose a target language.',\n connectExisting: 'Connect existing',\n createOrConnectDocuments: 'Create or connect separate localized documents.',\n createTranslation: 'Create translation',\n createTranslationFor: 'Create {{language}} translation',\n current: 'Current',\n disconnectDocument: 'Disconnect this document',\n documentID: 'Document ID',\n editTranslationFor: 'Edit {{language}} translation',\n existingDocumentID: 'Existing document ID',\n language: 'Language',\n linkedDocuments: 'Linked documents',\n localized: 'Localized',\n localizedContent: 'Localized content',\n missing: 'Missing',\n saveBeforeAddingTranslations: 'Save before adding translations.',\n saveBeforeCreatingTranslations: 'Save the document before creating translations.',\n saveBeforeManagingTranslations: 'Save the document before managing translations.',\n targetIDAndLanguageRequired: 'Target ID and language are required.',\n translationActions: 'Translation actions',\n translationConnected: 'Translation connected.',\n translationCouldNotBeConnected: 'Translation could not be connected.',\n translationCouldNotBeCreated: 'Translation could not be created.',\n translationCouldNotBeDisconnected: 'Translation could not be disconnected.',\n translationDisconnected: 'Translation disconnected.',\n translations: 'Translations',\n unassigned: 'Unassigned',\n unassignedStoredLanguageNotConfigured:\n 'Unassigned; stored language \"{{language}}\" is not configured',\n },\n },\n uk: {\n payloadMultilang: {\n allLanguages: 'Усі мови',\n chooseLanguage: 'Оберіть мову',\n chooseTargetLanguage: 'Оберіть цільову мову.',\n connectExisting: 'Підключити наявний',\n createOrConnectDocuments: 'Створюйте або підключайте окремі локалізовані документи.',\n createTranslation: 'Створити переклад',\n createTranslationFor: 'Створити переклад: {{language}}',\n current: 'Поточний',\n disconnectDocument: 'Від’єднати цей документ',\n documentID: 'ID документа',\n editTranslationFor: 'Редагувати переклад: {{language}}',\n existingDocumentID: 'ID наявного документа',\n language: 'Мова',\n linkedDocuments: 'Пов’язані документи',\n localized: 'Локалізовано',\n localizedContent: 'Локалізований вміст',\n missing: 'Відсутній',\n saveBeforeAddingTranslations: 'Збережіть перед додаванням перекладів.',\n saveBeforeCreatingTranslations: 'Збережіть документ перед створенням перекладів.',\n saveBeforeManagingTranslations: 'Збережіть документ перед керуванням перекладами.',\n targetIDAndLanguageRequired: 'Потрібні ID цільового документа та мова.',\n translationActions: 'Дії з перекладами',\n translationConnected: 'Переклад підключено.',\n translationCouldNotBeConnected: 'Не вдалося підключити переклад.',\n translationCouldNotBeCreated: 'Не вдалося створити переклад.',\n translationCouldNotBeDisconnected: 'Не вдалося від’єднати переклад.',\n translationDisconnected: 'Переклад від’єднано.',\n translations: 'Переклади',\n unassigned: 'Не призначено',\n unassignedStoredLanguageNotConfigured:\n 'Не призначено; збережена мова \"{{language}}\" не налаштована',\n },\n },\n} as const\n\nexport type PayloadMultilangTranslations =\n (typeof payloadMultilangTranslations)['en']\n\nexport type PayloadMultilangTranslationKey =\n `payloadMultilang:${keyof PayloadMultilangTranslations['payloadMultilang']}`\n"],"names":["payloadMultilangTranslations","en","payloadMultilang","allLanguages","chooseLanguage","chooseTargetLanguage","connectExisting","createOrConnectDocuments","createTranslation","createTranslationFor","current","disconnectDocument","documentID","editTranslationFor","existingDocumentID","language","linkedDocuments","localized","localizedContent","missing","saveBeforeAddingTranslations","saveBeforeCreatingTranslations","saveBeforeManagingTranslations","targetIDAndLanguageRequired","translationActions","translationConnected","translationCouldNotBeConnected","translationCouldNotBeCreated","translationCouldNotBeDisconnected","translationDisconnected","translations","unassigned","unassignedStoredLanguageNotConfigured","uk"],"mappings":"AAAA,OAAO,MAAMA,+BAA+B;IAC1CC,IAAI;QACFC,kBAAkB;YAChBC,cAAc;YACdC,gBAAgB;YAChBC,sBAAsB;YACtBC,iBAAiB;YACjBC,0BAA0B;YAC1BC,mBAAmB;YACnBC,sBAAsB;YACtBC,SAAS;YACTC,oBAAoB;YACpBC,YAAY;YACZC,oBAAoB;YACpBC,oBAAoB;YACpBC,UAAU;YACVC,iBAAiB;YACjBC,WAAW;YACXC,kBAAkB;YAClBC,SAAS;YACTC,8BAA8B;YAC9BC,gCAAgC;YAChCC,gCAAgC;YAChCC,6BAA6B;YAC7BC,oBAAoB;YACpBC,sBAAsB;YACtBC,gCAAgC;YAChCC,8BAA8B;YAC9BC,mCAAmC;YACnCC,yBAAyB;YACzBC,cAAc;YACdC,YAAY;YACZC,uCACE;QACJ;IACF;IACAC,IAAI;QACF/B,kBAAkB;YAChBC,cAAc;YACdC,gBAAgB;YAChBC,sBAAsB;YACtBC,iBAAiB;YACjBC,0BAA0B;YAC1BC,mBAAmB;YACnBC,sBAAsB;YACtBC,SAAS;YACTC,oBAAoB;YACpBC,YAAY;YACZC,oBAAoB;YACpBC,oBAAoB;YACpBC,UAAU;YACVC,iBAAiB;YACjBC,WAAW;YACXC,kBAAkB;YAClBC,SAAS;YACTC,8BAA8B;YAC9BC,gCAAgC;YAChCC,gCAAgC;YAChCC,6BAA6B;YAC7BC,oBAAoB;YACpBC,sBAAsB;YACtBC,gCAAgC;YAChCC,8BAA8B;YAC9BC,mCAAmC;YACnCC,yBAAyB;YACzBC,cAAc;YACdC,YAAY;YACZC,uCACE;QACJ;IACF;AACF,EAAU"}
package/dist/types.d.ts CHANGED
@@ -17,22 +17,10 @@ export type MultilangLanguage = {
17
17
  order?: number;
18
18
  };
19
19
  export type MultilangCollectionOptions = {
20
- /**
21
- * Set to false to create empty translation documents by default.
22
- */
23
- duplicate?: boolean;
24
- /**
25
- * Additional top-level document field names that should not be copied when creating a translation.
26
- */
27
- duplicateExcludeFields?: string[];
28
20
  /**
29
21
  * Override hidden metadata field names for this collection.
30
22
  */
31
23
  fields?: Partial<MultilangFieldNames>;
32
- /**
33
- * Top-level document field names that should share one value across every language in a translation group.
34
- */
35
- synchronizedFields?: string[];
36
24
  };
37
25
  export type MultilangGlobalOptions = {
38
26
  /**
@@ -49,10 +37,6 @@ export type PayloadMultilangConfig = {
49
37
  * Disable runtime UI/endpoints/hooks while keeping schema fields in place.
50
38
  */
51
39
  disabled?: boolean;
52
- /**
53
- * Global duplicate exclusions applied to all enabled collections.
54
- */
55
- duplicateExcludeFields?: string[];
56
40
  /**
57
41
  * Global hidden metadata field name overrides.
58
42
  */
@@ -69,12 +53,9 @@ export type PayloadMultilangConfig = {
69
53
  };
70
54
  export type ResolvedMultilangCollection = {
71
55
  defaultLanguage: MultilangLanguage;
72
- duplicate: boolean;
73
- duplicateExcludeFields: string[];
74
56
  fieldNames: MultilangFieldNames;
75
57
  languages: MultilangLanguage[];
76
58
  slug: string;
77
- synchronizedFields: string[];
78
59
  };
79
60
  export type ResolvedMultilangGlobal = {
80
61
  defaultLanguage: MultilangLanguage;
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, GlobalSlug, Where } from 'payload'\n\nexport type MultilangDirection = 'ltr' | 'rtl'\n\nexport type MultilangFieldNames = {\n group: string\n language: string\n meta: string\n}\n\nexport type MultilangLanguage = {\n active?: boolean\n code: string\n direction?: MultilangDirection\n flagLabel?: string\n id?: number | string\n isDefault?: boolean\n locale?: string\n name: string\n order?: number\n}\n\nexport type MultilangCollectionOptions = {\n /**\n * Set to false to create empty translation documents by default.\n */\n duplicate?: boolean\n /**\n * Additional top-level document field names that should not be copied when creating a translation.\n */\n duplicateExcludeFields?: string[]\n /**\n * Override hidden metadata field names for this collection.\n */\n fields?: Partial<MultilangFieldNames>\n /**\n * Top-level document field names that should share one value across every language in a translation group.\n */\n synchronizedFields?: string[]\n}\n\nexport type MultilangGlobalOptions = {\n /**\n * Override the top-level tabs field label used to group localized global fields.\n */\n label?: string\n}\n\nexport type PayloadMultilangConfig = {\n /**\n * Payload collections that should use Polylang-style document translations.\n */\n collections?: Partial<Record<CollectionSlug, MultilangCollectionOptions | true>>\n /**\n * Disable runtime UI/endpoints/hooks while keeping schema fields in place.\n */\n disabled?: boolean\n /**\n * Global duplicate exclusions applied to all enabled collections.\n */\n duplicateExcludeFields?: string[]\n /**\n * Global hidden metadata field name overrides.\n */\n fieldNames?: Partial<MultilangFieldNames>\n /**\n * Payload globals that should store language-specific values in one singleton document.\n */\n globals?: Partial<Record<GlobalSlug, MultilangGlobalOptions | true>>\n /**\n * Languages available to translated collections. Codes are normalized to\n * lowercase during plugin setup.\n */\n languages: MultilangLanguage[]\n}\n\nexport type ResolvedMultilangCollection = {\n defaultLanguage: MultilangLanguage\n duplicate: boolean\n duplicateExcludeFields: string[]\n fieldNames: MultilangFieldNames\n languages: MultilangLanguage[]\n slug: string\n synchronizedFields: string[]\n}\n\nexport type ResolvedMultilangGlobal = {\n defaultLanguage: MultilangLanguage\n label: string\n languages: MultilangLanguage[]\n slug: string\n}\n\nexport type TranslationMap<TDoc = Record<string, unknown>> = Record<string, TDoc>\n\nexport type TranslationState<TDoc = Record<string, unknown>> = {\n group?: string\n language?: string\n source?: TDoc\n translations: TranslationMap<TDoc>\n}\n\nexport type WithLanguageArgs = {\n fieldName?: string\n language: string\n where?: Where\n}\n"],"names":[],"mappings":"AAsGA,WAIC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, GlobalSlug, Where } from 'payload'\n\nexport type MultilangDirection = 'ltr' | 'rtl'\n\nexport type MultilangFieldNames = {\n group: string\n language: string\n meta: string\n}\n\nexport type MultilangLanguage = {\n active?: boolean\n code: string\n direction?: MultilangDirection\n flagLabel?: string\n id?: number | string\n isDefault?: boolean\n locale?: string\n name: string\n order?: number\n}\n\nexport type MultilangCollectionOptions = {\n /**\n * Override hidden metadata field names for this collection.\n */\n fields?: Partial<MultilangFieldNames>\n}\n\nexport type MultilangGlobalOptions = {\n /**\n * Override the top-level tabs field label used to group localized global fields.\n */\n label?: string\n}\n\nexport type PayloadMultilangConfig = {\n /**\n * Payload collections that should use Polylang-style document translations.\n */\n collections?: Partial<Record<CollectionSlug, MultilangCollectionOptions | true>>\n /**\n * Disable runtime UI/endpoints/hooks while keeping schema fields in place.\n */\n disabled?: boolean\n /**\n * Global hidden metadata field name overrides.\n */\n fieldNames?: Partial<MultilangFieldNames>\n /**\n * Payload globals that should store language-specific values in one singleton document.\n */\n globals?: Partial<Record<GlobalSlug, MultilangGlobalOptions | true>>\n /**\n * Languages available to translated collections. Codes are normalized to\n * lowercase during plugin setup.\n */\n languages: MultilangLanguage[]\n}\n\nexport type ResolvedMultilangCollection = {\n defaultLanguage: MultilangLanguage\n fieldNames: MultilangFieldNames\n languages: MultilangLanguage[]\n slug: string\n}\n\nexport type ResolvedMultilangGlobal = {\n defaultLanguage: MultilangLanguage\n label: string\n languages: MultilangLanguage[]\n slug: string\n}\n\nexport type TranslationMap<TDoc = Record<string, unknown>> = Record<string, TDoc>\n\nexport type TranslationState<TDoc = Record<string, unknown>> = {\n group?: string\n language?: string\n source?: TDoc\n translations: TranslationMap<TDoc>\n}\n\nexport type WithLanguageArgs = {\n fieldName?: string\n language: string\n where?: Where\n}\n"],"names":[],"mappings":"AAmFA,WAIC"}
@@ -32,6 +32,8 @@ export default buildConfig({
32
32
  })
33
33
  ```
34
34
 
35
+ Payload built-in localization must stay disabled. This plugin models translations as separate documents linked by metadata fields, so `localized: true` fields are not the intended model. If `config.localization` is enabled, `payloadMultilang()` throws during config setup.
36
+
35
37
  ## Plugin Options
36
38
 
37
39
  | Option | Type | Description |
@@ -40,9 +42,47 @@ export default buildConfig({
40
42
  | `collections` | `Record<string, true \| MultilangCollectionOptions>` | Collection slugs to localize. |
41
43
  | `globals` | `Record<string, true \| MultilangGlobalOptions>` | Global slugs to localize. |
42
44
  | `fieldNames` | `Partial<MultilangFieldNames>` | Optional custom field names used by localized collections. |
43
- | `duplicateExcludeFields` | `string[]` | Extra top-level fields to skip when a new translation duplicates source document data. |
44
45
  | `disabled` | `boolean` | Returns the Payload config unchanged when true. Useful for temporary opt-out in specific environments. |
45
46
 
47
+ ## Content Model
48
+
49
+ Translated collection entries are independent Payload documents. The plugin adds hidden metadata fields:
50
+
51
+ ```ts
52
+ {
53
+ language: '_multilangLanguage',
54
+ group: '_multilangGroup',
55
+ meta: '_multilangMeta',
56
+ }
57
+ ```
58
+
59
+ Do not mark content fields as `localized: true` for this plugin model. Put translated text directly on each language document instead.
60
+
61
+ ## Admin UI Translations
62
+
63
+ The plugin registers its own admin UI translations for English (`en`) and Ukrainian (`uk`) through Payload `i18n.translations`.
64
+
65
+ Apps can override any plugin string by setting the same translation keys after installing the plugin:
66
+
67
+ ```ts
68
+ export default buildConfig({
69
+ i18n: {
70
+ translations: {
71
+ uk: {
72
+ payloadMultilang: {
73
+ translations: 'Переклади',
74
+ },
75
+ },
76
+ },
77
+ },
78
+ plugins: [
79
+ payloadMultilang({
80
+ languages,
81
+ }),
82
+ ],
83
+ })
84
+ ```
85
+
46
86
  ## Languages
47
87
 
48
88
  ```ts
@@ -101,11 +141,7 @@ Use collection options when you need custom translation behavior.
101
141
  ```ts
102
142
  payloadMultilang({
103
143
  collections: {
104
- posts: {
105
- duplicate: true,
106
- duplicateExcludeFields: ['legacyId'],
107
- synchronizedFields: ['featuredImage', 'author'],
108
- },
144
+ posts: true,
109
145
  },
110
146
  languages,
111
147
  })
@@ -113,10 +149,24 @@ payloadMultilang({
113
149
 
114
150
  | Option | Type | Description |
115
151
  | ------------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
116
- | `duplicate` | `boolean` | Defaults to `true`. When creating a translation, copy source document fields into the new document. Set to `false` to create empty translation documents by default. |
117
- | `duplicateExcludeFields` | `string[]` | Additional top-level fields to skip during duplication for this collection. |
118
152
  | `fields` | `Partial<MultilangFieldNames>` | Override metadata field names for this collection. |
119
- | `synchronizedFields` | `string[]` | Top-level fields that should stay the same across all translations in a group. |
153
+
154
+ Mark fields that should stay shared across translations directly in the field config:
155
+
156
+ ```ts
157
+ {
158
+ name: 'featuredImage',
159
+ type: 'upload',
160
+ relationTo: 'media',
161
+ custom: {
162
+ multilang: {
163
+ synchronize: true,
164
+ },
165
+ },
166
+ }
167
+ ```
168
+
169
+ For repeatable fields with child fields, such as `array` and `blocks`, synchronization only applies to the outer shell. Item IDs, order, block types, and block names follow the source document, while each translation keeps its own nested field values.
120
170
 
121
171
  ## Globals
122
172
 
@@ -144,7 +194,7 @@ payloadMultilang({
144
194
  })
145
195
  ```
146
196
 
147
- Read and update localized global values with `findGlobalByLanguage` and `updateGlobalByLanguage`.
197
+ Read and update localized global values with `findGlobalByLanguageWithPayload`, `updateGlobalByLanguageWithPayload`, or helpers from `createMultilangHelpers()`.
148
198
 
149
199
  ## Custom Field Names
150
200
 
@@ -190,3 +240,18 @@ payloadMultilang({
190
240
  ```
191
241
 
192
242
  When using custom field names, pass the same names to helpers that accept `fieldNames` or `fieldName`.
243
+
244
+ ## Slug Uniqueness
245
+
246
+ Because translations are separate documents, decide whether slugs are unique globally or per language.
247
+
248
+ If every translated document has a different slug, a normal `unique: true` slug field works. If translations may share the same slug across languages, do not use global `unique: true`; enforce uniqueness with a compound database index or a custom validation rule scoped to:
249
+
250
+ ```ts
251
+ {
252
+ _multilangLanguage: language,
253
+ slug,
254
+ }
255
+ ```
256
+
257
+ Use `localizedSlugQuery()` for common language + slug lookups in frontend routes.
package/docs/helpers.md CHANGED
@@ -2,33 +2,52 @@
2
2
 
3
3
  Import helpers from `@roxxel/payload-multilang`.
4
4
 
5
+ ## Request-Scoped Helpers
6
+
7
+ Use `WithPayload` helpers in endpoints, hooks, route handlers, and code running on behalf of a user.
8
+
5
9
  ```ts
6
10
  import {
7
- findGlobalByLanguage,
8
11
  findGlobalByLanguageWithPayload,
9
- getDocumentTranslation,
10
- getDocumentTranslations,
11
- getLanguages,
12
- updateGlobalByLanguage,
12
+ getDocumentTranslationWithPayload,
13
+ getDocumentTranslationsWithPayload,
14
+ getGroupTranslationsWithPayload,
13
15
  updateGlobalByLanguageWithPayload,
14
- withLanguage,
15
16
  } from '@roxxel/payload-multilang'
17
+
18
+ const state = await getDocumentTranslationsWithPayload({
19
+ collection: 'posts',
20
+ id: post.id,
21
+ payload: req.payload,
22
+ req,
23
+ overrideAccess: false,
24
+ })
16
25
  ```
17
26
 
18
- ## `getLanguages`
27
+ `overrideAccess` defaults to `true`, matching Payload Local API defaults. Set it to `false` and pass `req` when enforcing request user access.
28
+
29
+ ## `createMultilangHelpers`
19
30
 
20
- Returns the languages configured for the plugin.
31
+ Use a tiny app-owned module for convenience helpers.
21
32
 
22
33
  ```ts
23
- const languages = await getLanguages()
34
+ import { createMultilangHelpers } from '@roxxel/payload-multilang'
35
+ import { getPayload } from 'payload'
36
+ import config from '@payload-config'
37
+
38
+ export const multilang = createMultilangHelpers({
39
+ getPayload: () => getPayload({ config }),
40
+ })
24
41
  ```
25
42
 
26
- You can also pass known languages or a Payload config-like object.
43
+ The factory returns:
27
44
 
28
45
  ```ts
29
- const languages = await getLanguages({
30
- config: payload.config,
31
- })
46
+ multilang.getLanguages()
47
+ multilang.getDocumentTranslations(args)
48
+ multilang.getDocumentTranslation(args)
49
+ multilang.findGlobalByLanguage(args)
50
+ multilang.updateGlobalByLanguage(args)
32
51
  ```
33
52
 
34
53
  ## `withLanguage`
@@ -36,6 +55,8 @@ const languages = await getLanguages({
36
55
  Adds a language constraint to a Payload `where` query.
37
56
 
38
57
  ```ts
58
+ import { withLanguage } from '@roxxel/payload-multilang'
59
+
39
60
  const where = withLanguage({
40
61
  language: 'uk',
41
62
  where: {
@@ -65,37 +86,31 @@ Result:
65
86
  }
66
87
  ```
67
88
 
68
- Signature:
69
-
70
- ```ts
71
- withLanguage(args: {
72
- fieldName?: string
73
- language: string
74
- where?: Where
75
- }): Where
76
- ```
77
-
78
89
  Use `fieldName` if you configured a custom collection language field.
79
90
 
80
- ## `getDocumentTranslations`
91
+ ## Document Helpers
81
92
 
82
- Returns the source document and all linked translations for a localized collection document.
93
+ `getDocumentTranslationsWithPayload` returns the source document and all linked translations for a localized collection document.
83
94
 
84
95
  ```ts
85
- const state = await getDocumentTranslations<Post>({
96
+ const state = await getDocumentTranslationsWithPayload<Post>({
86
97
  collection: 'posts',
87
98
  id: post.id,
99
+ payload: req.payload,
100
+ req,
101
+ overrideAccess: false,
88
102
  })
89
103
  ```
90
104
 
91
- Signature:
105
+ `getDocumentTranslationWithPayload` returns one target-language document.
92
106
 
93
107
  ```ts
94
- getDocumentTranslations<TDoc>(args: {
95
- collection: CollectionSlug
96
- fieldNames?: MultilangFieldNames
97
- id: number | string
98
- }): Promise<TranslationState<TDoc>>
108
+ const ukrainianPost = await getDocumentTranslationWithPayload<Post>({
109
+ collection: 'posts',
110
+ id: post.id,
111
+ language: 'uk',
112
+ payload: req.payload,
113
+ })
99
114
  ```
100
115
 
101
116
  Return shape:
@@ -109,123 +124,82 @@ type TranslationState<TDoc = Record<string, unknown>> = {
109
124
  }
110
125
  ```
111
126
 
112
- ## `getDocumentTranslation`
127
+ ## Global Helpers
113
128
 
114
- Returns one target-language document from the source document's translation group.
129
+ Localized globals store one singleton document with one top-level tab per language. These helpers read or update one tab and return flat data for that language.
115
130
 
116
131
  ```ts
117
- const ukrainianPost = await getDocumentTranslation<Post>({
118
- collection: 'posts',
119
- id: post.id,
132
+ const settings = await findGlobalByLanguageWithPayload<SiteSettings>({
133
+ slug: 'site-settings',
120
134
  language: 'uk',
135
+ payload: req.payload,
136
+ req,
137
+ overrideAccess: false,
121
138
  })
122
- ```
123
-
124
- Signature:
125
-
126
- ```ts
127
- getDocumentTranslation<TDoc>(args: {
128
- collection: CollectionSlug
129
- fieldNames?: MultilangFieldNames
130
- id: number | string
131
- language: string
132
- }): Promise<TDoc | undefined>
133
- ```
134
-
135
- ## `findGlobalByLanguage`
136
-
137
- Reads one language from a localized global and returns flat data for that language.
138
139
 
139
- ```ts
140
- const settings = await findGlobalByLanguage<SiteSettings>({
140
+ await updateGlobalByLanguageWithPayload<SiteSettings>({
141
141
  slug: 'site-settings',
142
142
  language: 'uk',
143
+ payload: req.payload,
144
+ data: {
145
+ title: 'Updated title',
146
+ },
143
147
  })
144
148
  ```
145
149
 
146
- Signature:
147
-
148
- ```ts
149
- findGlobalByLanguage<TDoc>(args: {
150
- depth?: number
151
- language: string
152
- slug: GlobalSlug
153
- }): Promise<TDoc>
154
- ```
155
-
156
- ## `updateGlobalByLanguage`
150
+ ## Constants
157
151
 
158
- Updates one language in a localized global.
152
+ Default metadata field names are exported so apps do not need to hardcode them.
159
153
 
160
154
  ```ts
161
- const settings = await updateGlobalByLanguage<SiteSettings>({
162
- slug: 'site-settings',
163
- language: 'uk',
164
- data: {
165
- title: 'Ukrainian title',
166
- },
167
- })
155
+ import {
156
+ DEFAULT_FIELD_NAMES,
157
+ MULTILANG_GROUP_FIELD,
158
+ MULTILANG_LANGUAGE_FIELD,
159
+ MULTILANG_META_FIELD,
160
+ } from '@roxxel/payload-multilang'
168
161
  ```
169
162
 
170
- Signature:
163
+ Values:
171
164
 
172
165
  ```ts
173
- updateGlobalByLanguage<TDoc>(args: {
174
- data: Record<string, unknown>
175
- depth?: number
176
- language: string
177
- slug: GlobalSlug
178
- }): Promise<TDoc>
166
+ {
167
+ language: '_multilangLanguage',
168
+ group: '_multilangGroup',
169
+ meta: '_multilangMeta',
170
+ }
179
171
  ```
180
172
 
181
- The helper merges `data` into the selected language and preserves existing values in other languages.
182
-
183
- ## Global Helpers With Payload
173
+ ## Preview Language Helper
184
174
 
185
- Use these variants when you already have a Payload instance or need request-aware access control.
175
+ Use `getMultilangDocumentLanguage` in admin preview or livePreview URL builders. It checks unsaved form data first, then the persisted document.
186
176
 
187
177
  ```ts
188
- const settings = await findGlobalByLanguageWithPayload<SiteSettings>({
189
- slug: 'site-settings',
190
- language: 'uk',
191
- overrideAccess: false,
192
- payload: req.payload,
193
- req,
194
- })
178
+ import { getMultilangDocumentLanguage } from '@roxxel/payload-multilang'
195
179
 
196
- await updateGlobalByLanguageWithPayload<SiteSettings>({
197
- slug: 'site-settings',
198
- language: 'uk',
199
- data: {
200
- title: 'Updated title',
201
- },
202
- overrideAccess: false,
203
- payload: req.payload,
204
- req,
180
+ const language = getMultilangDocumentLanguage({
181
+ data,
182
+ doc,
183
+ fallback: 'en',
205
184
  })
206
185
  ```
207
186
 
208
- Signatures:
187
+ ## Localized Slug Query
188
+
189
+ `localizedSlugQuery` builds the common Local API query for language + slug + status.
209
190
 
210
191
  ```ts
211
- findGlobalByLanguageWithPayload<TDoc>(args: {
212
- depth?: number
213
- language: string
214
- overrideAccess?: boolean
215
- payload: Payload
216
- req?: PayloadRequest
217
- slug: GlobalSlug
218
- }): Promise<TDoc>
219
-
220
- updateGlobalByLanguageWithPayload<TDoc>(args: {
221
- data: Record<string, unknown>
222
- depth?: number
223
- language: string
224
- overrideAccess?: boolean
225
- payload: Payload
226
- req?: PayloadRequest
227
- slug: GlobalSlug
228
- }): Promise<TDoc>
192
+ import { localizedSlugQuery } from '@roxxel/payload-multilang'
193
+
194
+ const { docs } = await payload.find({
195
+ collection: 'posts',
196
+ limit: 1,
197
+ ...localizedSlugQuery({
198
+ slug,
199
+ language: 'uk',
200
+ status: 'published',
201
+ }),
202
+ })
229
203
  ```
230
204
 
231
- `overrideAccess` defaults to `true`. Set it to `false` when operating on behalf of a user.
205
+ Use `status: 'draft'` to include `draft: true` and `_status = draft`, or `status: 'any'` to omit `_status`.