feat(richtext-lexical): i18n support (#6524)
This commit is contained in:
@@ -40,7 +40,7 @@ export const Account: React.FC<AdminViewProps> = async ({
|
||||
|
||||
const collectionConfig = config.collections.find((collection) => collection.slug === userSlug)
|
||||
|
||||
if (collectionConfig) {
|
||||
if (collectionConfig && user?.id) {
|
||||
const { docPermissions, hasPublishPermission, hasSavePermission } =
|
||||
await getDocumentPermissions({
|
||||
id: user.id,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { I18nClient } from '@payloadcms/translations'
|
||||
import type { GenericLanguages, I18n, I18nClient } from '@payloadcms/translations'
|
||||
import type { JSONSchema4 } from 'json-schema'
|
||||
import type React from 'react'
|
||||
|
||||
@@ -28,11 +28,12 @@ type RichTextAdapterBase<
|
||||
}) => Map<string, React.ReactNode>
|
||||
generateSchemaMap?: (args: {
|
||||
config: SanitizedConfig
|
||||
i18n: I18nClient
|
||||
i18n: I18n<any, any>
|
||||
schemaMap: Map<string, Field[]>
|
||||
schemaPath: string
|
||||
}) => Map<string, Field[]>
|
||||
hooks?: FieldBase['hooks']
|
||||
i18n?: Partial<GenericLanguages>
|
||||
outputSchema?: ({
|
||||
collectionIDFieldTypes,
|
||||
config,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { InvalidConfiguration } from '../errors/index.js'
|
||||
import { sanitizeGlobals } from '../globals/config/sanitize.js'
|
||||
import getPreferencesCollection from '../preferences/preferencesCollection.js'
|
||||
import checkDuplicateCollections from '../utilities/checkDuplicateCollections.js'
|
||||
import { deepMerge } from '../utilities/deepMerge.js'
|
||||
import { isPlainObject } from '../utilities/isPlainObject.js'
|
||||
import { defaults } from './defaults.js'
|
||||
|
||||
@@ -154,6 +155,9 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
config: config as SanitizedConfig,
|
||||
isRoot: true,
|
||||
})
|
||||
if (config.editor.i18n && Object.keys(config.editor.i18n).length >= 0) {
|
||||
config.i18n.translations = deepMerge(config.i18n.translations, config.editor.i18n)
|
||||
}
|
||||
}
|
||||
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
InvalidFieldRelationship,
|
||||
MissingFieldType,
|
||||
} from '../../errors/index.js'
|
||||
import { deepMerge } from '../../utilities/deepMerge.js'
|
||||
import { formatLabels, toWords } from '../../utilities/formatLabels.js'
|
||||
import { baseBlockFields } from '../baseFields/baseBlockFields.js'
|
||||
import { baseIDField } from '../baseFields/baseIDField.js'
|
||||
@@ -168,6 +169,10 @@ export const sanitizeFields = async ({
|
||||
})
|
||||
}
|
||||
|
||||
if (field.editor.i18n && Object.keys(field.editor.i18n).length >= 0) {
|
||||
config.i18n.translations = deepMerge(config.i18n.translations, field.editor.i18n)
|
||||
}
|
||||
|
||||
// Add editor adapter hooks to field hooks
|
||||
if (!field.hooks) field.hooks = {}
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build"
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build",
|
||||
"translateNewKeys": "tsx scripts/translateNewKeys.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faceless-ui/modal": "2.0.2",
|
||||
|
||||
68
packages/richtext-lexical/scripts/translateNewKeys.ts
Normal file
68
packages/richtext-lexical/scripts/translateNewKeys.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type {
|
||||
AcceptedLanguages,
|
||||
GenericLanguages,
|
||||
GenericTranslationsObject,
|
||||
} from '@payloadcms/translations'
|
||||
|
||||
import * as fs from 'node:fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { translateObject } from '../../translations/scripts/translateNewKeys/index.js'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
// Function to get all files with a specific name recursively in all subdirectories
|
||||
function findFilesRecursively(startPath, filter) {
|
||||
let results = []
|
||||
|
||||
const entries = fs.readdirSync(startPath, { withFileTypes: true })
|
||||
|
||||
entries.forEach((dirent) => {
|
||||
const fullPath = path.join(startPath, dirent.name)
|
||||
|
||||
if (dirent.isDirectory()) {
|
||||
results = results.concat(findFilesRecursively(fullPath, filter))
|
||||
} else {
|
||||
if (dirent.name === filter) {
|
||||
results.push(fullPath)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
const i18nFilePaths = findFilesRecursively(path.resolve(dirname, '../src'), 'i18n.ts')
|
||||
|
||||
async function translate() {
|
||||
for (const i18nFilePath of i18nFilePaths) {
|
||||
const imported = await import(i18nFilePath)
|
||||
const translationsObject = imported.i18n as Partial<GenericLanguages>
|
||||
const allTranslations: {
|
||||
[key in AcceptedLanguages]?: {
|
||||
dateFNSKey: string
|
||||
translations: GenericTranslationsObject
|
||||
}
|
||||
} = {}
|
||||
for (const lang in translationsObject) {
|
||||
allTranslations[lang] = {
|
||||
dateFNSKey: 'en',
|
||||
translations: translationsObject[lang],
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Translating', i18nFilePath)
|
||||
await translateObject({
|
||||
allTranslationsObject: allTranslations,
|
||||
fromTranslationsObject: translationsObject.en,
|
||||
inlineFile: i18nFilePath,
|
||||
tsFilePrefix: `import { GenericLanguages } from '@payloadcms/translations'
|
||||
|
||||
export const i18n: Partial<GenericLanguages> = `,
|
||||
tsFileSuffix: ``,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
void translate()
|
||||
@@ -1,5 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { ClientTranslationKeys } from '@payloadcms/translations'
|
||||
|
||||
import { $isNodeSelection } from 'lexical'
|
||||
|
||||
import type { FeatureProviderProviderClient } from '../types.js'
|
||||
@@ -36,7 +38,10 @@ const HorizontalRuleFeatureClient: FeatureProviderProviderClient<undefined> = (p
|
||||
Icon: HorizontalRuleIcon,
|
||||
key: 'horizontalRule',
|
||||
keywords: ['hr', 'horizontal rule', 'line', 'separator'],
|
||||
label: `Horizontal Rule`,
|
||||
label: ({ i18n }) => {
|
||||
return i18n.t('lexical:horizontalRule:label')
|
||||
},
|
||||
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined)
|
||||
},
|
||||
@@ -47,6 +52,7 @@ const HorizontalRuleFeatureClient: FeatureProviderProviderClient<undefined> = (p
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
toolbarFixed: {
|
||||
groups: [
|
||||
toolbarAddDropdownGroupWithItems([
|
||||
@@ -61,7 +67,9 @@ const HorizontalRuleFeatureClient: FeatureProviderProviderClient<undefined> = (p
|
||||
return $isHorizontalRuleNode(firstNode)
|
||||
},
|
||||
key: 'horizontalRule',
|
||||
label: `Horizontal Rule`,
|
||||
label: ({ i18n }) => {
|
||||
return i18n.t('lexical:horizontalRule:label')
|
||||
},
|
||||
onSelect: ({ editor }) => {
|
||||
editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined)
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { FeatureProviderProviderServer } from '../types.js'
|
||||
|
||||
import { createNode } from '../typeUtilities.js'
|
||||
import { HorizontalRuleFeatureClientComponent } from './feature.client.js'
|
||||
import { i18n } from './i18n.js'
|
||||
import { MarkdownTransformer } from './markdownTransformer.js'
|
||||
import { HorizontalRuleNode } from './nodes/HorizontalRuleNode.js'
|
||||
|
||||
@@ -12,6 +13,7 @@ export const HorizontalRuleFeature: FeatureProviderProviderServer<undefined, und
|
||||
feature: () => {
|
||||
return {
|
||||
ClientComponent: HorizontalRuleFeatureClientComponent,
|
||||
i18n,
|
||||
markdownTransformers: [MarkdownTransformer],
|
||||
nodes: [
|
||||
createNode({
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { GenericLanguages } from '@payloadcms/translations'
|
||||
|
||||
export const i18n: Partial<GenericLanguages> = {
|
||||
ar: {
|
||||
label: 'القاعدة الأفقية',
|
||||
},
|
||||
az: {
|
||||
label: 'Üfüqi Xətt',
|
||||
},
|
||||
bg: {
|
||||
label: 'Хоризонтална линия',
|
||||
},
|
||||
cs: {
|
||||
label: 'Vodorovný pravítko',
|
||||
},
|
||||
de: {
|
||||
label: 'Trennlinie',
|
||||
},
|
||||
en: {
|
||||
label: 'Horizontal Rule',
|
||||
},
|
||||
es: {
|
||||
label: 'Regla Horizontal',
|
||||
},
|
||||
fa: {
|
||||
label: 'قاعده افقی',
|
||||
},
|
||||
fr: {
|
||||
label: 'Règle horizontale',
|
||||
},
|
||||
he: {
|
||||
label: 'קו אופקי',
|
||||
},
|
||||
hr: {
|
||||
label: 'Vodoravna linija',
|
||||
},
|
||||
hu: {
|
||||
label: 'Vízszintes vonal',
|
||||
},
|
||||
it: {
|
||||
label: 'Regola Orizzontale',
|
||||
},
|
||||
ja: {
|
||||
label: '水平線',
|
||||
},
|
||||
ko: {
|
||||
label: '수평 규칙',
|
||||
},
|
||||
my: {
|
||||
label: 'Peraturan Mendatar',
|
||||
},
|
||||
nb: {
|
||||
label: 'Horisontal Regel',
|
||||
},
|
||||
nl: {
|
||||
label: 'Horizontale Regel',
|
||||
},
|
||||
pl: {
|
||||
label: 'Pozioma Linia',
|
||||
},
|
||||
pt: {
|
||||
label: 'Regra Horizontal',
|
||||
},
|
||||
ro: {
|
||||
label: 'Linie orizontală',
|
||||
},
|
||||
rs: {
|
||||
label: 'Horizontalna linija',
|
||||
},
|
||||
'rs-latin': {
|
||||
label: 'Horizontalna linija',
|
||||
},
|
||||
ru: {
|
||||
label: 'Горизонтальная линия',
|
||||
},
|
||||
sk: {
|
||||
label: 'Vodorovná čiara',
|
||||
},
|
||||
sv: {
|
||||
label: 'Horisontell linje',
|
||||
},
|
||||
th: {
|
||||
label: 'กฎขีดตรง',
|
||||
},
|
||||
tr: {
|
||||
label: 'Yatay Çizgi',
|
||||
},
|
||||
uk: {
|
||||
label: 'Горизонтальна лінія',
|
||||
},
|
||||
vi: {
|
||||
label: 'Quy tắc ngang',
|
||||
},
|
||||
zh: {
|
||||
label: '水平线',
|
||||
},
|
||||
'zh-TW': {
|
||||
label: '水平線',
|
||||
},
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export type ToolbarGroupItem = {
|
||||
}) => boolean
|
||||
key: string
|
||||
/** The label is displayed as text if the item is part of a dropdown group */
|
||||
label?: (({ i18n }: { i18n: I18nClient }) => string) | string
|
||||
label?: (({ i18n }: { i18n: I18nClient<{}, string> }) => string) | string
|
||||
onSelect?: ({ editor, isActive }: { editor: LexicalEditor; isActive: boolean }) => void
|
||||
order?: number
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Transformer } from '@lexical/markdown'
|
||||
import type { I18n, I18nClient } from '@payloadcms/translations'
|
||||
import type { GenericLanguages, I18n, I18nClient } from '@payloadcms/translations'
|
||||
import type { JSONSchema4 } from 'json-schema'
|
||||
import type { Klass, LexicalEditor, LexicalNode, SerializedEditorState } from 'lexical'
|
||||
import type { SerializedLexicalNode } from 'lexical'
|
||||
@@ -319,6 +319,25 @@ export type ServerFeature<ServerProps, ClientFeatureProps> = {
|
||||
isRequired: boolean
|
||||
}) => JSONSchema4
|
||||
}
|
||||
/**
|
||||
* Here you can provide i18n translations for your feature. These will only be available on the server and client.
|
||||
*
|
||||
* Translations here are automatically scoped to `lexical.featureKey.yourKey`
|
||||
*
|
||||
* @Example
|
||||
* ```ts
|
||||
* i18n: {
|
||||
* en: {
|
||||
* label: 'Horizontal Rule',
|
||||
* },
|
||||
* de: {
|
||||
* label: 'Trennlinie',
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
* In order to access these translations, you would use `i18n.t('lexical:horizontalRule:label')`.
|
||||
*/
|
||||
i18n?: Partial<GenericLanguages>
|
||||
markdownTransformers?: Transformer[]
|
||||
nodes?: Array<NodeWithHooks>
|
||||
|
||||
@@ -395,8 +414,9 @@ export type SanitizedPlugin =
|
||||
key: string
|
||||
position: 'belowContainer'
|
||||
}
|
||||
|
||||
export type SanitizedServerFeatures = Required<
|
||||
Pick<ResolvedServerFeature<unknown, unknown>, 'markdownTransformers' | 'nodes'>
|
||||
Pick<ResolvedServerFeature<unknown, unknown>, 'i18n' | 'markdownTransformers' | 'nodes'>
|
||||
> & {
|
||||
/** The node types mapped to their converters */
|
||||
converters: {
|
||||
|
||||
@@ -13,10 +13,10 @@ export const sanitizeServerFeatures = (
|
||||
html: [],
|
||||
},
|
||||
enabledFeatures: [],
|
||||
|
||||
generatedTypes: {
|
||||
modifyOutputSchemas: [],
|
||||
},
|
||||
|
||||
hooks: {
|
||||
afterChange: new Map(),
|
||||
afterRead: new Map(),
|
||||
@@ -24,6 +24,7 @@ export const sanitizeServerFeatures = (
|
||||
beforeDuplicate: new Map(),
|
||||
beforeValidate: new Map(),
|
||||
},
|
||||
i18n: {},
|
||||
markdownTransformers: [],
|
||||
nodes: [],
|
||||
populationPromises: new Map(),
|
||||
@@ -40,6 +41,17 @@ export const sanitizeServerFeatures = (
|
||||
sanitized.generatedTypes.modifyOutputSchemas.push(feature.generatedTypes.modifyOutputSchema)
|
||||
}
|
||||
|
||||
if (feature?.i18n) {
|
||||
for (const lang in feature.i18n) {
|
||||
if (!sanitized.i18n[lang]) {
|
||||
sanitized.i18n[lang] = {
|
||||
lexical: {},
|
||||
}
|
||||
}
|
||||
sanitized.i18n[lang].lexical[feature.key] = feature.i18n[lang]
|
||||
}
|
||||
}
|
||||
|
||||
if (feature.nodes?.length) {
|
||||
sanitized.nodes = sanitized.nodes.concat(feature.nodes)
|
||||
feature.nodes.forEach((node) => {
|
||||
|
||||
@@ -13,7 +13,7 @@ export type SlashMenuItem = {
|
||||
keyboardShortcut?: string
|
||||
// For extra searching.
|
||||
keywords?: Array<string>
|
||||
label?: (({ i18n }: { i18n: I18nClient }) => string) | string
|
||||
label?: (({ i18n }: { i18n: I18nClient<{}, string> }) => string) | string
|
||||
// What happens when you select this item?
|
||||
onSelect: ({ editor, queryString }: { editor: LexicalEditor; queryString: string }) => void
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export type SlashMenuGroup = {
|
||||
items: Array<SlashMenuItem>
|
||||
key: string
|
||||
// Used for class names and, if label is not provided, for display.
|
||||
label?: (({ i18n }: { i18n: I18nClient }) => string) | string
|
||||
label?: (({ i18n }: { i18n: I18nClient<{}, string> }) => string) | string
|
||||
}
|
||||
|
||||
export type SlashMenuItemInternal = SlashMenuItem & {
|
||||
|
||||
@@ -109,6 +109,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
|
||||
generateSchemaMap: getGenerateSchemaMap({
|
||||
resolvedFeatureMap,
|
||||
}),
|
||||
i18n: finalSanitizedEditorConfig.features.i18n,
|
||||
/* hooks: {
|
||||
afterChange: finalSanitizedEditorConfig.features.hooks.afterChange,
|
||||
afterRead: finalSanitizedEditorConfig.features.hooks.afterRead,
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"build:types": "tsc --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build",
|
||||
"translateNewKeys": "tsx scripts/translateNewKeys/index.ts"
|
||||
"translateNewKeys": "tsx scripts/translateNewKeys/run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { format } from 'prettier'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type {
|
||||
AcceptedLanguages,
|
||||
@@ -11,8 +10,6 @@ import type {
|
||||
GenericTranslationsObject,
|
||||
} from '../../src/types.js'
|
||||
|
||||
import { translations } from '../../src/exports/all.js'
|
||||
import { enTranslations } from '../../src/languages/en.js'
|
||||
import { cloneDeep } from '../../src/utilities/cloneDeep.js'
|
||||
import { deepMerge } from '../../src/utilities/deepMerge.js'
|
||||
import { acceptedLanguages } from '../../src/utilities/languages.js'
|
||||
@@ -22,9 +19,6 @@ import { generateTsObjectLiteral } from './generateTsObjectLiteral.js'
|
||||
import { sortKeys } from './sortKeys.js'
|
||||
import { translateText } from './translateText.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
/**
|
||||
*
|
||||
* props.allTranslationsObject:
|
||||
@@ -59,6 +53,13 @@ export async function translateObject(props: {
|
||||
}
|
||||
}
|
||||
fromTranslationsObject: GenericTranslationsObject
|
||||
/**
|
||||
*
|
||||
* If set, will output the entire translations object (incl. all locales) to this file.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
inlineFile?: string
|
||||
languages?: AcceptedLanguages[]
|
||||
targetFolder?: string
|
||||
tsFilePrefix?: string
|
||||
@@ -67,6 +68,7 @@ export async function translateObject(props: {
|
||||
const {
|
||||
allTranslationsObject,
|
||||
fromTranslationsObject,
|
||||
inlineFile,
|
||||
languages = acceptedLanguages.filter((lang) => lang !== 'en'),
|
||||
targetFolder = '',
|
||||
tsFilePrefix = `import type { DefaultTranslationsObject, Language } from '../types.js'\n\nexport const {{locale}}Translations: DefaultTranslationsObject = `,
|
||||
@@ -87,6 +89,12 @@ export async function translateObject(props: {
|
||||
const translationPromises: Promise<void>[] = []
|
||||
|
||||
for (const targetLang of languages) {
|
||||
if (!allTranslatedTranslationsObject?.[targetLang]) {
|
||||
allTranslatedTranslationsObject[targetLang] = {
|
||||
dateFNSKey: targetLang,
|
||||
translations: {},
|
||||
}
|
||||
}
|
||||
const keysWhichDoNotExistInFromlang = findMissingKeys(
|
||||
allTranslatedTranslationsObject?.[targetLang].translations,
|
||||
fromTranslationsObject,
|
||||
@@ -172,22 +180,18 @@ export async function translateObject(props: {
|
||||
|
||||
console.log('New translations:', allOnlyNewTranslatedTranslationsObject)
|
||||
|
||||
// save
|
||||
if (inlineFile?.length) {
|
||||
const simpleTranslationsObject = {}
|
||||
for (const lang in allTranslatedTranslationsObject) {
|
||||
simpleTranslationsObject[lang] = allTranslatedTranslationsObject[lang].translations
|
||||
}
|
||||
|
||||
for (const key of languages) {
|
||||
// e.g. sanitize rs-latin to rsLatin
|
||||
const sanitizedKey = key.replace(
|
||||
/-(\w)(\w*)/g,
|
||||
(_, firstLetter, remainingLetters) =>
|
||||
firstLetter.toUpperCase() + remainingLetters.toLowerCase(),
|
||||
)
|
||||
const filePath = path.resolve(dirname, targetFolder, `${sanitizedKey}.ts`)
|
||||
|
||||
// prefix & translations
|
||||
let fileContent: string = `${tsFilePrefix.replace('{{locale}}', sanitizedKey)}${generateTsObjectLiteral(allTranslatedTranslationsObject[key].translations)}\n`
|
||||
// write allTranslatedTranslationsObject
|
||||
const filePath = path.resolve(inlineFile)
|
||||
let fileContent: string = `${tsFilePrefix}${generateTsObjectLiteral(simpleTranslationsObject)}\n`
|
||||
|
||||
// suffix
|
||||
fileContent += `${tsFileSuffix.replaceAll('{{locale}}', sanitizedKey).replaceAll('{{dateFNSKey}}', `'${allTranslatedTranslationsObject[key].dateFNSKey}'`)}\n`
|
||||
fileContent += `${tsFileSuffix}\n`
|
||||
|
||||
// eslint
|
||||
fileContent = await applyEslintFixes(fileContent, filePath)
|
||||
@@ -202,28 +206,39 @@ export async function translateObject(props: {
|
||||
})
|
||||
|
||||
fs.writeFileSync(filePath, fileContent, 'utf8')
|
||||
} else {
|
||||
// save
|
||||
|
||||
for (const key of languages) {
|
||||
// e.g. sanitize rs-latin to rsLatin
|
||||
const sanitizedKey = key.replace(
|
||||
/-(\w)(\w*)/g,
|
||||
(_, firstLetter, remainingLetters) =>
|
||||
firstLetter.toUpperCase() + remainingLetters.toLowerCase(),
|
||||
)
|
||||
const filePath = path.resolve(targetFolder, `${sanitizedKey}.ts`)
|
||||
|
||||
// prefix & translations
|
||||
let fileContent: string = `${tsFilePrefix.replace('{{locale}}', sanitizedKey)}${generateTsObjectLiteral(allTranslatedTranslationsObject[key].translations)}\n`
|
||||
|
||||
// suffix
|
||||
fileContent += `${tsFileSuffix.replaceAll('{{locale}}', sanitizedKey).replaceAll('{{dateFNSKey}}', `'${allTranslatedTranslationsObject[key].dateFNSKey}'`)}\n`
|
||||
|
||||
// eslint
|
||||
fileContent = await applyEslintFixes(fileContent, filePath)
|
||||
|
||||
// prettier
|
||||
fileContent = await format(fileContent, {
|
||||
parser: 'typescript',
|
||||
printWidth: 100,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
})
|
||||
|
||||
fs.writeFileSync(filePath, fileContent, 'utf8')
|
||||
}
|
||||
}
|
||||
|
||||
return allTranslatedTranslationsObject
|
||||
}
|
||||
|
||||
const allTranslations: {
|
||||
[key in AcceptedLanguages]?: {
|
||||
dateFNSKey: string
|
||||
translations: GenericTranslationsObject
|
||||
}
|
||||
} = {}
|
||||
|
||||
for (const key of Object.keys(translations)) {
|
||||
allTranslations[key] = {
|
||||
dateFNSKey: translations[key].dateFNSKey,
|
||||
translations: translations[key].translations,
|
||||
}
|
||||
}
|
||||
|
||||
void translateObject({
|
||||
allTranslationsObject: allTranslations,
|
||||
fromTranslationsObject: enTranslations,
|
||||
//languages: ['de'],
|
||||
targetFolder: '../../src/languages',
|
||||
})
|
||||
|
||||
32
packages/translations/scripts/translateNewKeys/run.ts
Normal file
32
packages/translations/scripts/translateNewKeys/run.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { AcceptedLanguages, GenericTranslationsObject } from '../../src/types.js'
|
||||
|
||||
import { translations } from '../../src/exports/all.js'
|
||||
import { enTranslations } from '../../src/languages/en.js'
|
||||
import { translateObject } from './index.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const allTranslations: {
|
||||
[key in AcceptedLanguages]?: {
|
||||
dateFNSKey: string
|
||||
translations: GenericTranslationsObject
|
||||
}
|
||||
} = {}
|
||||
|
||||
for (const key of Object.keys(translations)) {
|
||||
allTranslations[key] = {
|
||||
dateFNSKey: translations[key].dateFNSKey,
|
||||
translations: translations[key].translations,
|
||||
}
|
||||
}
|
||||
|
||||
void translateObject({
|
||||
allTranslationsObject: allTranslations,
|
||||
fromTranslationsObject: enTranslations,
|
||||
//languages: ['de'],
|
||||
targetFolder: path.resolve(dirname, '../../src/languages'),
|
||||
})
|
||||
Reference in New Issue
Block a user