Skip to content

Commit c383f39

Browse files
authored
feat(richtext-lexical): i18n support (#6524)
1 parent 8a91a7a commit c383f39

File tree

17 files changed

+321
-52
lines changed

17 files changed

+321
-52
lines changed

packages/next/src/views/Account/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const Account: React.FC<AdminViewProps> = async ({
4040

4141
const collectionConfig = config.collections.find((collection) => collection.slug === userSlug)
4242

43-
if (collectionConfig) {
43+
if (collectionConfig && user?.id) {
4444
const { docPermissions, hasPublishPermission, hasSavePermission } =
4545
await getDocumentPermissions({
4646
id: user.id,

packages/payload/src/admin/RichText.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { I18nClient } from '@payloadcms/translations'
1+
import type { GenericLanguages, I18n, I18nClient } from '@payloadcms/translations'
22
import type { JSONSchema4 } from 'json-schema'
33
import type React from 'react'
44

@@ -28,11 +28,12 @@ type RichTextAdapterBase<
2828
}) => Map<string, React.ReactNode>
2929
generateSchemaMap?: (args: {
3030
config: SanitizedConfig
31-
i18n: I18nClient
31+
i18n: I18n<any, any>
3232
schemaMap: Map<string, Field[]>
3333
schemaPath: string
3434
}) => Map<string, Field[]>
3535
hooks?: FieldBase['hooks']
36+
i18n?: Partial<GenericLanguages>
3637
outputSchema?: ({
3738
collectionIDFieldTypes,
3839
config,

packages/payload/src/config/sanitize.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { InvalidConfiguration } from '../errors/index.js'
1717
import { sanitizeGlobals } from '../globals/config/sanitize.js'
1818
import getPreferencesCollection from '../preferences/preferencesCollection.js'
1919
import checkDuplicateCollections from '../utilities/checkDuplicateCollections.js'
20+
import { deepMerge } from '../utilities/deepMerge.js'
2021
import { isPlainObject } from '../utilities/isPlainObject.js'
2122
import { defaults } from './defaults.js'
2223

@@ -154,6 +155,9 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
154155
config: config as SanitizedConfig,
155156
isRoot: true,
156157
})
158+
if (config.editor.i18n && Object.keys(config.editor.i18n).length >= 0) {
159+
config.i18n.translations = deepMerge(config.i18n.translations, config.editor.i18n)
160+
}
157161
}
158162

159163
const promises: Promise<void>[] = []

packages/payload/src/fields/config/sanitize.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
InvalidFieldRelationship,
99
MissingFieldType,
1010
} from '../../errors/index.js'
11+
import { deepMerge } from '../../utilities/deepMerge.js'
1112
import { formatLabels, toWords } from '../../utilities/formatLabels.js'
1213
import { baseBlockFields } from '../baseFields/baseBlockFields.js'
1314
import { baseIDField } from '../baseFields/baseIDField.js'
@@ -168,6 +169,10 @@ export const sanitizeFields = async ({
168169
})
169170
}
170171

172+
if (field.editor.i18n && Object.keys(field.editor.i18n).length >= 0) {
173+
config.i18n.translations = deepMerge(config.i18n.translations, field.editor.i18n)
174+
}
175+
171176
// Add editor adapter hooks to field hooks
172177
if (!field.hooks) field.hooks = {}
173178

packages/richtext-lexical/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"build:types": "tsc --emitDeclarationOnly --outDir dist",
3838
"clean": "rimraf {dist,*.tsbuildinfo}",
3939
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
40-
"prepublishOnly": "pnpm clean && pnpm turbo build"
40+
"prepublishOnly": "pnpm clean && pnpm turbo build",
41+
"translateNewKeys": "tsx scripts/translateNewKeys.ts"
4142
},
4243
"dependencies": {
4344
"@faceless-ui/modal": "2.0.2",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type {
2+
AcceptedLanguages,
3+
GenericLanguages,
4+
GenericTranslationsObject,
5+
} from '@payloadcms/translations'
6+
7+
import * as fs from 'node:fs'
8+
import path from 'path'
9+
import { fileURLToPath } from 'url'
10+
11+
import { translateObject } from '../../translations/scripts/translateNewKeys/index.js'
12+
const filename = fileURLToPath(import.meta.url)
13+
const dirname = path.dirname(filename)
14+
15+
// Function to get all files with a specific name recursively in all subdirectories
16+
function findFilesRecursively(startPath, filter) {
17+
let results = []
18+
19+
const entries = fs.readdirSync(startPath, { withFileTypes: true })
20+
21+
entries.forEach((dirent) => {
22+
const fullPath = path.join(startPath, dirent.name)
23+
24+
if (dirent.isDirectory()) {
25+
results = results.concat(findFilesRecursively(fullPath, filter))
26+
} else {
27+
if (dirent.name === filter) {
28+
results.push(fullPath)
29+
}
30+
}
31+
})
32+
33+
return results
34+
}
35+
36+
const i18nFilePaths = findFilesRecursively(path.resolve(dirname, '../src'), 'i18n.ts')
37+
38+
async function translate() {
39+
for (const i18nFilePath of i18nFilePaths) {
40+
const imported = await import(i18nFilePath)
41+
const translationsObject = imported.i18n as Partial<GenericLanguages>
42+
const allTranslations: {
43+
[key in AcceptedLanguages]?: {
44+
dateFNSKey: string
45+
translations: GenericTranslationsObject
46+
}
47+
} = {}
48+
for (const lang in translationsObject) {
49+
allTranslations[lang] = {
50+
dateFNSKey: 'en',
51+
translations: translationsObject[lang],
52+
}
53+
}
54+
55+
console.log('Translating', i18nFilePath)
56+
await translateObject({
57+
allTranslationsObject: allTranslations,
58+
fromTranslationsObject: translationsObject.en,
59+
inlineFile: i18nFilePath,
60+
tsFilePrefix: `import { GenericLanguages } from '@payloadcms/translations'
61+
62+
export const i18n: Partial<GenericLanguages> = `,
63+
tsFileSuffix: ``,
64+
})
65+
}
66+
}
67+
68+
void translate()

packages/richtext-lexical/src/field/features/horizontalRule/feature.client.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use client'
22

3+
import type { ClientTranslationKeys } from '@payloadcms/translations'
4+
35
import { $isNodeSelection } from 'lexical'
46

57
import type { FeatureProviderProviderClient } from '../types.js'
@@ -36,7 +38,10 @@ const HorizontalRuleFeatureClient: FeatureProviderProviderClient<undefined> = (p
3638
Icon: HorizontalRuleIcon,
3739
key: 'horizontalRule',
3840
keywords: ['hr', 'horizontal rule', 'line', 'separator'],
39-
label: `Horizontal Rule`,
41+
label: ({ i18n }) => {
42+
return i18n.t('lexical:horizontalRule:label')
43+
},
44+
4045
onSelect: ({ editor }) => {
4146
editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined)
4247
},
@@ -47,6 +52,7 @@ const HorizontalRuleFeatureClient: FeatureProviderProviderClient<undefined> = (p
4752
},
4853
],
4954
},
55+
5056
toolbarFixed: {
5157
groups: [
5258
toolbarAddDropdownGroupWithItems([
@@ -61,7 +67,9 @@ const HorizontalRuleFeatureClient: FeatureProviderProviderClient<undefined> = (p
6167
return $isHorizontalRuleNode(firstNode)
6268
},
6369
key: 'horizontalRule',
64-
label: `Horizontal Rule`,
70+
label: ({ i18n }) => {
71+
return i18n.t('lexical:horizontalRule:label')
72+
},
6573
onSelect: ({ editor }) => {
6674
editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined)
6775
},

packages/richtext-lexical/src/field/features/horizontalRule/feature.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { FeatureProviderProviderServer } from '../types.js'
22

33
import { createNode } from '../typeUtilities.js'
44
import { HorizontalRuleFeatureClientComponent } from './feature.client.js'
5+
import { i18n } from './i18n.js'
56
import { MarkdownTransformer } from './markdownTransformer.js'
67
import { HorizontalRuleNode } from './nodes/HorizontalRuleNode.js'
78

@@ -12,6 +13,7 @@ export const HorizontalRuleFeature: FeatureProviderProviderServer<undefined, und
1213
feature: () => {
1314
return {
1415
ClientComponent: HorizontalRuleFeatureClientComponent,
16+
i18n,
1517
markdownTransformers: [MarkdownTransformer],
1618
nodes: [
1719
createNode({
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { GenericLanguages } from '@payloadcms/translations'
2+
3+
export const i18n: Partial<GenericLanguages> = {
4+
ar: {
5+
label: 'القاعدة الأفقية',
6+
},
7+
az: {
8+
label: 'Üfüqi Xətt',
9+
},
10+
bg: {
11+
label: 'Хоризонтална линия',
12+
},
13+
cs: {
14+
label: 'Vodorovný pravítko',
15+
},
16+
de: {
17+
label: 'Trennlinie',
18+
},
19+
en: {
20+
label: 'Horizontal Rule',
21+
},
22+
es: {
23+
label: 'Regla Horizontal',
24+
},
25+
fa: {
26+
label: 'قاعده افقی',
27+
},
28+
fr: {
29+
label: 'Règle horizontale',
30+
},
31+
he: {
32+
label: 'קו אופקי',
33+
},
34+
hr: {
35+
label: 'Vodoravna linija',
36+
},
37+
hu: {
38+
label: 'Vízszintes vonal',
39+
},
40+
it: {
41+
label: 'Regola Orizzontale',
42+
},
43+
ja: {
44+
label: '水平線',
45+
},
46+
ko: {
47+
label: '수평 규칙',
48+
},
49+
my: {
50+
label: 'Peraturan Mendatar',
51+
},
52+
nb: {
53+
label: 'Horisontal Regel',
54+
},
55+
nl: {
56+
label: 'Horizontale Regel',
57+
},
58+
pl: {
59+
label: 'Pozioma Linia',
60+
},
61+
pt: {
62+
label: 'Regra Horizontal',
63+
},
64+
ro: {
65+
label: 'Linie orizontală',
66+
},
67+
rs: {
68+
label: 'Horizontalna linija',
69+
},
70+
'rs-latin': {
71+
label: 'Horizontalna linija',
72+
},
73+
ru: {
74+
label: 'Горизонтальная линия',
75+
},
76+
sk: {
77+
label: 'Vodorovná čiara',
78+
},
79+
sv: {
80+
label: 'Horisontell linje',
81+
},
82+
th: {
83+
label: 'กฎขีดตรง',
84+
},
85+
tr: {
86+
label: 'Yatay Çizgi',
87+
},
88+
uk: {
89+
label: 'Горизонтальна лінія',
90+
},
91+
vi: {
92+
label: 'Quy tắc ngang',
93+
},
94+
zh: {
95+
label: '水平线',
96+
},
97+
'zh-TW': {
98+
label: '水平線',
99+
},
100+
}

packages/richtext-lexical/src/field/features/toolbars/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export type ToolbarGroupItem = {
4949
}) => boolean
5050
key: string
5151
/** The label is displayed as text if the item is part of a dropdown group */
52-
label?: (({ i18n }: { i18n: I18nClient }) => string) | string
52+
label?: (({ i18n }: { i18n: I18nClient<{}, string> }) => string) | string
5353
onSelect?: ({ editor, isActive }: { editor: LexicalEditor; isActive: boolean }) => void
5454
order?: number
5555
}

0 commit comments

Comments
 (0)