@sanity/document-internationalization 1.0.0 → 1.0.2

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 (114) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +89 -47
  3. package/lib/index.esm.js +2 -0
  4. package/lib/index.esm.js.map +1 -0
  5. package/lib/index.js +2 -0
  6. package/lib/index.js.map +1 -0
  7. package/lib/src/index.d.ts +200 -0
  8. package/package.json +99 -43
  9. package/src/actions/DeleteWithi18nAction.tsx +118 -0
  10. package/src/actions/DuplicateWithi18nAction.tsx +77 -0
  11. package/src/actions/PublishWithi18nAction.ts +118 -0
  12. package/src/actions/index.ts +42 -0
  13. package/src/badges/LanguageBadge.tsx +22 -0
  14. package/src/constants/I18nDelimiter.ts +1 -0
  15. package/src/constants/I18nPrefix.ts +1 -0
  16. package/src/constants/IdStructure.ts +4 -0
  17. package/src/constants/LanguageCultures.ts +902 -0
  18. package/src/constants/ReferenceBehavior.ts +5 -0
  19. package/src/constants/SupportedEmojiFlagCodes.ts +251 -0
  20. package/src/constants/UiMessages.ts +56 -0
  21. package/src/constants/index.ts +7 -0
  22. package/src/documentI18n.tsx +33 -0
  23. package/src/hooks/index.ts +2 -0
  24. package/src/hooks/useConfig.ts +22 -0
  25. package/src/hooks/useDelayedFlag.ts +31 -0
  26. package/src/index.ts +6 -1
  27. package/src/language-select/components/LanguageSelect/LanguageConfigContext.tsx +4 -0
  28. package/src/language-select/components/LanguageSelect/LanguageSelect.tsx +188 -0
  29. package/src/language-select/components/LanguageSelect/LanguageSelectContext.ts +16 -0
  30. package/src/language-select/components/LanguageSelect/LanguageSelectLabel.tsx +12 -0
  31. package/src/language-select/components/LanguageSelect/LanguageSelectList.tsx +84 -0
  32. package/src/language-select/components/LanguageSelect/LanguageSelectListItem.tsx +251 -0
  33. package/src/language-select/components/LanguageSelect/index.tsx +25 -0
  34. package/src/language-select/components/SingleFlag/SingleFlag.tsx +44 -0
  35. package/src/language-select/components/SingleFlag/allEmojiFlagCodes.ts +251 -0
  36. package/src/language-select/components/SingleFlag/index.ts +1 -0
  37. package/src/language-select/components/SplitPaneIcon/SplitPaneIcon.tsx +25 -0
  38. package/src/language-select/components/SplitPaneIcon/index.ts +1 -0
  39. package/src/language-select/components/index.ts +2 -0
  40. package/src/language-select/hooks/index.ts +1 -0
  41. package/src/language-select/hooks/useLanguages.ts +37 -0
  42. package/src/language-select/hooks/useListeningQuery.ts +66 -0
  43. package/src/structure/IDefaultDocumentNodeStructureProps.ts +4 -0
  44. package/src/structure/components/MaintenanceTab/MaintenanceTab.tsx +13 -0
  45. package/src/structure/components/MaintenanceTab/MaintenanceTabContent.tsx +259 -0
  46. package/src/structure/components/MaintenanceTab/index.ts +1 -0
  47. package/src/structure/components/MaintenanceTabResult/MaintenanceTabResult.tsx +44 -0
  48. package/src/structure/components/MaintenanceTabResult/index.ts +1 -0
  49. package/src/structure/components/MaintenanceTabTypeSelector/MaintenanceTabTypeSelector.tsx +56 -0
  50. package/src/structure/components/MaintenanceTabTypeSelector/index.ts +1 -0
  51. package/src/structure/hooks/index.ts +1 -0
  52. package/src/structure/hooks/useDocumentsInformation.ts +110 -0
  53. package/src/structure/index.tsx +120 -0
  54. package/src/structure/utils/fixBaseDocumentRefs.ts +30 -0
  55. package/src/structure/utils/fixBaseLanguageMismatch.ts +21 -0
  56. package/src/structure/utils/fixIdStructureMismatchDocuments.ts +72 -0
  57. package/src/structure/utils/fixLanguageFields.ts +32 -0
  58. package/src/structure/utils/fixOrphanedDocuments.ts +22 -0
  59. package/src/structure/utils/fixTranslationRefs.ts +50 -0
  60. package/src/structure/utils/index.ts +6 -0
  61. package/src/types/IEditState.ts +6 -0
  62. package/src/types/IExtendedLanguageObject.ts +6 -0
  63. package/src/types/ILanguageObject.ts +4 -0
  64. package/src/types/ILanguageQuery.ts +13 -0
  65. package/src/types/IResolverProps.ts +10 -0
  66. package/src/types/ITranslationRef.ts +6 -0
  67. package/src/types/IType.ts +40 -0
  68. package/src/types/IUseDocumentOperationResult.ts +11 -0
  69. package/src/types/TFieldNamesConfig.ts +5 -0
  70. package/src/types/TLanguage.ts +3 -0
  71. package/src/types/TLanguagesOption.ts +4 -0
  72. package/src/types/TSchema.ts +6 -0
  73. package/src/types/Ti18nConfig.ts +33 -0
  74. package/src/types/Ti18nDocument.ts +14 -0
  75. package/src/types/Ti18nSchema.ts +13 -0
  76. package/src/types/index.ts +15 -0
  77. package/src/utils/applyConfig.ts +43 -0
  78. package/src/utils/baseToTop.ts +5 -0
  79. package/src/utils/buildDocId.ts +8 -0
  80. package/src/utils/createSanityReference.ts +11 -0
  81. package/src/utils/getBaseIdFromId.ts +20 -0
  82. package/src/utils/getBaseLanguage.ts +6 -0
  83. package/src/utils/getFlag.ts +38 -0
  84. package/src/utils/getLanguageFromDocument.ts +9 -0
  85. package/src/utils/getLanguageFromId.ts +13 -0
  86. package/src/utils/getLanguagesFromOption.ts +40 -0
  87. package/src/utils/getTranslationsForId.ts +39 -0
  88. package/src/utils/index.ts +14 -0
  89. package/src/utils/makeObjectKey.ts +3 -0
  90. package/src/utils/normalizeLanguageList.ts +26 -0
  91. package/src/utils/serializePath.ts +11 -0
  92. package/src/utils/updateIntlFieldsForDocument.ts +79 -0
  93. package/src/utils/useSanityClient.ts +6 -0
  94. package/src/validators/index.ts +1 -0
  95. package/src/validators/isSlugUnique.ts +56 -0
  96. package/src/withDocumentI18nPlugin.ts +24 -0
  97. package/v2-incompatible.js +1 -1
  98. package/lib/cjs/index.js +0 -539
  99. package/lib/cjs/index.js.map +0 -1
  100. package/lib/esm/index.js +0 -532
  101. package/lib/esm/index.js.map +0 -1
  102. package/lib/types/index.d.ts +0 -12
  103. package/lib/types/index.d.ts.map +0 -1
  104. package/src/LanguageManage.tsx +0 -25
  105. package/src/LanguageOption.tsx +0 -158
  106. package/src/LanguagePatch.tsx +0 -61
  107. package/src/MenuButton.tsx +0 -101
  108. package/src/badges/index.tsx +0 -23
  109. package/src/constants.ts +0 -1
  110. package/src/hooks/useLanguageMetadata.tsx +0 -21
  111. package/src/hooks/useOpenInNewPane.tsx +0 -27
  112. package/src/plugin.tsx +0 -104
  113. package/src/schema/translation/metadata.ts +0 -42
  114. package/src/types.ts +0 -24
