mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 10:25:48 +08:00
chore(web): new lint setup (#30020)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
@@ -79,7 +79,6 @@ export type I18nText = {
|
||||
4. Add the new language to the `language.json` file.
|
||||
|
||||
```typescript
|
||||
|
||||
export const languages = [
|
||||
{
|
||||
value: 'en-US',
|
||||
@@ -172,7 +171,7 @@ export const languages = [
|
||||
supported: true,
|
||||
},
|
||||
// Add your language here 👇
|
||||
...
|
||||
// ...
|
||||
// Add your language here 👆
|
||||
]
|
||||
```
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import vm from 'node:vm'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { createRequire } from 'node:module'
|
||||
import { transpile } from 'typescript'
|
||||
import { parseModule, generateCode, loadFile } from 'magicast'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import vm from 'node:vm'
|
||||
import { translate } from 'bing-translate-api'
|
||||
import { generateCode, loadFile, parseModule } from 'magicast'
|
||||
import { transpile } from 'typescript'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
@@ -42,7 +42,8 @@ function parseArgs(argv) {
|
||||
let cursor = startIndex + 1
|
||||
while (cursor < argv.length && !argv[cursor].startsWith('--')) {
|
||||
const value = argv[cursor].trim()
|
||||
if (value) values.push(value)
|
||||
if (value)
|
||||
values.push(value)
|
||||
cursor++
|
||||
}
|
||||
return { values, nextIndex: cursor - 1 }
|
||||
@@ -127,7 +128,7 @@ function protectPlaceholders(text) {
|
||||
const patterns = [
|
||||
/\{\{[^{}]+\}\}/g, // mustache
|
||||
/\$\{[^{}]+\}/g, // template expressions
|
||||
/<[^>]+?>/g, // html-like tags
|
||||
/<[^>]+>/g, // html-like tags
|
||||
]
|
||||
|
||||
patterns.forEach((pattern) => {
|
||||
@@ -160,7 +161,7 @@ async function translateText(source, toLanguage) {
|
||||
const { translation } = await translate(safeText, null, languageKeyMap[toLanguage])
|
||||
return { value: restore(translation), skipped: false }
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.error(`❌ Error translating to ${toLanguage}:`, error.message)
|
||||
return { value: source, skipped: true, error: error.message }
|
||||
}
|
||||
@@ -310,7 +311,7 @@ export default translation
|
||||
}
|
||||
|
||||
const { code } = generateCode(mod)
|
||||
let res = `const translation =${code.replace('export default', '')}
|
||||
const res = `const translation =${code.replace('export default', '')}
|
||||
|
||||
export default translation
|
||||
`.replace(/,\n\n/g, ',\n').replace('};', '}')
|
||||
@@ -319,13 +320,13 @@ export default translation
|
||||
fs.writeFileSync(toGenLanguageFilePath, res)
|
||||
console.log(`💾 Saved translations to ${toGenLanguageFilePath}`)
|
||||
}
|
||||
else {
|
||||
else {
|
||||
console.log(`🔍 [DRY RUN] Would save translations to ${toGenLanguageFilePath}`)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.error(`Error processing file ${fullKeyFilePath}:`, error.message)
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import lodash from 'lodash'
|
||||
const { camelCase } = lodash
|
||||
import ts from 'typescript'
|
||||
|
||||
const { camelCase } = lodash
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import vm from 'node:vm'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { createRequire } from 'node:module'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import vm from 'node:vm'
|
||||
import { transpile } from 'typescript'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
@@ -11,6 +11,7 @@ const __dirname = path.dirname(__filename)
|
||||
|
||||
const targetLanguage = 'en-US'
|
||||
const data = require('./languages.json')
|
||||
|
||||
const languages = data.languages.filter(language => language.supported).map(language => language.value)
|
||||
|
||||
function parseArgs(argv) {
|
||||
@@ -27,7 +28,8 @@ function parseArgs(argv) {
|
||||
let cursor = startIndex + 1
|
||||
while (cursor < argv.length && !argv[cursor].startsWith('--')) {
|
||||
const value = argv[cursor].trim()
|
||||
if (value) values.push(value)
|
||||
if (value)
|
||||
values.push(value)
|
||||
cursor++
|
||||
}
|
||||
return { values, nextIndex: cursor - 1 }
|
||||
@@ -124,8 +126,7 @@ async function getKeysFromLanguage(language) {
|
||||
const filePath = path.join(folderPath, file)
|
||||
const fileName = file.replace(/\.[^/.]+$/, '') // Remove file extension
|
||||
const camelCaseFileName = fileName.replace(/[-_](.)/g, (_, c) =>
|
||||
c.toUpperCase(),
|
||||
) // Convert to camel case
|
||||
c.toUpperCase()) // Convert to camel case
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8')
|
||||
@@ -147,7 +148,7 @@ async function getKeysFromLanguage(language) {
|
||||
// Extract the translation object
|
||||
const translationObj = moduleExports.default || moduleExports
|
||||
|
||||
if(!translationObj || typeof translationObj !== 'object') {
|
||||
if (!translationObj || typeof translationObj !== 'object') {
|
||||
console.error(`Error parsing file: ${filePath}`)
|
||||
reject(new Error(`Error parsing file: ${filePath}`))
|
||||
return
|
||||
@@ -161,7 +162,7 @@ async function getKeysFromLanguage(language) {
|
||||
// This is an object (but not array), recurse into it but don't add it as a key
|
||||
iterateKeys(obj[key], nestedKey)
|
||||
}
|
||||
else {
|
||||
else {
|
||||
// This is a leaf node (string, number, boolean, array, etc.), add it as a key
|
||||
nestedKeys.push(nestedKey)
|
||||
}
|
||||
@@ -173,7 +174,7 @@ async function getKeysFromLanguage(language) {
|
||||
const fileKeys = nestedKeys.map(key => `${camelCaseFileName}.${key}`)
|
||||
allKeys.push(...fileKeys)
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.error(`Error processing file ${filePath}:`, error.message)
|
||||
reject(error)
|
||||
}
|
||||
@@ -193,7 +194,7 @@ function removeKeysFromObject(obj, keysToRemove, prefix = '') {
|
||||
modified = true
|
||||
console.log(`🗑️ Removed key: ${fullKey}`)
|
||||
}
|
||||
else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||
else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||
const subModified = removeKeysFromObject(obj[key], keysToRemove, fullKey)
|
||||
modified = modified || subModified
|
||||
}
|
||||
@@ -246,7 +247,7 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else {
|
||||
// Nested key - need to find the exact path
|
||||
const currentPath = []
|
||||
let braceDepth = 0
|
||||
@@ -256,12 +257,12 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
|
||||
const trimmedLine = line.trim()
|
||||
|
||||
// Track current object path
|
||||
const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*{/)
|
||||
const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*\{/)
|
||||
if (keyMatch) {
|
||||
currentPath.push(keyMatch[1])
|
||||
braceDepth++
|
||||
}
|
||||
else if (trimmedLine === '},' || trimmedLine === '}') {
|
||||
else if (trimmedLine === '},' || trimmedLine === '}') {
|
||||
if (braceDepth > 0) {
|
||||
braceDepth--
|
||||
currentPath.pop()
|
||||
@@ -316,11 +317,12 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
|
||||
|
||||
// Check if this line ends the value (ends with quote and comma/no comma)
|
||||
if ((trimmed.endsWith('\',') || trimmed.endsWith('",') || trimmed.endsWith('`,')
|
||||
|| trimmed.endsWith('\'') || trimmed.endsWith('"') || trimmed.endsWith('`'))
|
||||
&& !trimmed.startsWith('//'))
|
||||
|| trimmed.endsWith('\'') || trimmed.endsWith('"') || trimmed.endsWith('`'))
|
||||
&& !trimmed.startsWith('//')) {
|
||||
break
|
||||
}
|
||||
}
|
||||
else {
|
||||
else {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -332,7 +334,7 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
|
||||
console.log(`🗑️ Found key to remove: ${keyToRemove} at line ${targetLineIndex + 1}${linesToRemoveForKey.length > 1 ? ` (multiline, ${linesToRemoveForKey.length} lines)` : ''}`)
|
||||
modified = true
|
||||
}
|
||||
else {
|
||||
else {
|
||||
console.log(`⚠️ Could not find key: ${keyToRemove}`)
|
||||
}
|
||||
}
|
||||
@@ -365,7 +367,7 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) {
|
||||
|
||||
return false
|
||||
}
|
||||
catch (error) {
|
||||
catch (error) {
|
||||
console.error(`Error processing file ${filePath}:`, error.message)
|
||||
return false
|
||||
}
|
||||
@@ -439,7 +441,8 @@ async function main() {
|
||||
let totalRemoved = 0
|
||||
for (const fileName of files) {
|
||||
const removed = await removeExtraKeysFromFile(language, fileName, extraKeys)
|
||||
if (removed) totalRemoved++
|
||||
if (removed)
|
||||
totalRemoved++
|
||||
}
|
||||
|
||||
console.log(`✅ Auto-removal completed for ${language}. Modified ${totalRemoved} files.`)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import lodash from 'lodash'
|
||||
import ts from 'typescript'
|
||||
|
||||
@@ -50,8 +50,8 @@ import 'react-i18next'
|
||||
// Extract types from translation files using typeof import pattern`
|
||||
|
||||
// Generate individual type definitions
|
||||
const typeDefinitions = namespaces.map(namespace => {
|
||||
const typeName = camelCase(namespace).replace(/^\w/, c => c.toUpperCase()) + 'Messages'
|
||||
const typeDefinitions = namespaces.map((namespace) => {
|
||||
const typeName = `${camelCase(namespace).replace(/^\w/, c => c.toUpperCase())}Messages`
|
||||
return `type ${typeName} = typeof import('../i18n/en-US/${namespace}').default`
|
||||
}).join('\n')
|
||||
|
||||
@@ -59,11 +59,11 @@ import 'react-i18next'
|
||||
const messagesInterface = `
|
||||
// Complete type structure that matches i18next-config.ts camelCase conversion
|
||||
export type Messages = {
|
||||
${namespaces.map(namespace => {
|
||||
const camelCased = camelCase(namespace)
|
||||
const typeName = camelCase(namespace).replace(/^\w/, c => c.toUpperCase()) + 'Messages'
|
||||
return ` ${camelCased}: ${typeName};`
|
||||
}).join('\n')}
|
||||
${namespaces.map((namespace) => {
|
||||
const camelCased = camelCase(namespace)
|
||||
const typeName = `${camelCase(namespace).replace(/^\w/, c => c.toUpperCase())}Messages`
|
||||
return ` ${camelCased}: ${typeName};`
|
||||
}).join('\n')}
|
||||
}`
|
||||
|
||||
const utilityTypes = `
|
||||
@@ -133,13 +133,14 @@ function main() {
|
||||
}
|
||||
|
||||
console.log('✅ Type definitions are in sync')
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Generate mode: write file
|
||||
fs.writeFileSync(outputPath, typeDefinitions)
|
||||
console.log(`✅ Generated type definitions: ${outputPath}`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
}
|
||||
catch (error) {
|
||||
console.error('❌ Error:', error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -3,31 +3,31 @@ import i18n from 'i18next'
|
||||
import { camelCase } from 'lodash-es'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
|
||||
import app from '../i18n/en-US/app'
|
||||
// Static imports for en-US (fallback language)
|
||||
import appAnnotation from '../i18n/en-US/app-annotation'
|
||||
import appApi from '../i18n/en-US/app-api'
|
||||
import appDebug from '../i18n/en-US/app-debug'
|
||||
import appLog from '../i18n/en-US/app-log'
|
||||
import appOverview from '../i18n/en-US/app-overview'
|
||||
import app from '../i18n/en-US/app'
|
||||
import billing from '../i18n/en-US/billing'
|
||||
import common from '../i18n/en-US/common'
|
||||
import custom from '../i18n/en-US/custom'
|
||||
import dataset from '../i18n/en-US/dataset'
|
||||
import datasetCreation from '../i18n/en-US/dataset-creation'
|
||||
import datasetDocuments from '../i18n/en-US/dataset-documents'
|
||||
import datasetHitTesting from '../i18n/en-US/dataset-hit-testing'
|
||||
import datasetPipeline from '../i18n/en-US/dataset-pipeline'
|
||||
import datasetSettings from '../i18n/en-US/dataset-settings'
|
||||
import dataset from '../i18n/en-US/dataset'
|
||||
import education from '../i18n/en-US/education'
|
||||
import explore from '../i18n/en-US/explore'
|
||||
import layout from '../i18n/en-US/layout'
|
||||
import login from '../i18n/en-US/login'
|
||||
import oauth from '../i18n/en-US/oauth'
|
||||
import pipeline from '../i18n/en-US/pipeline'
|
||||
import plugin from '../i18n/en-US/plugin'
|
||||
import pluginTags from '../i18n/en-US/plugin-tags'
|
||||
import pluginTrigger from '../i18n/en-US/plugin-trigger'
|
||||
import plugin from '../i18n/en-US/plugin'
|
||||
import register from '../i18n/en-US/register'
|
||||
import runLog from '../i18n/en-US/run-log'
|
||||
import share from '../i18n/en-US/share'
|
||||
@@ -141,7 +141,8 @@ if (!i18n.isInitialized) {
|
||||
}
|
||||
|
||||
export const changeLanguage = async (lng?: string) => {
|
||||
if (!lng) return
|
||||
if (!lng)
|
||||
return
|
||||
if (!i18n.hasResourceBundle(lng, 'translation')) {
|
||||
const resource = await loadLangResources(lng)
|
||||
i18n.addResourceBundle(lng, 'translation', resource, true, true)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import { changeLanguage } from '@/i18n-config/i18next-config'
|
||||
import { LOCALE_COOKIE_NAME } from '@/config'
|
||||
import { changeLanguage } from '@/i18n-config/i18next-config'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
|
||||
export const i18n = {
|
||||
@@ -23,8 +23,11 @@ export const getLocaleOnClient = (): Locale => {
|
||||
}
|
||||
|
||||
export const renderI18nObject = (obj: Record<string, string>, language: string) => {
|
||||
if (!obj) return ''
|
||||
if (obj?.[language]) return obj[language]
|
||||
if (obj?.en_US) return obj.en_US
|
||||
if (!obj)
|
||||
return ''
|
||||
if (obj?.[language])
|
||||
return obj[language]
|
||||
if (obj?.en_US)
|
||||
return obj.en_US
|
||||
return Object.values(obj)[0]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import data from './languages.json'
|
||||
|
||||
export type Item = {
|
||||
value: number | string
|
||||
name: string
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { cookies, headers } from 'next/headers'
|
||||
import Negotiator from 'negotiator'
|
||||
import type { Locale } from '.'
|
||||
import { match } from '@formatjs/intl-localematcher'
|
||||
|
||||
import { createInstance } from 'i18next'
|
||||
|
||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||
import Negotiator from 'negotiator'
|
||||
import { cookies, headers } from 'next/headers'
|
||||
import { initReactI18next } from 'react-i18next/initReactI18next'
|
||||
import { i18n } from '.'
|
||||
import type { Locale } from '.'
|
||||
|
||||
// https://locize.com/blog/next-13-app-dir-i18n/
|
||||
const initI18next = async (lng: Locale, ns: string) => {
|
||||
|
||||
Reference in New Issue
Block a user