mirror of
https://github.com/langgenius/dify.git
synced 2026-03-13 02:57:41 +08:00
feat: fix i18n missing keys and merge upstream/main (#24615)
Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: kenwoodjw <blackxin55+@gmail.com> Signed-off-by: Yongtao Huang <yongtaoh2022@gmail.com> Signed-off-by: yihong0618 <zouzou0208@gmail.com> Signed-off-by: zhanluxianshen <zhanluxianshen@163.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: GuanMu <ballmanjq@gmail.com> Co-authored-by: Davide Delbianco <davide.delbianco@outlook.com> Co-authored-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com> Co-authored-by: kenwoodjw <blackxin55+@gmail.com> Co-authored-by: Yongtao Huang <yongtaoh2022@gmail.com> Co-authored-by: Yongtao Huang <99629139+hyongtao-db@users.noreply.github.com> Co-authored-by: Qiang Lee <18018968632@163.com> Co-authored-by: 李强04 <liqiang04@gaotu.cn> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: Matri Qi <matrixdom@126.com> Co-authored-by: huayaoyue6 <huayaoyue@163.com> Co-authored-by: Bowen Liang <liangbowen@gf.com.cn> Co-authored-by: znn <jubinkumarsoni@gmail.com> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: yihong <zouzou0208@gmail.com> Co-authored-by: Muke Wang <shaodwaaron@gmail.com> Co-authored-by: wangmuke <wangmuke@kingsware.cn> Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Co-authored-by: quicksand <quicksandzn@gmail.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: Eric Guo <eric.guocz@gmail.com> Co-authored-by: Zhedong Cen <cenzhedong2@126.com> Co-authored-by: jiangbo721 <jiangbo721@163.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: hjlarry <25834719+hjlarry@users.noreply.github.com> Co-authored-by: lxsummer <35754229+lxjustdoit@users.noreply.github.com> Co-authored-by: 湛露先生 <zhanluxianshen@163.com> Co-authored-by: Guangdong Liu <liugddx@gmail.com> Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Yessenia-d <yessenia.contact@gmail.com> Co-authored-by: huangzhuo1949 <167434202+huangzhuo1949@users.noreply.github.com> Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com> Co-authored-by: 17hz <0x149527@gmail.com> Co-authored-by: Amy <1530140574@qq.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: Nite Knite <nkCoding@gmail.com> Co-authored-by: Yeuoly <45712896+Yeuoly@users.noreply.github.com> Co-authored-by: Petrus Han <petrus.hanks@gmail.com> Co-authored-by: iamjoel <2120155+iamjoel@users.noreply.github.com> Co-authored-by: Kalo Chin <frog.beepers.0n@icloud.com> Co-authored-by: Ujjwal Maurya <ujjwalsbx@gmail.com> Co-authored-by: Maries <xh001x@hotmail.com>
This commit is contained in:
38
web/scripts/README.md
Normal file
38
web/scripts/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Production Build Optimization Scripts
|
||||
|
||||
## optimize-standalone.js
|
||||
|
||||
This script removes unnecessary development dependencies from the Next.js standalone build output to reduce the production Docker image size.
|
||||
|
||||
### What it does
|
||||
|
||||
The script specifically targets and removes `jest-worker` packages that are bundled with Next.js but not needed in production. These packages are included because:
|
||||
|
||||
1. Next.js includes jest-worker in its compiled dependencies
|
||||
1. terser-webpack-plugin (used by Next.js for minification) depends on jest-worker
|
||||
1. pnpm's dependency resolution creates symlinks to jest-worker in various locations
|
||||
|
||||
### Usage
|
||||
|
||||
The script is automatically run during Docker builds via the `build:docker` npm script:
|
||||
|
||||
```bash
|
||||
# Docker build (removes jest-worker after build)
|
||||
pnpm build:docker
|
||||
```
|
||||
|
||||
To run the optimization manually:
|
||||
|
||||
```bash
|
||||
node scripts/optimize-standalone.js
|
||||
```
|
||||
|
||||
### What gets removed
|
||||
|
||||
- `node_modules/.pnpm/next@*/node_modules/next/dist/compiled/jest-worker`
|
||||
- `node_modules/.pnpm/terser-webpack-plugin@*/node_modules/jest-worker` (symlinks)
|
||||
- `node_modules/.pnpm/jest-worker@*` (actual packages)
|
||||
|
||||
### Impact
|
||||
|
||||
Removing jest-worker saves approximately 36KB per instance from the production image. While this may seem small, it helps ensure production images only contain necessary runtime dependencies.
|
||||
149
web/scripts/optimize-standalone.js
Normal file
149
web/scripts/optimize-standalone.js
Normal file
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Script to optimize Next.js standalone output for production
|
||||
* Removes unnecessary files like jest-worker that are bundled with Next.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🔧 Optimizing standalone output...');
|
||||
|
||||
const standaloneDir = path.join(__dirname, '..', '.next', 'standalone');
|
||||
|
||||
// Check if standalone directory exists
|
||||
if (!fs.existsSync(standaloneDir)) {
|
||||
console.error('❌ Standalone directory not found. Please run "next build" first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// List of paths to remove (relative to standalone directory)
|
||||
const pathsToRemove = [
|
||||
// Remove jest-worker from Next.js compiled dependencies
|
||||
'node_modules/.pnpm/next@*/node_modules/next/dist/compiled/jest-worker',
|
||||
// Remove jest-worker symlinks from terser-webpack-plugin
|
||||
'node_modules/.pnpm/terser-webpack-plugin@*/node_modules/jest-worker',
|
||||
// Remove actual jest-worker packages (directories only, not symlinks)
|
||||
'node_modules/.pnpm/jest-worker@*',
|
||||
];
|
||||
|
||||
// Function to safely remove a path
|
||||
function removePath(basePath, relativePath) {
|
||||
const fullPath = path.join(basePath, relativePath);
|
||||
|
||||
// Handle wildcard patterns
|
||||
if (relativePath.includes('*')) {
|
||||
const parts = relativePath.split('/');
|
||||
let currentPath = basePath;
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
if (part.includes('*')) {
|
||||
// Find matching directories
|
||||
if (fs.existsSync(currentPath)) {
|
||||
const entries = fs.readdirSync(currentPath);
|
||||
|
||||
// replace '*' with '.*'
|
||||
const regexPattern = part.replace(/\*/g, '.*');
|
||||
|
||||
const regex = new RegExp(`^${regexPattern}$`);
|
||||
|
||||
for (const entry of entries) {
|
||||
if (regex.test(entry)) {
|
||||
const remainingPath = parts.slice(i + 1).join('/');
|
||||
const matchedPath = path.join(currentPath, entry, remainingPath);
|
||||
|
||||
try {
|
||||
// Use lstatSync to check if path exists (works for both files and symlinks)
|
||||
const stats = fs.lstatSync(matchedPath);
|
||||
|
||||
if (stats.isSymbolicLink()) {
|
||||
// Remove symlink
|
||||
fs.unlinkSync(matchedPath);
|
||||
console.log(`✅ Removed symlink: ${path.relative(basePath, matchedPath)}`);
|
||||
} else {
|
||||
// Remove directory/file
|
||||
fs.rmSync(matchedPath, { recursive: true, force: true });
|
||||
console.log(`✅ Removed: ${path.relative(basePath, matchedPath)}`);
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently ignore ENOENT (path not found) errors
|
||||
if (error.code !== 'ENOENT') {
|
||||
console.error(`❌ Failed to remove ${matchedPath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
currentPath = path.join(currentPath, part);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Direct path removal
|
||||
if (fs.existsSync(fullPath)) {
|
||||
try {
|
||||
fs.rmSync(fullPath, { recursive: true, force: true });
|
||||
console.log(`✅ Removed: ${relativePath}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to remove ${fullPath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unnecessary paths
|
||||
console.log('🗑️ Removing unnecessary files...');
|
||||
for (const pathToRemove of pathsToRemove) {
|
||||
removePath(standaloneDir, pathToRemove);
|
||||
}
|
||||
|
||||
// Calculate size reduction
|
||||
console.log('\n📊 Optimization complete!');
|
||||
|
||||
// Optional: Display the size of remaining jest-related files (if any)
|
||||
const checkForJest = (dir) => {
|
||||
const jestFiles = [];
|
||||
|
||||
function walk(currentPath) {
|
||||
if (!fs.existsSync(currentPath)) return;
|
||||
|
||||
try {
|
||||
const entries = fs.readdirSync(currentPath);
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentPath, entry);
|
||||
|
||||
try {
|
||||
const stat = fs.lstatSync(fullPath); // Use lstatSync to handle symlinks
|
||||
|
||||
if (stat.isDirectory() && !stat.isSymbolicLink()) {
|
||||
// Skip node_modules subdirectories to avoid deep traversal
|
||||
if (entry === 'node_modules' && currentPath !== standaloneDir) {
|
||||
continue;
|
||||
}
|
||||
walk(fullPath);
|
||||
} else if (stat.isFile() && entry.includes('jest')) {
|
||||
jestFiles.push(path.relative(standaloneDir, fullPath));
|
||||
}
|
||||
} catch (err) {
|
||||
// Skip files that can't be accessed
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Skip directories that can't be read
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
walk(dir);
|
||||
return jestFiles;
|
||||
};
|
||||
|
||||
const remainingJestFiles = checkForJest(standaloneDir);
|
||||
if (remainingJestFiles.length > 0) {
|
||||
console.log('\n⚠️ Warning: Some jest-related files still remain:');
|
||||
remainingJestFiles.forEach(file => console.log(` - ${file}`));
|
||||
} else {
|
||||
console.log('\n✨ No jest-related files found in standalone output!');
|
||||
}
|
||||
Reference in New Issue
Block a user