@roxxel/payload-multilang 0.0.4 → 0.0.5
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 +34 -11
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/data.d.ts +71 -33
- package/dist/lib/data.js +87 -57
- package/dist/lib/data.js.map +1 -1
- package/docs/configuration.md +32 -1
- package/docs/helpers.md +95 -121
- package/docs/usage.md +107 -88
- package/package.json +1 -1
- package/dist/payload-config.d.js +0 -2
- package/dist/payload-config.d.js.map +0 -1
package/LICENSE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 Roxxel
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -97,13 +97,14 @@ const posts = await payload.find({
|
|
|
97
97
|
|
|
98
98
|
## Translation Helpers
|
|
99
99
|
|
|
100
|
+
In request-scoped code, use the `WithPayload` helpers with `req.payload`.
|
|
101
|
+
|
|
100
102
|
```ts
|
|
101
103
|
import {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
updateGlobalByLanguage,
|
|
104
|
+
findGlobalByLanguageWithPayload,
|
|
105
|
+
getDocumentTranslationWithPayload,
|
|
106
|
+
getDocumentTranslationsWithPayload,
|
|
107
|
+
updateGlobalByLanguageWithPayload,
|
|
107
108
|
withLanguage,
|
|
108
109
|
} from '@roxxel/payload-multilang'
|
|
109
110
|
```
|
|
@@ -111,33 +112,55 @@ import {
|
|
|
111
112
|
Common examples:
|
|
112
113
|
|
|
113
114
|
```ts
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const state = await getDocumentTranslations({
|
|
115
|
+
const state = await getDocumentTranslationsWithPayload({
|
|
117
116
|
collection: 'posts',
|
|
118
117
|
id: post.id,
|
|
118
|
+
payload: req.payload,
|
|
119
|
+
req,
|
|
120
|
+
overrideAccess: false,
|
|
119
121
|
})
|
|
120
122
|
|
|
121
|
-
const ukrainianPost = await
|
|
123
|
+
const ukrainianPost = await getDocumentTranslationWithPayload({
|
|
122
124
|
collection: 'posts',
|
|
123
125
|
id: post.id,
|
|
124
126
|
language: 'uk',
|
|
127
|
+
payload: req.payload,
|
|
128
|
+
req,
|
|
129
|
+
overrideAccess: false,
|
|
125
130
|
})
|
|
126
131
|
|
|
127
|
-
const settings = await
|
|
132
|
+
const settings = await findGlobalByLanguageWithPayload({
|
|
128
133
|
slug: 'site-settings',
|
|
129
134
|
language: 'uk',
|
|
135
|
+
payload: req.payload,
|
|
136
|
+
req,
|
|
137
|
+
overrideAccess: false,
|
|
130
138
|
})
|
|
131
139
|
|
|
132
|
-
await
|
|
140
|
+
await updateGlobalByLanguageWithPayload({
|
|
133
141
|
slug: 'site-settings',
|
|
134
142
|
language: 'uk',
|
|
143
|
+
payload: req.payload,
|
|
144
|
+
req,
|
|
145
|
+
overrideAccess: false,
|
|
135
146
|
data: {
|
|
136
147
|
title: 'Localized title',
|
|
137
148
|
},
|
|
138
149
|
})
|
|
139
150
|
```
|
|
140
151
|
|
|
152
|
+
For app-level convenience, create your own configured helper module:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { createMultilangHelpers } from '@roxxel/payload-multilang'
|
|
156
|
+
import { getPayload } from 'payload'
|
|
157
|
+
import config from '@payload-config'
|
|
158
|
+
|
|
159
|
+
export const multilang = createMultilangHelpers({
|
|
160
|
+
getPayload: () => getPayload({ config }),
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
141
164
|
## Documentation
|
|
142
165
|
|
|
143
166
|
- [Configuration](./docs/configuration.md)
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { MultilangFieldNames } from './types.js';
|
|
2
2
|
export declare const DEFAULT_FIELD_NAMES: MultilangFieldNames;
|
|
3
|
+
export declare const MULTILANG_GROUP_FIELD: string;
|
|
4
|
+
export declare const MULTILANG_LANGUAGE_FIELD: string;
|
|
5
|
+
export declare const MULTILANG_META_FIELD: string;
|
|
3
6
|
export declare const DEFAULT_GLOBAL_TABS_LABEL = "Localized content";
|
|
4
7
|
export declare const DEFAULT_DUPLICATE_EXCLUDE_FIELDS: string[];
|
|
5
8
|
export declare const MULTILANG_SKIP_HOOK = "payloadMultilangSkip";
|
package/dist/constants.js
CHANGED
|
@@ -3,6 +3,9 @@ export const DEFAULT_FIELD_NAMES = {
|
|
|
3
3
|
language: '_multilangLanguage',
|
|
4
4
|
meta: '_multilangMeta'
|
|
5
5
|
};
|
|
6
|
+
export const MULTILANG_GROUP_FIELD = DEFAULT_FIELD_NAMES.group;
|
|
7
|
+
export const MULTILANG_LANGUAGE_FIELD = DEFAULT_FIELD_NAMES.language;
|
|
8
|
+
export const MULTILANG_META_FIELD = DEFAULT_FIELD_NAMES.meta;
|
|
6
9
|
export const DEFAULT_GLOBAL_TABS_LABEL = 'Localized content';
|
|
7
10
|
export const DEFAULT_DUPLICATE_EXCLUDE_FIELDS = [
|
|
8
11
|
'id',
|
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 DEFAULT_GLOBAL_TABS_LABEL = 'Localized content'\n\nexport const DEFAULT_DUPLICATE_EXCLUDE_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","DEFAULT_GLOBAL_TABS_LABEL","DEFAULT_DUPLICATE_EXCLUDE_FIELDS","MULTILANG_SKIP_HOOK"],"mappings":"AAEA,OAAO,MAAMA,sBAA2C;IACtDC,OAAO;IACPC,UAAU;IACVC,MAAM;AACR,EAAC;AAED,OAAO,MAAMC,4BAA4B,oBAAmB;AAE5D,OAAO,MAAMC,mCAAmC;IAC9C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAA;AAED,OAAO,MAAMC,sBAAsB,uBAAsB"}
|
|
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 DEFAULT_DUPLICATE_EXCLUDE_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","DEFAULT_DUPLICATE_EXCLUDE_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,mCAAmC;IAC9C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAA;AAED,OAAO,MAAMC,sBAAsB,uBAAsB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Plugin } from 'payload';
|
|
2
2
|
import type { PayloadMultilangConfig } from './types.js';
|
|
3
|
-
export {
|
|
3
|
+
export { DEFAULT_FIELD_NAMES, MULTILANG_GROUP_FIELD, MULTILANG_LANGUAGE_FIELD, MULTILANG_META_FIELD, } from './constants.js';
|
|
4
|
+
export { createMultilangHelpers, findGlobalByLanguageWithPayload, getDocumentTranslationsWithPayload, getDocumentTranslationWithPayload, getGroupTranslationsWithPayload, getMultilangDocumentLanguage, localizedSlugQuery, updateGlobalByLanguageWithPayload, withLanguage, } from './lib/data.js';
|
|
5
|
+
export type { FindGlobalByLanguageArgs, GetDocumentTranslationArgs, GetDocumentTranslationsArgs, LocalizedSlugQuery, LocalizedSlugQueryArgs, LocalizedSlugQueryStatus, MultilangDocumentLanguageArgs, MultilangHelpers, PayloadFactory, UpdateGlobalByLanguageArgs, } from './lib/data.js';
|
|
4
6
|
export type { MultilangCollectionOptions, MultilangFieldNames, MultilangGlobalOptions, MultilangLanguage, PayloadMultilangConfig, ResolvedMultilangGlobal, TranslationMap, TranslationState, } from './types.js';
|
|
5
7
|
export declare const payloadMultilang: (pluginOptions?: PayloadMultilangConfig) => Plugin;
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,17 @@ import { createTranslationEndpoints } from './endpoints/translations.js';
|
|
|
2
2
|
import { withTranslatedCollection } from './hooks/translatedCollection.js';
|
|
3
3
|
import { withTranslatedGlobal } from './hooks/translatedGlobal.js';
|
|
4
4
|
import { getCollectionConfig, getGlobalConfig, sanitizePluginConfig } from './lib/config.js';
|
|
5
|
-
export {
|
|
5
|
+
export { DEFAULT_FIELD_NAMES, MULTILANG_GROUP_FIELD, MULTILANG_LANGUAGE_FIELD, MULTILANG_META_FIELD } from './constants.js';
|
|
6
|
+
export { createMultilangHelpers, findGlobalByLanguageWithPayload, getDocumentTranslationsWithPayload, getDocumentTranslationWithPayload, getGroupTranslationsWithPayload, getMultilangDocumentLanguage, localizedSlugQuery, updateGlobalByLanguageWithPayload, withLanguage } from './lib/data.js';
|
|
6
7
|
export const payloadMultilang = (pluginOptions = {
|
|
7
8
|
languages: []
|
|
8
9
|
})=>(config)=>{
|
|
9
10
|
if (pluginOptions.disabled) {
|
|
10
11
|
return config;
|
|
11
12
|
}
|
|
13
|
+
if (config.localization) {
|
|
14
|
+
throw new Error('payload-multilang uses per-language documents and cannot run with Payload built-in localization enabled. Set localization: false and do not use localized: true fields for this plugin model.');
|
|
15
|
+
}
|
|
12
16
|
const pluginConfig = sanitizePluginConfig(pluginOptions);
|
|
13
17
|
const incomingCollections = config.collections || [];
|
|
14
18
|
const collections = incomingCollections.map((collection)=>{
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig, Plugin } from 'payload'\n\nimport type { PayloadMultilangConfig } from './types.js'\n\nimport { createTranslationEndpoints } from './endpoints/translations.js'\nimport { withTranslatedCollection } from './hooks/translatedCollection.js'\nimport { withTranslatedGlobal } from './hooks/translatedGlobal.js'\nimport {\n getCollectionConfig,\n getGlobalConfig,\n sanitizePluginConfig,\n} from './lib/config.js'\n\nexport {\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig, Plugin } from 'payload'\n\nimport type { PayloadMultilangConfig } from './types.js'\n\nimport { createTranslationEndpoints } from './endpoints/translations.js'\nimport { withTranslatedCollection } from './hooks/translatedCollection.js'\nimport { withTranslatedGlobal } from './hooks/translatedGlobal.js'\nimport {\n getCollectionConfig,\n getGlobalConfig,\n sanitizePluginConfig,\n} from './lib/config.js'\n\nexport {\n DEFAULT_FIELD_NAMES,\n MULTILANG_GROUP_FIELD,\n MULTILANG_LANGUAGE_FIELD,\n MULTILANG_META_FIELD,\n} from './constants.js'\n\nexport {\n createMultilangHelpers,\n findGlobalByLanguageWithPayload,\n getDocumentTranslationsWithPayload,\n getDocumentTranslationWithPayload,\n getGroupTranslationsWithPayload,\n getMultilangDocumentLanguage,\n localizedSlugQuery,\n updateGlobalByLanguageWithPayload,\n withLanguage,\n} from './lib/data.js'\n\nexport type {\n FindGlobalByLanguageArgs,\n GetDocumentTranslationArgs,\n GetDocumentTranslationsArgs,\n LocalizedSlugQuery,\n LocalizedSlugQueryArgs,\n LocalizedSlugQueryStatus,\n MultilangDocumentLanguageArgs,\n MultilangHelpers,\n PayloadFactory,\n UpdateGlobalByLanguageArgs,\n} from './lib/data.js'\n\nexport type {\n MultilangCollectionOptions,\n MultilangFieldNames,\n MultilangGlobalOptions,\n MultilangLanguage,\n PayloadMultilangConfig,\n ResolvedMultilangGlobal,\n TranslationMap,\n TranslationState,\n} from './types.js'\n\nexport const payloadMultilang =\n (pluginOptions: PayloadMultilangConfig = { languages: [] }): Plugin =>\n (config: Config): Config => {\n if (pluginOptions.disabled) {\n return config\n }\n\n if (config.localization) {\n throw new Error(\n 'payload-multilang uses per-language documents and cannot run with Payload built-in localization enabled. Set localization: false and do not use localized: true fields for this plugin model.',\n )\n }\n\n const pluginConfig = sanitizePluginConfig(pluginOptions)\n\n const incomingCollections = config.collections || []\n const collections = incomingCollections.map(\n (collection): CollectionConfig => {\n const translatedCollection = getCollectionConfig(\n pluginConfig.collections,\n collection.slug,\n )\n\n if (!translatedCollection) {\n return collection\n }\n\n const translated = withTranslatedCollection({\n collection: translatedCollection,\n config: collection,\n })\n\n return {\n ...translated,\n endpoints: [\n ...(translated.endpoints || []),\n ...createTranslationEndpoints({\n collection: translatedCollection,\n collectionConfig: collection,\n }),\n ],\n }\n },\n )\n\n const incomingGlobals = config.globals || []\n const globals = incomingGlobals.map((globalConfig): GlobalConfig => {\n const translatedGlobal = getGlobalConfig(\n pluginConfig.globals,\n globalConfig.slug,\n )\n\n if (!translatedGlobal) {\n return globalConfig\n }\n\n return withTranslatedGlobal({\n config: globalConfig,\n global: translatedGlobal,\n })\n })\n\n return {\n ...config,\n admin: {\n ...config.admin,\n custom: {\n ...config.admin?.custom,\n payloadMultilang: {\n ...((config.admin?.custom as\n | { payloadMultilang?: Record<string, unknown> }\n | undefined)?.payloadMultilang || {}),\n defaultLanguage: pluginConfig.defaultLanguage,\n languages: pluginConfig.languages,\n },\n },\n },\n collections,\n globals,\n }\n }\n"],"names":["createTranslationEndpoints","withTranslatedCollection","withTranslatedGlobal","getCollectionConfig","getGlobalConfig","sanitizePluginConfig","DEFAULT_FIELD_NAMES","MULTILANG_GROUP_FIELD","MULTILANG_LANGUAGE_FIELD","MULTILANG_META_FIELD","createMultilangHelpers","findGlobalByLanguageWithPayload","getDocumentTranslationsWithPayload","getDocumentTranslationWithPayload","getGroupTranslationsWithPayload","getMultilangDocumentLanguage","localizedSlugQuery","updateGlobalByLanguageWithPayload","withLanguage","payloadMultilang","pluginOptions","languages","config","disabled","localization","Error","pluginConfig","incomingCollections","collections","map","collection","translatedCollection","slug","translated","endpoints","collectionConfig","incomingGlobals","globals","globalConfig","translatedGlobal","global","admin","custom","defaultLanguage"],"mappings":"AAIA,SAASA,0BAA0B,QAAQ,8BAA6B;AACxE,SAASC,wBAAwB,QAAQ,kCAAiC;AAC1E,SAASC,oBAAoB,QAAQ,8BAA6B;AAClE,SACEC,mBAAmB,EACnBC,eAAe,EACfC,oBAAoB,QACf,kBAAiB;AAExB,SACEC,mBAAmB,EACnBC,qBAAqB,EACrBC,wBAAwB,EACxBC,oBAAoB,QACf,iBAAgB;AAEvB,SACEC,sBAAsB,EACtBC,+BAA+B,EAC/BC,kCAAkC,EAClCC,iCAAiC,EACjCC,+BAA+B,EAC/BC,4BAA4B,EAC5BC,kBAAkB,EAClBC,iCAAiC,EACjCC,YAAY,QACP,gBAAe;AA0BtB,OAAO,MAAMC,mBACX,CAACC,gBAAwC;IAAEC,WAAW,EAAE;AAAC,CAAC,GAC1D,CAACC;QACC,IAAIF,cAAcG,QAAQ,EAAE;YAC1B,OAAOD;QACT;QAEA,IAAIA,OAAOE,YAAY,EAAE;YACvB,MAAM,IAAIC,MACR;QAEJ;QAEA,MAAMC,eAAerB,qBAAqBe;QAE1C,MAAMO,sBAAsBL,OAAOM,WAAW,IAAI,EAAE;QACpD,MAAMA,cAAcD,oBAAoBE,GAAG,CACzC,CAACC;YACC,MAAMC,uBAAuB5B,oBAC3BuB,aAAaE,WAAW,EACxBE,WAAWE,IAAI;YAGjB,IAAI,CAACD,sBAAsB;gBACzB,OAAOD;YACT;YAEA,MAAMG,aAAahC,yBAAyB;gBAC1C6B,YAAYC;gBACZT,QAAQQ;YACV;YAEA,OAAO;gBACL,GAAGG,UAAU;gBACbC,WAAW;uBACLD,WAAWC,SAAS,IAAI,EAAE;uBAC3BlC,2BAA2B;wBAC5B8B,YAAYC;wBACZI,kBAAkBL;oBACpB;iBACD;YACH;QACF;QAGF,MAAMM,kBAAkBd,OAAOe,OAAO,IAAI,EAAE;QAC5C,MAAMA,UAAUD,gBAAgBP,GAAG,CAAC,CAACS;YACnC,MAAMC,mBAAmBnC,gBACvBsB,aAAaW,OAAO,EACpBC,aAAaN,IAAI;YAGnB,IAAI,CAACO,kBAAkB;gBACrB,OAAOD;YACT;YAEA,OAAOpC,qBAAqB;gBAC1BoB,QAAQgB;gBACRE,QAAQD;YACV;QACF;QAEA,OAAO;YACL,GAAGjB,MAAM;YACTmB,OAAO;gBACL,GAAGnB,OAAOmB,KAAK;gBACfC,QAAQ;oBACN,GAAGpB,OAAOmB,KAAK,EAAEC,MAAM;oBACvBvB,kBAAkB;wBAChB,GAAI,AAACG,OAAOmB,KAAK,EAAEC,QAEHvB,oBAAoB,CAAC,CAAC;wBACtCwB,iBAAiBjB,aAAaiB,eAAe;wBAC7CtB,WAAWK,aAAaL,SAAS;oBACnC;gBACF;YACF;YACAO;YACAS;QACF;IACF,EAAC"}
|
package/dist/lib/data.d.ts
CHANGED
|
@@ -11,18 +11,17 @@ type ConfigWithMultilang = Pick<Config, 'admin'> | Pick<Payload['config'], 'admi
|
|
|
11
11
|
export declare const getLanguagesFromCustom: (custom: unknown) => MultilangLanguage[];
|
|
12
12
|
/**
|
|
13
13
|
* Returns the languages configured for payload-multilang.
|
|
14
|
-
*
|
|
15
|
-
* Pass `languages` or a Payload `config` object when you already have them. Otherwise the helper
|
|
16
|
-
* resolves the cached Payload config from `@payload-config`.
|
|
17
14
|
*/
|
|
18
|
-
export declare const getLanguages: ({ config, languages, }?: {
|
|
15
|
+
export declare const getLanguages: ({ config, languages, payload, }?: {
|
|
19
16
|
config?: ConfigWithMultilang;
|
|
20
17
|
languages?: MultilangLanguage[];
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
payload?: Payload;
|
|
19
|
+
}) => MultilangLanguage[];
|
|
20
|
+
export declare const getDefaultLanguage: ({ config, languages, payload, }: {
|
|
23
21
|
config?: ConfigWithMultilang;
|
|
24
22
|
languages?: MultilangLanguage[];
|
|
25
|
-
|
|
23
|
+
payload?: Payload;
|
|
24
|
+
}) => MultilangLanguage | undefined;
|
|
26
25
|
/**
|
|
27
26
|
* Adds a language constraint to a Payload `where` query.
|
|
28
27
|
*
|
|
@@ -30,6 +29,36 @@ export declare const getDefaultLanguage: ({ config, languages, }: {
|
|
|
30
29
|
* payload-multilang language.
|
|
31
30
|
*/
|
|
32
31
|
export declare const withLanguage: ({ fieldName, language, where, }: WithLanguageArgs) => Where;
|
|
32
|
+
export type MultilangDocumentLanguageArgs = {
|
|
33
|
+
data?: unknown;
|
|
34
|
+
doc?: unknown;
|
|
35
|
+
fallback?: string;
|
|
36
|
+
fieldName?: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Resolves a document's payload-multilang language from draft/form data first, then persisted doc data.
|
|
40
|
+
*
|
|
41
|
+
* Useful in Payload admin preview and livePreview URL builders where unsaved form data may already
|
|
42
|
+
* contain the selected language.
|
|
43
|
+
*/
|
|
44
|
+
export declare const getMultilangDocumentLanguage: ({ data, doc, fallback, fieldName, }: MultilangDocumentLanguageArgs) => string | undefined;
|
|
45
|
+
export type LocalizedSlugQueryStatus = 'any' | 'draft' | 'published';
|
|
46
|
+
export type LocalizedSlugQueryArgs = {
|
|
47
|
+
fieldName?: string;
|
|
48
|
+
language: string;
|
|
49
|
+
slug: string;
|
|
50
|
+
slugFieldName?: string;
|
|
51
|
+
status?: LocalizedSlugQueryStatus;
|
|
52
|
+
where?: Where;
|
|
53
|
+
};
|
|
54
|
+
export type LocalizedSlugQuery = {
|
|
55
|
+
draft?: boolean;
|
|
56
|
+
where: Where;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Builds the common Payload Local API query for per-language documents addressed by slug.
|
|
60
|
+
*/
|
|
61
|
+
export declare const localizedSlugQuery: ({ slug, fieldName, language, slugFieldName, status, where, }: LocalizedSlugQueryArgs) => LocalizedSlugQuery;
|
|
33
62
|
export declare const getDocumentTranslationsWithPayload: <TDoc extends DocumentData = DocumentData>({ id, collection, fieldNames, overrideAccess, payload, req, }: {
|
|
34
63
|
collection: CollectionSlug;
|
|
35
64
|
fieldNames?: MultilangFieldNames;
|
|
@@ -48,26 +77,15 @@ export declare const getGroupTranslationsWithPayload: <TDoc extends DocumentData
|
|
|
48
77
|
}) => Promise<TranslationState<TDoc>>;
|
|
49
78
|
/**
|
|
50
79
|
* Returns the source document and every document in the same translation group, keyed by language.
|
|
51
|
-
*
|
|
52
|
-
* The helper resolves Payload with the cached project config, so callers only need to pass the
|
|
53
|
-
* collection slug and document id.
|
|
54
|
-
*/
|
|
55
|
-
export declare const getDocumentTranslations: <TDoc extends DocumentData = DocumentData>({ id, collection, fieldNames, }: {
|
|
56
|
-
collection: CollectionSlug;
|
|
57
|
-
fieldNames?: MultilangFieldNames;
|
|
58
|
-
id: number | string;
|
|
59
|
-
}) => Promise<TranslationState<TDoc>>;
|
|
60
|
-
/**
|
|
61
|
-
* Returns one target-language document from the source document's translation group.
|
|
62
|
-
*
|
|
63
|
-
* The helper resolves Payload with the cached project config, so callers only need to pass the
|
|
64
|
-
* collection slug, source document id, and target language code.
|
|
65
80
|
*/
|
|
66
|
-
export declare const
|
|
81
|
+
export declare const getDocumentTranslationWithPayload: <TDoc extends DocumentData = DocumentData>({ id, collection, fieldNames, language, overrideAccess, payload, req, }: {
|
|
67
82
|
collection: CollectionSlug;
|
|
68
83
|
fieldNames?: MultilangFieldNames;
|
|
69
84
|
id: number | string;
|
|
70
85
|
language: string;
|
|
86
|
+
overrideAccess?: boolean;
|
|
87
|
+
payload: Payload;
|
|
88
|
+
req?: PayloadRequest;
|
|
71
89
|
}) => Promise<TDoc | undefined>;
|
|
72
90
|
export declare const findGlobalByLanguageWithPayload: <TDoc extends DocumentData = DocumentData>({ slug, depth, language, overrideAccess, payload, req, }: {
|
|
73
91
|
depth?: number;
|
|
@@ -86,17 +104,6 @@ export declare const updateGlobalByLanguageWithPayload: <TDoc extends DocumentDa
|
|
|
86
104
|
req?: PayloadRequest;
|
|
87
105
|
slug: GlobalSlug;
|
|
88
106
|
}) => Promise<TDoc>;
|
|
89
|
-
export declare const findGlobalByLanguage: <TDoc extends DocumentData = DocumentData>({ slug, depth, language, }: {
|
|
90
|
-
depth?: number;
|
|
91
|
-
language: string;
|
|
92
|
-
slug: GlobalSlug;
|
|
93
|
-
}) => Promise<TDoc>;
|
|
94
|
-
export declare const updateGlobalByLanguage: <TDoc extends DocumentData = DocumentData>({ slug, data, depth, language, }: {
|
|
95
|
-
data: Record<string, unknown>;
|
|
96
|
-
depth?: number;
|
|
97
|
-
language: string;
|
|
98
|
-
slug: GlobalSlug;
|
|
99
|
-
}) => Promise<TDoc>;
|
|
100
107
|
export declare const duplicateDocumentData: ({ collection, doc, duplicateExcludeFields, fieldNames, targetLanguage, }: {
|
|
101
108
|
collection: CollectionConfig;
|
|
102
109
|
doc: DocumentData;
|
|
@@ -104,4 +111,35 @@ export declare const duplicateDocumentData: ({ collection, doc, duplicateExclude
|
|
|
104
111
|
fieldNames: MultilangFieldNames;
|
|
105
112
|
targetLanguage: string;
|
|
106
113
|
}) => DocumentData;
|
|
114
|
+
export type PayloadFactory = () => Payload | Promise<Payload>;
|
|
115
|
+
export type GetDocumentTranslationsArgs = {
|
|
116
|
+
collection: CollectionSlug;
|
|
117
|
+
fieldNames?: MultilangFieldNames;
|
|
118
|
+
id: number | string;
|
|
119
|
+
overrideAccess?: boolean;
|
|
120
|
+
req?: PayloadRequest;
|
|
121
|
+
};
|
|
122
|
+
export type GetDocumentTranslationArgs = {
|
|
123
|
+
language: string;
|
|
124
|
+
} & GetDocumentTranslationsArgs;
|
|
125
|
+
export type FindGlobalByLanguageArgs = {
|
|
126
|
+
depth?: number;
|
|
127
|
+
language: string;
|
|
128
|
+
overrideAccess?: boolean;
|
|
129
|
+
req?: PayloadRequest;
|
|
130
|
+
slug: GlobalSlug;
|
|
131
|
+
};
|
|
132
|
+
export type UpdateGlobalByLanguageArgs = {
|
|
133
|
+
data: Record<string, unknown>;
|
|
134
|
+
} & FindGlobalByLanguageArgs;
|
|
135
|
+
export type MultilangHelpers = {
|
|
136
|
+
findGlobalByLanguage: <TDoc extends DocumentData = DocumentData>(args: FindGlobalByLanguageArgs) => Promise<TDoc>;
|
|
137
|
+
getDocumentTranslation: <TDoc extends DocumentData = DocumentData>(args: GetDocumentTranslationArgs) => Promise<TDoc | undefined>;
|
|
138
|
+
getDocumentTranslations: <TDoc extends DocumentData = DocumentData>(args: GetDocumentTranslationsArgs) => Promise<TranslationState<TDoc>>;
|
|
139
|
+
getLanguages: () => Promise<MultilangLanguage[]>;
|
|
140
|
+
updateGlobalByLanguage: <TDoc extends DocumentData = DocumentData>(args: UpdateGlobalByLanguageArgs) => Promise<TDoc>;
|
|
141
|
+
};
|
|
142
|
+
export declare const createMultilangHelpers: ({ getPayload, }: {
|
|
143
|
+
getPayload: PayloadFactory;
|
|
144
|
+
}) => MultilangHelpers;
|
|
107
145
|
export {};
|
package/dist/lib/data.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import payloadConfig from '@payload-config';
|
|
2
|
-
import { getPayload } from 'payload';
|
|
3
1
|
import { DEFAULT_FIELD_NAMES } from '../constants.js';
|
|
4
2
|
export const asDocument = (value)=>value && typeof value === 'object' ? value : {};
|
|
5
3
|
export const getStringValue = (value)=>typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
@@ -29,25 +27,23 @@ export const getLanguagesFromCustom = (custom)=>{
|
|
|
29
27
|
};
|
|
30
28
|
/**
|
|
31
29
|
* Returns the languages configured for payload-multilang.
|
|
32
|
-
|
|
33
|
-
* Pass `languages` or a Payload `config` object when you already have them. Otherwise the helper
|
|
34
|
-
* resolves the cached Payload config from `@payload-config`.
|
|
35
|
-
*/ export const getLanguages = async ({ config, languages } = {})=>{
|
|
30
|
+
*/ export const getLanguages = ({ config, languages, payload } = {})=>{
|
|
36
31
|
if (languages) {
|
|
37
32
|
return languages.map(normalizeLanguage).filter((language)=>language.code);
|
|
38
33
|
}
|
|
34
|
+
if (payload) {
|
|
35
|
+
return getLanguagesFromCustom(payload.config.admin?.custom);
|
|
36
|
+
}
|
|
39
37
|
if (config) {
|
|
40
38
|
return getLanguagesFromCustom(config.admin?.custom);
|
|
41
39
|
}
|
|
42
|
-
|
|
43
|
-
config: payloadConfig
|
|
44
|
-
});
|
|
45
|
-
return getLanguagesFromCustom(payload.config.admin?.custom);
|
|
40
|
+
throw new Error('getLanguages requires languages, config, or payload. Use createMultilangHelpers({ getPayload }) for app-level convenience.');
|
|
46
41
|
};
|
|
47
|
-
export const getDefaultLanguage =
|
|
48
|
-
const configuredLanguages =
|
|
42
|
+
export const getDefaultLanguage = ({ config, languages, payload })=>{
|
|
43
|
+
const configuredLanguages = getLanguages({
|
|
49
44
|
config,
|
|
50
|
-
languages
|
|
45
|
+
languages,
|
|
46
|
+
payload
|
|
51
47
|
});
|
|
52
48
|
return configuredLanguages.find((language)=>language.isDefault) || configuredLanguages[0];
|
|
53
49
|
};
|
|
@@ -72,6 +68,46 @@ export const getDefaultLanguage = async ({ config, languages })=>{
|
|
|
72
68
|
]
|
|
73
69
|
};
|
|
74
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Resolves a document's payload-multilang language from draft/form data first, then persisted doc data.
|
|
73
|
+
*
|
|
74
|
+
* Useful in Payload admin preview and livePreview URL builders where unsaved form data may already
|
|
75
|
+
* contain the selected language.
|
|
76
|
+
*/ export const getMultilangDocumentLanguage = ({ data, doc, fallback, fieldName = DEFAULT_FIELD_NAMES.language })=>getStringValue(asDocument(data)[fieldName]) || getStringValue(asDocument(doc)[fieldName]) || fallback;
|
|
77
|
+
/**
|
|
78
|
+
* Builds the common Payload Local API query for per-language documents addressed by slug.
|
|
79
|
+
*/ export const localizedSlugQuery = ({ slug, fieldName = DEFAULT_FIELD_NAMES.language, language, slugFieldName = 'slug', status = 'published', where })=>{
|
|
80
|
+
const constraints = [
|
|
81
|
+
{
|
|
82
|
+
[slugFieldName]: {
|
|
83
|
+
equals: slug
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
[fieldName]: {
|
|
88
|
+
equals: language
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
];
|
|
92
|
+
if (where) {
|
|
93
|
+
constraints.unshift(where);
|
|
94
|
+
}
|
|
95
|
+
if (status !== 'any') {
|
|
96
|
+
constraints.push({
|
|
97
|
+
_status: {
|
|
98
|
+
equals: status
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
...status === 'draft' ? {
|
|
104
|
+
draft: true
|
|
105
|
+
} : {},
|
|
106
|
+
where: constraints.length === 1 ? constraints[0] : {
|
|
107
|
+
and: constraints
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
};
|
|
75
111
|
export const getDocumentTranslationsWithPayload = async ({ id, collection, fieldNames = DEFAULT_FIELD_NAMES, overrideAccess = true, payload, req })=>{
|
|
76
112
|
const source = asDocument(await payload.findByID({
|
|
77
113
|
id,
|
|
@@ -146,30 +182,14 @@ export const getGroupTranslationsWithPayload = async ({ collection, fieldNames =
|
|
|
146
182
|
};
|
|
147
183
|
/**
|
|
148
184
|
* Returns the source document and every document in the same translation group, keyed by language.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
* collection slug and document id.
|
|
152
|
-
*/ export const getDocumentTranslations = async ({ id, collection, fieldNames })=>{
|
|
153
|
-
const payload = await getPayload({
|
|
154
|
-
config: payloadConfig
|
|
155
|
-
});
|
|
156
|
-
return getDocumentTranslationsWithPayload({
|
|
185
|
+
*/ export const getDocumentTranslationWithPayload = async ({ id, collection, fieldNames, language, overrideAccess = true, payload, req })=>{
|
|
186
|
+
const state = await getDocumentTranslationsWithPayload({
|
|
157
187
|
id,
|
|
158
188
|
collection,
|
|
159
189
|
fieldNames,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Returns one target-language document from the source document's translation group.
|
|
165
|
-
*
|
|
166
|
-
* The helper resolves Payload with the cached project config, so callers only need to pass the
|
|
167
|
-
* collection slug, source document id, and target language code.
|
|
168
|
-
*/ export const getDocumentTranslation = async ({ id, collection, fieldNames, language })=>{
|
|
169
|
-
const state = await getDocumentTranslations({
|
|
170
|
-
id,
|
|
171
|
-
collection,
|
|
172
|
-
fieldNames
|
|
190
|
+
overrideAccess,
|
|
191
|
+
payload,
|
|
192
|
+
req
|
|
173
193
|
});
|
|
174
194
|
return state.translations[language];
|
|
175
195
|
};
|
|
@@ -257,29 +277,6 @@ export const updateGlobalByLanguageWithPayload = async ({ slug, data, depth, lan
|
|
|
257
277
|
language
|
|
258
278
|
});
|
|
259
279
|
};
|
|
260
|
-
export const findGlobalByLanguage = async ({ slug, depth, language })=>{
|
|
261
|
-
const payload = await getPayload({
|
|
262
|
-
config: payloadConfig
|
|
263
|
-
});
|
|
264
|
-
return findGlobalByLanguageWithPayload({
|
|
265
|
-
slug,
|
|
266
|
-
depth,
|
|
267
|
-
language,
|
|
268
|
-
payload
|
|
269
|
-
});
|
|
270
|
-
};
|
|
271
|
-
export const updateGlobalByLanguage = async ({ slug, data, depth, language })=>{
|
|
272
|
-
const payload = await getPayload({
|
|
273
|
-
config: payloadConfig
|
|
274
|
-
});
|
|
275
|
-
return updateGlobalByLanguageWithPayload({
|
|
276
|
-
slug,
|
|
277
|
-
data,
|
|
278
|
-
depth,
|
|
279
|
-
language,
|
|
280
|
-
payload
|
|
281
|
-
});
|
|
282
|
-
};
|
|
283
280
|
const fieldAffectsData = (field)=>'name' in field && typeof field.name === 'string' && field.type !== 'ui';
|
|
284
281
|
export const duplicateDocumentData = ({ collection, doc, duplicateExcludeFields, fieldNames, targetLanguage })=>{
|
|
285
282
|
const excluded = new Set([
|
|
@@ -303,5 +300,38 @@ export const duplicateDocumentData = ({ collection, doc, duplicateExcludeFields,
|
|
|
303
300
|
}
|
|
304
301
|
return data;
|
|
305
302
|
};
|
|
303
|
+
export const createMultilangHelpers = ({ getPayload })=>{
|
|
304
|
+
const resolvePayload = async ()=>getPayload();
|
|
305
|
+
return {
|
|
306
|
+
findGlobalByLanguage: async ({ slug, depth, language, overrideAccess, req })=>findGlobalByLanguageWithPayload({
|
|
307
|
+
slug,
|
|
308
|
+
depth,
|
|
309
|
+
language,
|
|
310
|
+
overrideAccess,
|
|
311
|
+
payload: await resolvePayload(),
|
|
312
|
+
req
|
|
313
|
+
}),
|
|
314
|
+
getDocumentTranslation: async (args)=>getDocumentTranslationWithPayload({
|
|
315
|
+
...args,
|
|
316
|
+
payload: await resolvePayload()
|
|
317
|
+
}),
|
|
318
|
+
getDocumentTranslations: async (args)=>getDocumentTranslationsWithPayload({
|
|
319
|
+
...args,
|
|
320
|
+
payload: await resolvePayload()
|
|
321
|
+
}),
|
|
322
|
+
getLanguages: async ()=>getLanguages({
|
|
323
|
+
payload: await resolvePayload()
|
|
324
|
+
}),
|
|
325
|
+
updateGlobalByLanguage: async ({ slug, data, depth, language, overrideAccess, req })=>updateGlobalByLanguageWithPayload({
|
|
326
|
+
slug,
|
|
327
|
+
data,
|
|
328
|
+
depth,
|
|
329
|
+
language,
|
|
330
|
+
overrideAccess,
|
|
331
|
+
payload: await resolvePayload(),
|
|
332
|
+
req
|
|
333
|
+
})
|
|
334
|
+
};
|
|
335
|
+
};
|
|
306
336
|
|
|
307
337
|
//# sourceMappingURL=data.js.map
|
package/dist/lib/data.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/data.ts"],"sourcesContent":["import type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n GlobalSlug,\n Payload,\n PayloadRequest,\n Where,\n} from 'payload'\n\nimport payloadConfig from '@payload-config'\nimport { getPayload } from 'payload'\n\nimport type {\n MultilangFieldNames,\n MultilangLanguage,\n TranslationState,\n WithLanguageArgs,\n} from '../types.js'\n\nimport { DEFAULT_FIELD_NAMES } from '../constants.js'\n\nexport type DocumentData = {\n id?: number | string\n} & Record<string, unknown>\n\nexport const asDocument = (value: unknown): DocumentData =>\n value && typeof value === 'object' ? (value as DocumentData) : {}\n\nexport const getStringValue = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined\n\nexport const getID = (value: unknown): number | string | undefined =>\n typeof value === 'number' || typeof value === 'string' ? value : undefined\n\nexport const normalizeLanguage = (doc: unknown): MultilangLanguage => {\n const data = asDocument(doc)\n const code = getStringValue(data.code)?.trim().toLowerCase() || ''\n\n return {\n id: getID(data.id),\n name: getStringValue(data.name)?.trim() || code,\n active: data.active !== false,\n code,\n direction: data.direction === 'rtl' ? 'rtl' : 'ltr',\n flagLabel: getStringValue(data.flagLabel),\n isDefault: data.isDefault === true,\n locale: getStringValue(data.locale),\n order: typeof data.order === 'number' ? data.order : 0,\n }\n}\n\ntype ConfigWithMultilang = Pick<Config, 'admin'> | Pick<Payload['config'], 'admin'>\n\nexport const getLanguagesFromCustom = (custom: unknown): MultilangLanguage[] => {\n const payloadMultilang = asDocument(asDocument(custom).payloadMultilang)\n const languages = payloadMultilang.languages\n\n if (!Array.isArray(languages)) {\n return []\n }\n\n return languages.map(normalizeLanguage).filter((language) => language.code)\n}\n\n/**\n * Returns the languages configured for payload-multilang.\n *\n * Pass `languages` or a Payload `config` object when you already have them. Otherwise the helper\n * resolves the cached Payload config from `@payload-config`.\n */\nexport const getLanguages = async ({\n config,\n languages,\n}: {\n config?: ConfigWithMultilang\n languages?: MultilangLanguage[]\n} = {}): Promise<MultilangLanguage[]> => {\n if (languages) {\n return languages.map(normalizeLanguage).filter((language) => language.code)\n }\n\n if (config) {\n return getLanguagesFromCustom(config.admin?.custom)\n }\n\n const payload = await getPayload({ config: payloadConfig })\n\n return getLanguagesFromCustom(payload.config.admin?.custom)\n}\n\nexport const getDefaultLanguage = async ({\n config,\n languages,\n}: {\n config?: ConfigWithMultilang\n languages?: MultilangLanguage[]\n}): Promise<MultilangLanguage | undefined> => {\n const configuredLanguages = await getLanguages({ config, languages })\n\n return configuredLanguages.find((language) => language.isDefault) || configuredLanguages[0]\n}\n\n/**\n * Adds a language constraint to a Payload `where` query.\n *\n * Use this when querying an enabled collection directly and you only want documents for one\n * payload-multilang language.\n */\nexport const withLanguage = ({\n fieldName = DEFAULT_FIELD_NAMES.language,\n language,\n where,\n}: WithLanguageArgs): Where => {\n const languageWhere: Where = {\n [fieldName]: {\n equals: language,\n },\n }\n\n if (!where) {\n return languageWhere\n }\n\n return {\n and: [where, languageWhere],\n }\n}\n\nexport const getDocumentTranslationsWithPayload = async <TDoc extends DocumentData = DocumentData>({\n id,\n collection,\n fieldNames = DEFAULT_FIELD_NAMES,\n overrideAccess = true,\n payload,\n req,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n id: number | string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n}): Promise<TranslationState<TDoc>> => {\n const source = asDocument(\n await payload.findByID({\n id,\n collection,\n depth: 0,\n overrideAccess,\n req,\n }),\n ) as TDoc\n\n const group = getStringValue(source[fieldNames.group])\n const language = getStringValue(source[fieldNames.language])\n\n if (!group) {\n return {\n language,\n source,\n translations: language ? { [language]: source } : {},\n }\n }\n\n const result = await payload.find({\n collection,\n depth: 0,\n limit: 200,\n overrideAccess,\n req,\n where: {\n [fieldNames.group]: {\n equals: group,\n },\n },\n })\n\n const translations = result.docs.reduce<TranslationState<TDoc>['translations']>((acc, doc) => {\n const translation = asDocument(doc) as TDoc\n const code = getStringValue(translation[fieldNames.language])\n\n if (code) {\n acc[code] = translation\n }\n\n return acc\n }, {})\n\n return {\n group,\n language,\n source,\n translations,\n }\n}\n\nexport const getGroupTranslationsWithPayload = async <TDoc extends DocumentData = DocumentData>({\n collection,\n fieldNames = DEFAULT_FIELD_NAMES,\n group,\n overrideAccess = true,\n payload,\n req,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n group: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n}): Promise<TranslationState<TDoc>> => {\n const result = await payload.find({\n collection,\n depth: 0,\n limit: 200,\n overrideAccess,\n req,\n where: {\n [fieldNames.group]: {\n equals: group,\n },\n },\n })\n\n const translations = result.docs.reduce<TranslationState<TDoc>['translations']>((acc, doc) => {\n const translation = asDocument(doc) as TDoc\n const code = getStringValue(translation[fieldNames.language])\n\n if (code) {\n acc[code] = translation\n }\n\n return acc\n }, {})\n\n return {\n group,\n translations,\n }\n}\n\n/**\n * Returns the source document and every document in the same translation group, keyed by language.\n *\n * The helper resolves Payload with the cached project config, so callers only need to pass the\n * collection slug and document id.\n */\nexport const getDocumentTranslations = async <TDoc extends DocumentData = DocumentData>({\n id,\n collection,\n fieldNames,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n id: number | string\n}): Promise<TranslationState<TDoc>> => {\n const payload = await getPayload({ config: payloadConfig })\n\n return getDocumentTranslationsWithPayload<TDoc>({\n id,\n collection,\n fieldNames,\n payload,\n })\n}\n\n/**\n * Returns one target-language document from the source document's translation group.\n *\n * The helper resolves Payload with the cached project config, so callers only need to pass the\n * collection slug, source document id, and target language code.\n */\nexport const getDocumentTranslation = async <TDoc extends DocumentData = DocumentData>({\n id,\n collection,\n fieldNames,\n language,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n id: number | string\n language: string\n}): Promise<TDoc | undefined> => {\n const state = await getDocumentTranslations<TDoc>({\n id,\n collection,\n fieldNames,\n })\n\n return state.translations[language]\n}\n\nconst GLOBAL_SYSTEM_FIELDS = new Set(['_status', 'createdAt', 'globalType', 'id', 'updatedAt'])\n\nconst getGlobalPayloadMultilangCustom = ({\n slug,\n payload,\n}: {\n payload: Payload\n slug: GlobalSlug\n}): {\n defaultLanguage?: MultilangLanguage\n languages?: MultilangLanguage[]\n} => {\n const global = payload.config.globals.find((item) => item.slug === slug)\n const payloadMultilang = asDocument(asDocument(global?.admin?.custom).payloadMultilang)\n\n return payloadMultilang as {\n defaultLanguage?: MultilangLanguage\n languages?: MultilangLanguage[]\n }\n}\n\nconst validateGlobalLanguage = ({\n slug,\n language,\n payload,\n}: {\n language: string\n payload: Payload\n slug: GlobalSlug\n}): void => {\n const languages = getGlobalPayloadMultilangCustom({ slug, payload }).languages\n\n if (!Array.isArray(languages) || languages.length === 0) {\n return\n }\n\n if (!languages.some((candidate) => candidate.code === language)) {\n throw new Error(`Language \"${language}\" is not configured for ${slug}.`)\n }\n}\n\nconst getGlobalLanguageTab = <TDoc extends DocumentData = DocumentData>({\n doc,\n language,\n}: {\n doc: DocumentData\n language: string\n}): TDoc => {\n const languageData = asDocument(doc[language])\n const systemData = Object.entries(doc).reduce<DocumentData>((acc, [key, value]) => {\n if (GLOBAL_SYSTEM_FIELDS.has(key)) {\n acc[key] = value\n }\n\n return acc\n }, {})\n\n return {\n ...systemData,\n ...languageData,\n } as TDoc\n}\n\nexport const findGlobalByLanguageWithPayload = async <TDoc extends DocumentData = DocumentData>({\n slug,\n depth,\n language,\n overrideAccess = true,\n payload,\n req,\n}: {\n depth?: number\n language: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n slug: GlobalSlug\n}): Promise<TDoc> => {\n validateGlobalLanguage({ slug, language, payload })\n\n const doc = asDocument(\n await payload.findGlobal({\n slug,\n depth,\n overrideAccess,\n req,\n } as never),\n )\n\n return getGlobalLanguageTab<TDoc>({\n doc,\n language,\n })\n}\n\nexport const updateGlobalByLanguageWithPayload = async <TDoc extends DocumentData = DocumentData>({\n slug,\n data,\n depth,\n language,\n overrideAccess = true,\n payload,\n req,\n}: {\n data: Record<string, unknown>\n depth?: number\n language: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n slug: GlobalSlug\n}): Promise<TDoc> => {\n validateGlobalLanguage({ slug, language, payload })\n\n const currentDoc = asDocument(\n await payload.findGlobal({\n slug,\n depth: 0,\n overrideAccess,\n req,\n } as never),\n )\n const currentLanguageData = asDocument(currentDoc[language])\n\n const doc = asDocument(\n await payload.updateGlobal({\n slug,\n data: {\n [language]: {\n ...currentLanguageData,\n ...data,\n },\n },\n depth,\n overrideAccess,\n req,\n } as never),\n )\n\n return getGlobalLanguageTab<TDoc>({\n doc,\n language,\n })\n}\n\nexport const findGlobalByLanguage = async <TDoc extends DocumentData = DocumentData>({\n slug,\n depth,\n language,\n}: {\n depth?: number\n language: string\n slug: GlobalSlug\n}): Promise<TDoc> => {\n const payload = await getPayload({ config: payloadConfig })\n\n return findGlobalByLanguageWithPayload<TDoc>({\n slug,\n depth,\n language,\n payload,\n })\n}\n\nexport const updateGlobalByLanguage = async <TDoc extends DocumentData = DocumentData>({\n slug,\n data,\n depth,\n language,\n}: {\n data: Record<string, unknown>\n depth?: number\n language: string\n slug: GlobalSlug\n}): Promise<TDoc> => {\n const payload = await getPayload({ config: payloadConfig })\n\n return updateGlobalByLanguageWithPayload<TDoc>({\n slug,\n data,\n depth,\n language,\n payload,\n })\n}\n\nconst fieldAffectsData = (field: Field): field is { name: string } & Field =>\n 'name' in field && typeof field.name === 'string' && field.type !== 'ui'\n\nexport const duplicateDocumentData = ({\n collection,\n doc,\n duplicateExcludeFields,\n fieldNames,\n targetLanguage,\n}: {\n collection: CollectionConfig\n doc: DocumentData\n duplicateExcludeFields: string[]\n fieldNames: MultilangFieldNames\n targetLanguage: string\n}): DocumentData => {\n const excluded = new Set([\n fieldNames.group,\n fieldNames.language,\n fieldNames.meta,\n ...duplicateExcludeFields,\n ])\n\n const data: DocumentData = {}\n\n for (const field of collection.fields || []) {\n if (!fieldAffectsData(field) || excluded.has(field.name)) {\n continue\n }\n\n if (!(field.name in doc)) {\n continue\n }\n\n data[field.name] = doc[field.name]\n }\n\n if (typeof data.slug === 'string' && data.slug.length > 0) {\n data.slug = `${data.slug}-${targetLanguage}`\n }\n\n return data\n}\n"],"names":["payloadConfig","getPayload","DEFAULT_FIELD_NAMES","asDocument","value","getStringValue","length","undefined","getID","normalizeLanguage","doc","data","code","trim","toLowerCase","id","name","active","direction","flagLabel","isDefault","locale","order","getLanguagesFromCustom","custom","payloadMultilang","languages","Array","isArray","map","filter","language","getLanguages","config","admin","payload","getDefaultLanguage","configuredLanguages","find","withLanguage","fieldName","where","languageWhere","equals","and","getDocumentTranslationsWithPayload","collection","fieldNames","overrideAccess","req","source","findByID","depth","group","translations","result","limit","docs","reduce","acc","translation","getGroupTranslationsWithPayload","getDocumentTranslations","getDocumentTranslation","state","GLOBAL_SYSTEM_FIELDS","Set","getGlobalPayloadMultilangCustom","slug","global","globals","item","validateGlobalLanguage","some","candidate","Error","getGlobalLanguageTab","languageData","systemData","Object","entries","key","has","findGlobalByLanguageWithPayload","findGlobal","updateGlobalByLanguageWithPayload","currentDoc","currentLanguageData","updateGlobal","findGlobalByLanguage","updateGlobalByLanguage","fieldAffectsData","field","type","duplicateDocumentData","duplicateExcludeFields","targetLanguage","excluded","meta","fields"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/data.ts"],"sourcesContent":["import type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n GlobalSlug,\n Payload,\n PayloadRequest,\n Where,\n} from 'payload'\n\nimport type {\n MultilangFieldNames,\n MultilangLanguage,\n TranslationState,\n WithLanguageArgs,\n} from '../types.js'\n\nimport { DEFAULT_FIELD_NAMES } from '../constants.js'\n\nexport type DocumentData = {\n id?: number | string\n} & Record<string, unknown>\n\nexport const asDocument = (value: unknown): DocumentData =>\n value && typeof value === 'object' ? (value as DocumentData) : {}\n\nexport const getStringValue = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined\n\nexport const getID = (value: unknown): number | string | undefined =>\n typeof value === 'number' || typeof value === 'string' ? value : undefined\n\nexport const normalizeLanguage = (doc: unknown): MultilangLanguage => {\n const data = asDocument(doc)\n const code = getStringValue(data.code)?.trim().toLowerCase() || ''\n\n return {\n id: getID(data.id),\n name: getStringValue(data.name)?.trim() || code,\n active: data.active !== false,\n code,\n direction: data.direction === 'rtl' ? 'rtl' : 'ltr',\n flagLabel: getStringValue(data.flagLabel),\n isDefault: data.isDefault === true,\n locale: getStringValue(data.locale),\n order: typeof data.order === 'number' ? data.order : 0,\n }\n}\n\ntype ConfigWithMultilang = Pick<Config, 'admin'> | Pick<Payload['config'], 'admin'>\n\nexport const getLanguagesFromCustom = (custom: unknown): MultilangLanguage[] => {\n const payloadMultilang = asDocument(asDocument(custom).payloadMultilang)\n const languages = payloadMultilang.languages\n\n if (!Array.isArray(languages)) {\n return []\n }\n\n return languages.map(normalizeLanguage).filter((language) => language.code)\n}\n\n/**\n * Returns the languages configured for payload-multilang.\n */\nexport const getLanguages = ({\n config,\n languages,\n payload,\n}: {\n config?: ConfigWithMultilang\n languages?: MultilangLanguage[]\n payload?: Payload\n} = {}): MultilangLanguage[] => {\n if (languages) {\n return languages.map(normalizeLanguage).filter((language) => language.code)\n }\n\n if (payload) {\n return getLanguagesFromCustom(payload.config.admin?.custom)\n }\n\n if (config) {\n return getLanguagesFromCustom(config.admin?.custom)\n }\n\n throw new Error(\n 'getLanguages requires languages, config, or payload. Use createMultilangHelpers({ getPayload }) for app-level convenience.',\n )\n}\n\nexport const getDefaultLanguage = ({\n config,\n languages,\n payload,\n}: {\n config?: ConfigWithMultilang\n languages?: MultilangLanguage[]\n payload?: Payload\n}): MultilangLanguage | undefined => {\n const configuredLanguages = getLanguages({ config, languages, payload })\n\n return configuredLanguages.find((language) => language.isDefault) || configuredLanguages[0]\n}\n\n/**\n * Adds a language constraint to a Payload `where` query.\n *\n * Use this when querying an enabled collection directly and you only want documents for one\n * payload-multilang language.\n */\nexport const withLanguage = ({\n fieldName = DEFAULT_FIELD_NAMES.language,\n language,\n where,\n}: WithLanguageArgs): Where => {\n const languageWhere: Where = {\n [fieldName]: {\n equals: language,\n },\n }\n\n if (!where) {\n return languageWhere\n }\n\n return {\n and: [where, languageWhere],\n }\n}\n\nexport type MultilangDocumentLanguageArgs = {\n data?: unknown\n doc?: unknown\n fallback?: string\n fieldName?: string\n}\n\n/**\n * Resolves a document's payload-multilang language from draft/form data first, then persisted doc data.\n *\n * Useful in Payload admin preview and livePreview URL builders where unsaved form data may already\n * contain the selected language.\n */\nexport const getMultilangDocumentLanguage = ({\n data,\n doc,\n fallback,\n fieldName = DEFAULT_FIELD_NAMES.language,\n}: MultilangDocumentLanguageArgs): string | undefined =>\n getStringValue(asDocument(data)[fieldName]) ||\n getStringValue(asDocument(doc)[fieldName]) ||\n fallback\n\nexport type LocalizedSlugQueryStatus = 'any' | 'draft' | 'published'\n\nexport type LocalizedSlugQueryArgs = {\n fieldName?: string\n language: string\n slug: string\n slugFieldName?: string\n status?: LocalizedSlugQueryStatus\n where?: Where\n}\n\nexport type LocalizedSlugQuery = {\n draft?: boolean\n where: Where\n}\n\n/**\n * Builds the common Payload Local API query for per-language documents addressed by slug.\n */\nexport const localizedSlugQuery = ({\n slug,\n fieldName = DEFAULT_FIELD_NAMES.language,\n language,\n slugFieldName = 'slug',\n status = 'published',\n where,\n}: LocalizedSlugQueryArgs): LocalizedSlugQuery => {\n const constraints: Where[] = [\n {\n [slugFieldName]: {\n equals: slug,\n },\n },\n {\n [fieldName]: {\n equals: language,\n },\n },\n ]\n\n if (where) {\n constraints.unshift(where)\n }\n\n if (status !== 'any') {\n constraints.push({\n _status: {\n equals: status,\n },\n })\n }\n\n return {\n ...(status === 'draft' ? { draft: true } : {}),\n where:\n constraints.length === 1\n ? constraints[0]\n : {\n and: constraints,\n },\n }\n}\n\nexport const getDocumentTranslationsWithPayload = async <TDoc extends DocumentData = DocumentData>({\n id,\n collection,\n fieldNames = DEFAULT_FIELD_NAMES,\n overrideAccess = true,\n payload,\n req,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n id: number | string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n}): Promise<TranslationState<TDoc>> => {\n const source = asDocument(\n await payload.findByID({\n id,\n collection,\n depth: 0,\n overrideAccess,\n req,\n }),\n ) as TDoc\n\n const group = getStringValue(source[fieldNames.group])\n const language = getStringValue(source[fieldNames.language])\n\n if (!group) {\n return {\n language,\n source,\n translations: language ? { [language]: source } : {},\n }\n }\n\n const result = await payload.find({\n collection,\n depth: 0,\n limit: 200,\n overrideAccess,\n req,\n where: {\n [fieldNames.group]: {\n equals: group,\n },\n },\n })\n\n const translations = result.docs.reduce<TranslationState<TDoc>['translations']>((acc, doc) => {\n const translation = asDocument(doc) as TDoc\n const code = getStringValue(translation[fieldNames.language])\n\n if (code) {\n acc[code] = translation\n }\n\n return acc\n }, {})\n\n return {\n group,\n language,\n source,\n translations,\n }\n}\n\nexport const getGroupTranslationsWithPayload = async <TDoc extends DocumentData = DocumentData>({\n collection,\n fieldNames = DEFAULT_FIELD_NAMES,\n group,\n overrideAccess = true,\n payload,\n req,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n group: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n}): Promise<TranslationState<TDoc>> => {\n const result = await payload.find({\n collection,\n depth: 0,\n limit: 200,\n overrideAccess,\n req,\n where: {\n [fieldNames.group]: {\n equals: group,\n },\n },\n })\n\n const translations = result.docs.reduce<TranslationState<TDoc>['translations']>((acc, doc) => {\n const translation = asDocument(doc) as TDoc\n const code = getStringValue(translation[fieldNames.language])\n\n if (code) {\n acc[code] = translation\n }\n\n return acc\n }, {})\n\n return {\n group,\n translations,\n }\n}\n\n/**\n * Returns the source document and every document in the same translation group, keyed by language.\n */\nexport const getDocumentTranslationWithPayload = async <TDoc extends DocumentData = DocumentData>({\n id,\n collection,\n fieldNames,\n language,\n overrideAccess = true,\n payload,\n req,\n}: {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n id: number | string\n language: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n}): Promise<TDoc | undefined> => {\n const state = await getDocumentTranslationsWithPayload<TDoc>({\n id,\n collection,\n fieldNames,\n overrideAccess,\n payload,\n req,\n })\n\n return state.translations[language]\n}\n\nconst GLOBAL_SYSTEM_FIELDS = new Set(['_status', 'createdAt', 'globalType', 'id', 'updatedAt'])\n\nconst getGlobalPayloadMultilangCustom = ({\n slug,\n payload,\n}: {\n payload: Payload\n slug: GlobalSlug\n}): {\n defaultLanguage?: MultilangLanguage\n languages?: MultilangLanguage[]\n} => {\n const global = payload.config.globals.find((item) => item.slug === slug)\n const payloadMultilang = asDocument(asDocument(global?.admin?.custom).payloadMultilang)\n\n return payloadMultilang as {\n defaultLanguage?: MultilangLanguage\n languages?: MultilangLanguage[]\n }\n}\n\nconst validateGlobalLanguage = ({\n slug,\n language,\n payload,\n}: {\n language: string\n payload: Payload\n slug: GlobalSlug\n}): void => {\n const languages = getGlobalPayloadMultilangCustom({ slug, payload }).languages\n\n if (!Array.isArray(languages) || languages.length === 0) {\n return\n }\n\n if (!languages.some((candidate) => candidate.code === language)) {\n throw new Error(`Language \"${language}\" is not configured for ${slug}.`)\n }\n}\n\nconst getGlobalLanguageTab = <TDoc extends DocumentData = DocumentData>({\n doc,\n language,\n}: {\n doc: DocumentData\n language: string\n}): TDoc => {\n const languageData = asDocument(doc[language])\n const systemData = Object.entries(doc).reduce<DocumentData>((acc, [key, value]) => {\n if (GLOBAL_SYSTEM_FIELDS.has(key)) {\n acc[key] = value\n }\n\n return acc\n }, {})\n\n return {\n ...systemData,\n ...languageData,\n } as TDoc\n}\n\nexport const findGlobalByLanguageWithPayload = async <TDoc extends DocumentData = DocumentData>({\n slug,\n depth,\n language,\n overrideAccess = true,\n payload,\n req,\n}: {\n depth?: number\n language: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n slug: GlobalSlug\n}): Promise<TDoc> => {\n validateGlobalLanguage({ slug, language, payload })\n\n const doc = asDocument(\n await payload.findGlobal({\n slug,\n depth,\n overrideAccess,\n req,\n } as never),\n )\n\n return getGlobalLanguageTab<TDoc>({\n doc,\n language,\n })\n}\n\nexport const updateGlobalByLanguageWithPayload = async <TDoc extends DocumentData = DocumentData>({\n slug,\n data,\n depth,\n language,\n overrideAccess = true,\n payload,\n req,\n}: {\n data: Record<string, unknown>\n depth?: number\n language: string\n overrideAccess?: boolean\n payload: Payload\n req?: PayloadRequest\n slug: GlobalSlug\n}): Promise<TDoc> => {\n validateGlobalLanguage({ slug, language, payload })\n\n const currentDoc = asDocument(\n await payload.findGlobal({\n slug,\n depth: 0,\n overrideAccess,\n req,\n } as never),\n )\n const currentLanguageData = asDocument(currentDoc[language])\n\n const doc = asDocument(\n await payload.updateGlobal({\n slug,\n data: {\n [language]: {\n ...currentLanguageData,\n ...data,\n },\n },\n depth,\n overrideAccess,\n req,\n } as never),\n )\n\n return getGlobalLanguageTab<TDoc>({\n doc,\n language,\n })\n}\n\nconst fieldAffectsData = (field: Field): field is { name: string } & Field =>\n 'name' in field && typeof field.name === 'string' && field.type !== 'ui'\n\nexport const duplicateDocumentData = ({\n collection,\n doc,\n duplicateExcludeFields,\n fieldNames,\n targetLanguage,\n}: {\n collection: CollectionConfig\n doc: DocumentData\n duplicateExcludeFields: string[]\n fieldNames: MultilangFieldNames\n targetLanguage: string\n}): DocumentData => {\n const excluded = new Set([\n fieldNames.group,\n fieldNames.language,\n fieldNames.meta,\n ...duplicateExcludeFields,\n ])\n\n const data: DocumentData = {}\n\n for (const field of collection.fields || []) {\n if (!fieldAffectsData(field) || excluded.has(field.name)) {\n continue\n }\n\n if (!(field.name in doc)) {\n continue\n }\n\n data[field.name] = doc[field.name]\n }\n\n if (typeof data.slug === 'string' && data.slug.length > 0) {\n data.slug = `${data.slug}-${targetLanguage}`\n }\n\n return data\n}\n\nexport type PayloadFactory = () => Payload | Promise<Payload>\n\nexport type GetDocumentTranslationsArgs = {\n collection: CollectionSlug\n fieldNames?: MultilangFieldNames\n id: number | string\n overrideAccess?: boolean\n req?: PayloadRequest\n}\n\nexport type GetDocumentTranslationArgs = {\n language: string\n} & GetDocumentTranslationsArgs\n\nexport type FindGlobalByLanguageArgs = {\n depth?: number\n language: string\n overrideAccess?: boolean\n req?: PayloadRequest\n slug: GlobalSlug\n}\n\nexport type UpdateGlobalByLanguageArgs = {\n data: Record<string, unknown>\n} & FindGlobalByLanguageArgs\n\nexport type MultilangHelpers = {\n findGlobalByLanguage: <TDoc extends DocumentData = DocumentData>(\n args: FindGlobalByLanguageArgs,\n ) => Promise<TDoc>\n getDocumentTranslation: <TDoc extends DocumentData = DocumentData>(\n args: GetDocumentTranslationArgs,\n ) => Promise<TDoc | undefined>\n getDocumentTranslations: <TDoc extends DocumentData = DocumentData>(\n args: GetDocumentTranslationsArgs,\n ) => Promise<TranslationState<TDoc>>\n getLanguages: () => Promise<MultilangLanguage[]>\n updateGlobalByLanguage: <TDoc extends DocumentData = DocumentData>(\n args: UpdateGlobalByLanguageArgs,\n ) => Promise<TDoc>\n}\n\nexport const createMultilangHelpers = ({\n getPayload,\n}: {\n getPayload: PayloadFactory\n}): MultilangHelpers => {\n const resolvePayload = async () => getPayload()\n\n return {\n findGlobalByLanguage: async <TDoc extends DocumentData = DocumentData>({\n slug,\n depth,\n language,\n overrideAccess,\n req,\n }: FindGlobalByLanguageArgs): Promise<TDoc> =>\n findGlobalByLanguageWithPayload<TDoc>({\n slug,\n depth,\n language,\n overrideAccess,\n payload: await resolvePayload(),\n req,\n }),\n\n getDocumentTranslation: async <TDoc extends DocumentData = DocumentData>(\n args: GetDocumentTranslationArgs,\n ): Promise<TDoc | undefined> =>\n getDocumentTranslationWithPayload<TDoc>({\n ...args,\n payload: await resolvePayload(),\n }),\n\n getDocumentTranslations: async <TDoc extends DocumentData = DocumentData>(\n args: GetDocumentTranslationsArgs,\n ): Promise<TranslationState<TDoc>> =>\n getDocumentTranslationsWithPayload<TDoc>({\n ...args,\n payload: await resolvePayload(),\n }),\n\n getLanguages: async (): Promise<MultilangLanguage[]> =>\n getLanguages({ payload: await resolvePayload() }),\n\n updateGlobalByLanguage: async <TDoc extends DocumentData = DocumentData>({\n slug,\n data,\n depth,\n language,\n overrideAccess,\n req,\n }: UpdateGlobalByLanguageArgs): Promise<TDoc> =>\n updateGlobalByLanguageWithPayload<TDoc>({\n slug,\n data,\n depth,\n language,\n overrideAccess,\n payload: await resolvePayload(),\n req,\n }),\n }\n}\n"],"names":["DEFAULT_FIELD_NAMES","asDocument","value","getStringValue","length","undefined","getID","normalizeLanguage","doc","data","code","trim","toLowerCase","id","name","active","direction","flagLabel","isDefault","locale","order","getLanguagesFromCustom","custom","payloadMultilang","languages","Array","isArray","map","filter","language","getLanguages","config","payload","admin","Error","getDefaultLanguage","configuredLanguages","find","withLanguage","fieldName","where","languageWhere","equals","and","getMultilangDocumentLanguage","fallback","localizedSlugQuery","slug","slugFieldName","status","constraints","unshift","push","_status","draft","getDocumentTranslationsWithPayload","collection","fieldNames","overrideAccess","req","source","findByID","depth","group","translations","result","limit","docs","reduce","acc","translation","getGroupTranslationsWithPayload","getDocumentTranslationWithPayload","state","GLOBAL_SYSTEM_FIELDS","Set","getGlobalPayloadMultilangCustom","global","globals","item","validateGlobalLanguage","some","candidate","getGlobalLanguageTab","languageData","systemData","Object","entries","key","has","findGlobalByLanguageWithPayload","findGlobal","updateGlobalByLanguageWithPayload","currentDoc","currentLanguageData","updateGlobal","fieldAffectsData","field","type","duplicateDocumentData","duplicateExcludeFields","targetLanguage","excluded","meta","fields","createMultilangHelpers","getPayload","resolvePayload","findGlobalByLanguage","getDocumentTranslation","args","getDocumentTranslations","updateGlobalByLanguage"],"mappings":"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"}
|
package/docs/configuration.md
CHANGED
|
@@ -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 |
|
|
@@ -43,6 +45,20 @@ export default buildConfig({
|
|
|
43
45
|
| `duplicateExcludeFields` | `string[]` | Extra top-level fields to skip when a new translation duplicates source document data. |
|
|
44
46
|
| `disabled` | `boolean` | Returns the Payload config unchanged when true. Useful for temporary opt-out in specific environments. |
|
|
45
47
|
|
|
48
|
+
## Content Model
|
|
49
|
+
|
|
50
|
+
Translated collection entries are independent Payload documents. The plugin adds hidden metadata fields:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
{
|
|
54
|
+
language: '_multilangLanguage',
|
|
55
|
+
group: '_multilangGroup',
|
|
56
|
+
meta: '_multilangMeta',
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Do not mark content fields as `localized: true` for this plugin model. Put translated text directly on each language document instead.
|
|
61
|
+
|
|
46
62
|
## Languages
|
|
47
63
|
|
|
48
64
|
```ts
|
|
@@ -144,7 +160,7 @@ payloadMultilang({
|
|
|
144
160
|
})
|
|
145
161
|
```
|
|
146
162
|
|
|
147
|
-
Read and update localized global values with `
|
|
163
|
+
Read and update localized global values with `findGlobalByLanguageWithPayload`, `updateGlobalByLanguageWithPayload`, or helpers from `createMultilangHelpers()`.
|
|
148
164
|
|
|
149
165
|
## Custom Field Names
|
|
150
166
|
|
|
@@ -190,3 +206,18 @@ payloadMultilang({
|
|
|
190
206
|
```
|
|
191
207
|
|
|
192
208
|
When using custom field names, pass the same names to helpers that accept `fieldNames` or `fieldName`.
|
|
209
|
+
|
|
210
|
+
## Slug Uniqueness
|
|
211
|
+
|
|
212
|
+
Because translations are separate documents, decide whether slugs are unique globally or per language.
|
|
213
|
+
|
|
214
|
+
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:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
{
|
|
218
|
+
_multilangLanguage: language,
|
|
219
|
+
slug,
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
Use a tiny app-owned module for convenience helpers.
|
|
21
32
|
|
|
22
33
|
```ts
|
|
23
|
-
|
|
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
|
-
|
|
43
|
+
The factory returns:
|
|
27
44
|
|
|
28
45
|
```ts
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
##
|
|
91
|
+
## Document Helpers
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
`getDocumentTranslationsWithPayload` returns the source document and all linked translations for a localized collection document.
|
|
83
94
|
|
|
84
95
|
```ts
|
|
85
|
-
const state = await
|
|
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
|
-
|
|
105
|
+
`getDocumentTranslationWithPayload` returns one target-language document.
|
|
92
106
|
|
|
93
107
|
```ts
|
|
94
|
-
|
|
95
|
-
collection:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
##
|
|
127
|
+
## Global Helpers
|
|
113
128
|
|
|
114
|
-
|
|
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
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
+
Default metadata field names are exported so apps do not need to hardcode them.
|
|
159
153
|
|
|
160
154
|
```ts
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
163
|
+
Values:
|
|
171
164
|
|
|
172
165
|
```ts
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}): Promise<TDoc>
|
|
166
|
+
{
|
|
167
|
+
language: '_multilangLanguage',
|
|
168
|
+
group: '_multilangGroup',
|
|
169
|
+
meta: '_multilangMeta',
|
|
170
|
+
}
|
|
179
171
|
```
|
|
180
172
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
## Global Helpers With Payload
|
|
173
|
+
## Preview Language Helper
|
|
184
174
|
|
|
185
|
-
Use
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
187
|
+
## Localized Slug Query
|
|
188
|
+
|
|
189
|
+
`localizedSlugQuery` builds the common Local API query for language + slug + status.
|
|
209
190
|
|
|
210
191
|
```ts
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
`
|
|
205
|
+
Use `status: 'draft'` to include `draft: true` and `_status = draft`, or `status: 'any'` to omit `_status`.
|
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
147
|
```
|
|
91
148
|
|
|
92
|
-
|
|
149
|
+
For draft previews:
|
|
93
150
|
|
|
94
151
|
```ts
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
language
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Use `getDocumentTranslation()` when you only need one target language.
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
import { getDocumentTranslation } from '@roxxel/payload-multilang'
|
|
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,44 +199,38 @@ payloadMultilang({
|
|
|
142
199
|
})
|
|
143
200
|
```
|
|
144
201
|
|
|
145
|
-
|
|
202
|
+
Read one language as flat data:
|
|
146
203
|
|
|
147
204
|
```ts
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const settings = await findGlobalByLanguage({
|
|
205
|
+
const settings = await multilang.findGlobalByLanguage({
|
|
151
206
|
slug: 'site-settings',
|
|
152
207
|
language: 'uk',
|
|
153
208
|
})
|
|
154
|
-
|
|
155
|
-
console.log(settings.title)
|
|
156
209
|
```
|
|
157
210
|
|
|
158
|
-
|
|
211
|
+
In request-scoped code:
|
|
159
212
|
|
|
160
213
|
```ts
|
|
161
|
-
import {
|
|
214
|
+
import { findGlobalByLanguageWithPayload } from '@roxxel/payload-multilang'
|
|
162
215
|
|
|
163
|
-
await
|
|
216
|
+
const settings = await findGlobalByLanguageWithPayload({
|
|
164
217
|
slug: 'site-settings',
|
|
165
218
|
language: 'uk',
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
219
|
+
payload: req.payload,
|
|
220
|
+
req,
|
|
221
|
+
overrideAccess: false,
|
|
169
222
|
})
|
|
170
223
|
```
|
|
171
224
|
|
|
172
|
-
|
|
225
|
+
Update one language without replacing other language values:
|
|
173
226
|
|
|
174
227
|
```ts
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const settings = await findGlobalByLanguageWithPayload({
|
|
228
|
+
await multilang.updateGlobalByLanguage({
|
|
178
229
|
slug: 'site-settings',
|
|
179
230
|
language: 'uk',
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
231
|
+
data: {
|
|
232
|
+
title: 'Ukrainian title',
|
|
233
|
+
},
|
|
183
234
|
})
|
|
184
235
|
```
|
|
185
236
|
|
|
@@ -228,38 +279,6 @@ payloadMultilang({
|
|
|
228
279
|
})
|
|
229
280
|
```
|
|
230
281
|
|
|
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
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
Adjust the URL shape to match your frontend routing.
|
|
262
|
-
|
|
263
282
|
## Generate Types
|
|
264
283
|
|
|
265
284
|
After enabling collections, globals, or changing field names, regenerate Payload types.
|
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":""}
|