@@ -0,0 +1,120 @@
1
+ import {EarthGlobeIcon} from '@sanity/icons'
2
+ import {Schema, SchemaType} from 'sanity'
3
+ import {DocumentListBuilder, ListItemBuilder, StructureBuilder} from 'sanity/desk'
4
+ import {applyConfig} from '../utils'
5
+ import {I18nDelimiter, I18nPrefix, IdStructure, UiMessages} from '../constants'
6
+ import {Ti18nConfig} from '../types'
7
+ import {createMaintenanceTabComponent} from './components/MaintenanceTab'
8
+
9
+ export interface StructureConfig {
10
+ S: StructureBuilder
11
+ config: Ti18nConfig
12
+ schema: Schema & {
13
+ i18n?: Ti18nConfig
14
+ }
15
+ }
16
+
17
+ const hasIcon = (schemaType?: SchemaType | string): boolean => {
18
+ if (!schemaType || typeof schemaType === 'string') {
19
+ return false
20
+ }
21
+ return Boolean(schemaType.icon)
22
+ }
23
+
24
+ export const getDefaultDocumentNode = (S: StructureBuilder) => {
25
+ return S.document()
26
+ }
27
+
28
+ export const getDocumentTypes = (props: StructureConfig) => {
29
+ const {S, schema} = props
30
+ const listItemsWithouti18n: ListItemBuilder[] = []
31
+ const listItemsWithi18n = S.documentTypeListItems().filter((l) => {
32
+ let schemaType = l.getSchemaType()
33
+ schemaType = typeof schemaType === 'string' ? schema.get(schemaType) : schemaType
34
+
35
+ const hasi18n = schemaType && (schemaType as unknown as {i18n: boolean}).i18n
36
+ if (!hasi18n) listItemsWithouti18n.push(l)
37
+ return hasi18n
38
+ })
39
+ return {
40
+ withoutI18n: listItemsWithouti18n,
41
+ withI18n: listItemsWithi18n,
42
+ }
43
+ }
44
+
45
+ export const getMaintenanceTabComponent = (props: StructureConfig) => {
46
+ const {S} = props
47
+ const MaintenanceTab = createMaintenanceTabComponent(props)
48
+ return S.component(MaintenanceTab)
49
+ .title(UiMessages.translationsMaintenance.title)
50
+ .id(`__i18n_translations_maintenance_tab`)
51
+ }
52
+
53
+ export const getMaintenanceListItem = (props: StructureConfig) => {
54
+ const {S} = props
55
+ return S.listItem()
56
+ .id(`__i18n_translations_maintenance_tab`)
57
+ .title(UiMessages.translationsMaintenance.title)
58
+ .icon(EarthGlobeIcon)
59
+ .child(getMaintenanceTabComponent(props))
60
+ }
61
+
62
+ export const getFilteredDocumentTypeListItems = (props: StructureConfig) => {
63
+ const {S, config: pluginConfig, schema} = props
64
+ const config = applyConfig(
65
+ pluginConfig,
66
+ typeof schema.i18n === 'object' ? schema.i18n : undefined
67
+ )
68
+ const types = getDocumentTypes(props)
69
+ const filterFns = {
70
+ [IdStructure.SUBPATH]: (list: ListItemBuilder, doc: DocumentListBuilder) =>
71
+ doc.filter('!(_id in path($path)) && !(_id in path($drafts)) && _type == $type').params({
72
+ path: `${I18nPrefix}.**`,
73
+ drafts: `drafts.${I18nPrefix}.**`,
74
+ type: list.getId(),
75
+ }),
76
+ [IdStructure.DELIMITER]: (list: ListItemBuilder, doc: DocumentListBuilder) =>
77
+ doc.filter('!(_id match $id) && _type == $type').params({
78
+ id: `*${I18nDelimiter}*`,
79
+ type: list.getId(),
80
+ }),
81
+ }
82
+
83
+ const items = [
84
+ ...types.withoutI18n,
85
+ ...types.withI18n.map((l) => {
86
+ const schemaType = l.getSchemaType()
87
+ const schemaTypeName = typeof schemaType === 'string' ? schemaType : schemaType?.name
88
+
89
+ return l.child(
90
+ filterFns[config.idStructure](
91
+ l,
92
+ S.documentList()
93
+ .id(l.getId() || '')
94
+ .title(l.getTitle() || '')
95
+ .schemaType(schemaTypeName ?? '')
96
+ .menuItems([...(schemaTypeName ? S.orderingMenuItemsForType(schemaTypeName) : [])])
97
+ )
98
+ )
99
+ }),
100
+ ]
101
+
102
+ if (config.withTranslationsMaintenance) {
103
+ items.splice(0, 0, getMaintenanceListItem(props))
104
+ }
105
+
106
+ return items.map((item) => item.serialize())
107
+ }
108
+
109
+ export function getDocumentList(props: StructureConfig) {
110
+ const {S} = props
111
+ const types = getDocumentTypes(props)
112
+ if (types.withI18n.length === 0) return S.defaults()
113
+
114
+ const items = getFilteredDocumentTypeListItems(props)
115
+ return S.list()
116
+ .id('__root__')
117
+ .title('Content')
118
+ .items(items)
119
+ .showIcons(items.some((item) => hasIcon(item.schemaType)))
120
+ }
@@ -0,0 +1,30 @@
1
+ import type {SanityDocument, SanityClient, Transaction} from '@sanity/client'
2
+ import {ReferenceBehavior} from '../../constants'
3
+ import {ApplyConfigResult, createSanityReference, getBaseIdFromId} from '../../utils'
4
+
5
+ export const fixBaseDocumentRefs = (
6
+ sanityClient: SanityClient,
7
+ config: ApplyConfigResult,
8
+ translatedDocuments: SanityDocument[]
9
+ ): Transaction => {
10
+ const transaction = sanityClient.transaction()
11
+
12
+ if (config.referenceBehavior !== ReferenceBehavior.DISABLED) {
13
+ const baseRefFieldName = config.fieldNames.baseReference
14
+ translatedDocuments.forEach((d) => {
15
+ if (!d[baseRefFieldName]) {
16
+ const baseId = getBaseIdFromId(d._id)
17
+ transaction.patch(d._id, {
18
+ set: {
19
+ [baseRefFieldName]: createSanityReference(
20
+ baseId,
21
+ config.referenceBehavior === ReferenceBehavior.WEAK
22
+ ),
23
+ },
24
+ })
25
+ }
26
+ })
27
+ }
28
+
29
+ return transaction
30
+ }
@@ -0,0 +1,21 @@
1
+ import type {SanityDocument, SanityClient, Transaction} from '@sanity/client'
2
+ import {ApplyConfigResult, getBaseLanguage, getLanguagesFromOption} from '../../utils'
3
+
4
+ export const fixBaseLanguageMismatch = async (
5
+ sanityClient: SanityClient,
6
+ config: ApplyConfigResult,
7
+ basedocuments: SanityDocument[]
8
+ ): Promise<Transaction> => {
9
+ const languages = await getLanguagesFromOption(sanityClient, config, config.languages)
10
+ const baseLanguage = getBaseLanguage(languages, config.base)
11
+ const langFieldName = config.fieldNames.lang
12
+ const transaction = sanityClient.transaction()
13
+ basedocuments.forEach((doc) => {
14
+ if (doc[langFieldName] !== baseLanguage?.id) {
15
+ transaction.patch(doc._id, {
16
+ set: {[langFieldName]: baseLanguage?.id}, // eslint-disable-line
17
+ })
18
+ }
19
+ })
20
+ return transaction
21
+ }
@@ -0,0 +1,72 @@
1
+ import type {SanityDocument, Transaction} from '@sanity/client'
2
+ import chunk from 'just-split'
3
+ import {SanityClient} from '@sanity/client'
4
+ import {ReferenceBehavior} from '../../constants'
5
+ import {
6
+ ApplyConfigResult,
7
+ buildDocId,
8
+ createSanityReference,
9
+ getBaseIdFromId,
10
+ getLanguageFromDocument,
11
+ } from '../../utils'
12
+
13
+ export const fixIdStructureMismatchDocuments = (
14
+ sanityClient: SanityClient,
15
+ config: ApplyConfigResult,
16
+ schema: string,
17
+ documents: SanityDocument[]
18
+ ): Transaction[] => {
19
+ const refsFieldName = config.fieldNames.references
20
+
21
+ // remove old refs
22
+ const existingBaseDocumentIds = new Set(documents.map((d) => getBaseIdFromId(d._id)))
23
+ const removeOldRefsTransaction = sanityClient.transaction()
24
+ existingBaseDocumentIds.forEach((id) => {
25
+ removeOldRefsTransaction.patch(id, {
26
+ set: {[refsFieldName]: []},
27
+ })
28
+ })
29
+
30
+ // create new document ids
31
+ const newDocumentTransactions = chunk(
32
+ documents.filter((d) => d._id !== getBaseIdFromId(d._id)),
33
+ 100
34
+ ).map((documentsChunk) => {
35
+ const transaction = sanityClient.transaction()
36
+ documentsChunk.forEach((d) => {
37
+ const baseId = getBaseIdFromId(d._id)
38
+ const lang = getLanguageFromDocument(d, config)
39
+ if (lang) {
40
+ const newId = buildDocId(config, baseId, lang)
41
+ transaction.createIfNotExists({
42
+ ...d,
43
+ _id: newId,
44
+ _type: schema,
45
+ })
46
+ transaction.delete(d._id)
47
+
48
+ // patch base document with updated refs
49
+ if (config.referenceBehavior !== ReferenceBehavior.DISABLED) {
50
+ transaction.patch(baseId, {setIfMissing: {[refsFieldName]: []}})
51
+ transaction.patch(baseId, {
52
+ insert: {
53
+ after: `${refsFieldName}[-1]`,
54
+ items: [
55
+ {
56
+ _key: lang,
57
+ ...createSanityReference(
58
+ newId,
59
+ config.referenceBehavior === ReferenceBehavior.WEAK
60
+ ),
61
+ },
62
+ ],
63
+ },
64
+ })
65
+ }
66
+ }
67
+ })
68
+ return transaction
69
+ })
70
+
71
+ return [removeOldRefsTransaction, ...newDocumentTransactions]
72
+ }
@@ -0,0 +1,32 @@
1
+ import type {SanityClient, SanityDocument, Transaction} from '@sanity/client'
2
+ import type {Schema} from 'sanity'
3
+ import {Ti18nSchema} from '../../types'
4
+ import {ApplyConfigResult, getLanguageFromId} from '../../utils'
5
+
6
+ export const fixLanguageFields = (
7
+ sanityClient: SanityClient,
8
+ config: ApplyConfigResult,
9
+ schemaRegistry: Schema,
10
+ documents: SanityDocument[]
11
+ ): Transaction => {
12
+ const langFieldName = config.fieldNames?.lang
13
+ const transaction = sanityClient.transaction()
14
+
15
+ documents.forEach((d) => {
16
+ const schemaObject = schemaRegistry.get(d._type) as Ti18nSchema
17
+ const base =
18
+ (typeof schemaObject.i18n === 'object' ? schemaObject.i18n.base : undefined) || config.base
19
+ if (!d[langFieldName]) {
20
+ // @README keep the language from ID behavior
21
+ // because in this case we expect the language field not to be available
22
+ const language = getLanguageFromId(d._id) || base
23
+ transaction.patch(d._id, {
24
+ set: {
25
+ [langFieldName]: language,
26
+ },
27
+ })
28
+ }
29
+ })
30
+
31
+ return transaction
32
+ }
@@ -0,0 +1,22 @@
1
+ import type {SanityClient, SanityDocument, Transaction} from '@sanity/client'
2
+ import type {Reference} from 'sanity'
3
+ import {ApplyConfigResult} from '../../utils'
4
+
5
+ export const fixOrphanedDocuments = (
6
+ sanityClient: SanityClient,
7
+ config: ApplyConfigResult,
8
+ basedocuments: SanityDocument[],
9
+ translatedDocuments: SanityDocument[]
10
+ ): Transaction => {
11
+ const transaction = sanityClient.transaction()
12
+ translatedDocuments.forEach((d) => {
13
+ const base = basedocuments.find(
14
+ (doc) =>
15
+ (Array.isArray(d?.[config.fieldNames.references]) &&
16
+ d?.[config.fieldNames.references]?.some((ref: Reference) => ref._ref === d._id)) ||
17
+ doc._id === d?.[config.fieldNames.baseReference]?._ref
18
+ )
19
+ if (!base) transaction.delete(d._id)
20
+ })
21
+ return transaction
22
+ }
@@ -0,0 +1,50 @@
1
+ import chunk from 'lodash/chunk'
2
+ import compact from 'lodash/compact'
3
+ import {SanityClient, Transaction, SanityDocument} from '@sanity/client'
4
+ import type {Reference} from 'sanity'
5
+ import {ReferenceBehavior} from '../../constants'
6
+ import {
7
+ ApplyConfigResult,
8
+ createSanityReference,
9
+ getBaseIdFromId,
10
+ getLanguageFromDocument,
11
+ } from '../../utils'
12
+
13
+ export const fixTranslationRefs = (
14
+ sanityClient: SanityClient,
15
+ config: ApplyConfigResult,
16
+ baseDocuments: SanityDocument[],
17
+ translatedDocuments: SanityDocument[]
18
+ // eslint-disable-next-line max-params
19
+ ): Transaction[] => {
20
+ const refsFieldName = config.fieldNames.references
21
+ const transactions = chunk(baseDocuments, 50).map((documentsChunk) => {
22
+ const transaction = sanityClient.transaction()
23
+ documentsChunk.forEach((d) => {
24
+ let translatedRefs: Reference[] = []
25
+ const relevantTranslations = translatedDocuments.filter(
26
+ (dx) => getBaseIdFromId(dx._id) === d._id
27
+ )
28
+ if (config.referenceBehavior !== ReferenceBehavior.DISABLED) {
29
+ translatedRefs = compact(
30
+ relevantTranslations.map((doc) => {
31
+ const lang = getLanguageFromDocument(doc, config)
32
+ if (!lang) return null
33
+ return {
34
+ _key: lang,
35
+ ...createSanityReference(
36
+ doc._id,
37
+ config.referenceBehavior === ReferenceBehavior.WEAK
38
+ ),
39
+ }
40
+ }, {})
41
+ )
42
+ }
43
+ transaction.patch(d._id, {
44
+ set: {[refsFieldName]: translatedRefs},
45
+ })
46
+ })
47
+ return transaction
48
+ })
49
+ return transactions
50
+ }
@@ -0,0 +1,6 @@
1
+ export * from './fixIdStructureMismatchDocuments'
2
+ export * from './fixLanguageFields'
3
+ export * from './fixTranslationRefs'
4
+ export * from './fixOrphanedDocuments'
5
+ export * from './fixBaseLanguageMismatch'
6
+ export * from './fixBaseDocumentRefs'
@@ -0,0 +1,6 @@
1
+ import type {SanityDocument} from '@sanity/client'
2
+
3
+ export interface IEditState<T extends Record<string, any> = Record<string, any>> {
4
+ draft?: SanityDocument<T>
5
+ published?: SanityDocument<T>
6
+ }
@@ -0,0 +1,6 @@
1
+ import type {ILanguageObject} from './ILanguageObject'
2
+
3
+ export interface IExtendedLanguageObject extends ILanguageObject {
4
+ isCurrentLanguage?: boolean
5
+ isBase?: boolean
6
+ }
@@ -0,0 +1,4 @@
1
+ export interface ILanguageObject {
2
+ id: string
3
+ title: string
4
+ }
@@ -0,0 +1,13 @@
1
+ export interface ILanguageQuery {
2
+ query: string
3
+ value:
4
+ | string
5
+ | {
6
+ id: string
7
+ title: string
8
+ }
9
+ | {
10
+ name: string
11
+ title: string
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ import {SanityDocument} from '@sanity/client'
2
+
3
+ export interface IResolverProps<T extends Record<string, any> = Record<string, any>> {
4
+ id: string
5
+ type: string
6
+ liveEdit: boolean
7
+ draft?: SanityDocument<T>
8
+ published?: SanityDocument<T>
9
+ onComplete?: () => void
10
+ }
@@ -0,0 +1,6 @@
1
+ export interface ITranslationRef {
2
+ _key: string
3
+ _type: 'reference'
4
+ _ref: string
5
+ _weak?: boolean
6
+ }
@@ -0,0 +1,40 @@
1
+ import {TLanguagesOption} from './TLanguagesOption'
2
+ import {ObjectSchemaTypeWithOptions} from 'sanity'
3
+ export interface IField {
4
+ name: string
5
+ type: IType
6
+ readOnly?: boolean
7
+ }
8
+
9
+ export interface IFieldSet {
10
+ name: string
11
+ title: string
12
+ description: string
13
+ single: boolean
14
+ options: ObjectSchemaTypeWithOptions['options']
15
+ field: IField
16
+ fields: IField[]
17
+ }
18
+
19
+ export interface IType {
20
+ name: string
21
+ title: string
22
+ description: string
23
+ hidden?: boolean
24
+ level: number
25
+ type: string
26
+ jsonType: string
27
+ fields: IField[]
28
+ fieldsets: IFieldSet[]
29
+ options: ObjectSchemaTypeWithOptions['options'] & {
30
+ base?: string
31
+ i18n?: boolean
32
+ languages?: TLanguagesOption
33
+ css?: (classNames: Record<string, string>) => string
34
+ messages?: {
35
+ loading?: string
36
+ missingTranslations?: string
37
+ }
38
+ [key: string]: any
39
+ }
40
+ }
@@ -0,0 +1,11 @@
1
+ type ExecutablAction = {
2
+ disabled?: string
3
+ execute: (...args: any[]) => void
4
+ }
5
+
6
+ export interface IUseDocumentOperationResult {
7
+ patch: ExecutablAction
8
+ publish: ExecutablAction
9
+ duplicate: ExecutablAction
10
+ delete: ExecutablAction
11
+ }
@@ -0,0 +1,5 @@
1
+ export type TFieldNamesConfig = {
2
+ lang?: string
3
+ references?: string
4
+ baseReference?: string
5
+ }
@@ -0,0 +1,3 @@
1
+ import {ILanguageObject} from './ILanguageObject'
2
+
3
+ export type Tlanguage = string | ILanguageObject
@@ -0,0 +1,4 @@
1
+ import {ILanguageQuery} from './ILanguageQuery'
2
+ import {ILanguageObject} from './ILanguageObject'
3
+
4
+ export type TLanguagesOption = (string | ILanguageObject)[] | ILanguageQuery
@@ -0,0 +1,6 @@
1
+ export type TSchema<T = any> = T & {
2
+ name: string
3
+ title: string
4
+ icon?: any
5
+ fields: any[]
6
+ }
@@ -0,0 +1,33 @@
1
+ import {SanityDocument, SchemaType} from 'sanity'
2
+ import type {ComponentType, FC} from 'react'
3
+ import {IdStructure, ReferenceBehavior} from '../constants'
4
+ import {TLanguagesOption} from './TLanguagesOption'
5
+ import {TFieldNamesConfig} from './TFieldNamesConfig'
6
+ import {ILanguageObject} from './ILanguageObject'
7
+
8
+ export type Ti18nConfig = {
9
+ base?: string
10
+ languages?: TLanguagesOption
11
+ idStructure?: IdStructure
12
+ referenceBehavior?: ReferenceBehavior
13
+ fieldNames?: TFieldNamesConfig
14
+ withTranslationsMaintenance?: boolean
15
+ languagesLoader?: (
16
+ languages: ILanguageObject[],
17
+ doc: SanityDocument | undefined
18
+ ) => Promise<ILanguageObject[]> | ILanguageObject[]
19
+ shouldReload?: (doc: SanityDocument | undefined | null) => boolean
20
+ fallbackLanguageSelect?: FC<{
21
+ schemaType?: SchemaType & {
22
+ i18n?: boolean | Ti18nConfig
23
+ }
24
+ }>
25
+ customFlagComponents?: Record<
26
+ string,
27
+ ComponentType<{
28
+ className?: string
29
+ code?: string
30
+ langCulture?: string
31
+ }>
32
+ >
33
+ }
@@ -0,0 +1,14 @@
1
+ import type {SanityDocument} from '@sanity/client'
2
+
3
+ // @README these types are not per-se accurate as field names can
4
+ // be manually configured and are not always these defaults
5
+ export type Ti18nDocument<D extends Record<string, any> = Record<string, any>> =
6
+ SanityDocument<D> & {
7
+ __i18n_lang?: string
8
+ __i18n_refs?: {
9
+ _key: string
10
+ _type: 'reference'
11
+ _ref: string
12
+ _weak: boolean
13
+ }[]
14
+ }
@@ -0,0 +1,13 @@
1
+ import {SchemaType} from 'sanity'
2
+ import {Ti18nConfig} from './Ti18nConfig'
3
+
4
+ declare module '@sanity/types' {
5
+ // makes i18n property allowed on document when using defineTyp/defineField/defineArrayMember
6
+ export interface DocumentDefinition {
7
+ i18n?: boolean | Ti18nConfig
8
+ }
9
+ }
10
+
11
+ export type Ti18nSchema = SchemaType & {
12
+ i18n: boolean | Ti18nConfig
13
+ }
@@ -0,0 +1,15 @@
1
+ export * from './ILanguageObject'
2
+ export * from './IExtendedLanguageObject'
3
+ export * from './ILanguageQuery'
4
+ export * from './IType'
5
+ export * from './TLanguage'
6
+ export * from './IResolverProps'
7
+ export * from './TLanguagesOption'
8
+ export * from './TSchema'
9
+ export * from './Ti18nSchema'
10
+ export * from './Ti18nConfig'
11
+ export * from './Ti18nDocument'
12
+ export * from './TFieldNamesConfig'
13
+ export * from './IUseDocumentOperationResult'
14
+ export * from './ITranslationRef'
15
+ export * from './IEditState'
@@ -0,0 +1,43 @@
1
+ import {IdStructure, ReferenceBehavior} from '../constants'
2
+ import type {Ti18nConfig} from '../types'
3
+
4
+ type ApplyConfigOptionalKeys =
5
+ | 'shouldReload'
6
+ | 'languagesLoader'
7
+ | 'fallbackLanguageSelect'
8
+ | 'customFlagComponents'
9
+ export type ApplyConfigResult = Omit<
10
+ Required<{
11
+ [K in keyof Ti18nConfig]: Required<Ti18nConfig[K]>
12
+ }>,
13
+ ApplyConfigOptionalKeys
14
+ > &
15
+ Pick<Ti18nConfig, ApplyConfigOptionalKeys>
16
+
17
+ export function applyConfig(
18
+ baseConfig?: Ti18nConfig,
19
+ incomingConfig?: Ti18nConfig | null
20
+ ): ApplyConfigResult {
21
+ return {
22
+ base: incomingConfig?.base || baseConfig?.base || '',
23
+ languages: incomingConfig?.languages || baseConfig?.languages || [],
24
+ idStructure: baseConfig?.idStructure || IdStructure.DELIMITER,
25
+ referenceBehavior: baseConfig?.referenceBehavior || ReferenceBehavior.STRONG,
26
+ withTranslationsMaintenance: baseConfig?.withTranslationsMaintenance === true,
27
+ shouldReload: baseConfig?.shouldReload,
28
+ languagesLoader: baseConfig?.languagesLoader,
29
+ fallbackLanguageSelect: baseConfig?.fallbackLanguageSelect,
30
+ customFlagComponents: baseConfig?.customFlagComponents,
31
+ fieldNames: {
32
+ lang: incomingConfig?.fieldNames?.lang || baseConfig?.fieldNames?.lang || '__i18n_lang',
33
+ references:
34
+ incomingConfig?.fieldNames?.references ||
35
+ baseConfig?.fieldNames?.references ||
36
+ '__i18n_refs',
37
+ baseReference:
38
+ incomingConfig?.fieldNames?.baseReference ||
39
+ baseConfig?.fieldNames?.baseReference ||
40
+ '__i18n_base',
41
+ },
42
+ }
43
+ }
@@ -0,0 +1,5 @@
1
+ import type {IExtendedLanguageObject} from '../types'
2
+
3
+ export const baseToTop = (a: IExtendedLanguageObject, b: IExtendedLanguageObject): number => {
4
+ return Number(a.isBase ?? 0) - Number(b.isBase ?? 0)
5
+ }
@@ -0,0 +1,8 @@
1
+ import {Ti18nConfig} from '../types'
2
+ import {I18nDelimiter, I18nPrefix, IdStructure} from '../constants'
3
+
4
+ export const buildDocId = (pluginConfig: Ti18nConfig, id: string, lang?: string | null): string => {
5
+ if (pluginConfig.idStructure === IdStructure.DELIMITER)
6
+ return `${id}${I18nDelimiter}${lang || '*'}`
7
+ return `${I18nPrefix}.${id}.${lang || '*'}`
8
+ }
@@ -0,0 +1,11 @@
1
+ import type {Reference} from 'sanity'
2
+
3
+ type Ref = Omit<Reference, '_type'> & {_type: 'reference'}
4
+
5
+ export function createSanityReference(id: string, weak = false): Ref {
6
+ return {
7
+ _type: 'reference' as const,
8
+ _ref: id.replace(`drafts.`, ``),
9
+ ...(weak === true ? {_weak: true} : {}),
10
+ }
11
+ }
@@ -0,0 +1,20 @@
1
+ import {I18nPrefix, I18nDelimiter} from '../constants'
2
+
3
+ /* Generic to support both string -> string & string? -> string? */
4
+ export const getBaseIdFromId = <T extends string | undefined>(id?: T): T => {
5
+ if (!id) {
6
+ return undefined as T
7
+ }
8
+ const nonDraftId = id.replace(/^drafts\./, '')
9
+
10
+ // subpath
11
+ const rx = new RegExp(`${I18nPrefix}\\.([^.]+)\\.[^.]+`)
12
+ const match = nonDraftId.match(rx)
13
+ if (match && match.length === 2) return match[1] as T
14
+
15
+ // delimiter
16
+ const split = nonDraftId.split(I18nDelimiter)
17
+ if (split.length > 0) return split[0] as T
18
+
19
+ return nonDraftId as T
20
+ }