mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-29 05:20:49 +00:00
🌉 fix: Preserve Missing Locize Translations (#14917)
This commit is contained in:
parent
57ea1137f6
commit
c77e6a5ad2
2 changed files with 100 additions and 3 deletions
14
.github/workflows/locize-i18n-sync.yml
vendored
14
.github/workflows/locize-i18n-sync.yml
vendored
|
|
@ -2,11 +2,12 @@ name: Sync Locize Translations & Create Translation PR
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [dev]
|
||||
paths:
|
||||
- 'client/src/locales/en/**'
|
||||
repository_dispatch:
|
||||
types: [locize/versionPublished]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: locize-i18n-sync
|
||||
|
|
@ -51,7 +52,7 @@ jobs:
|
|||
|
||||
create-pull-request:
|
||||
name: Create Translation PR on Version Published
|
||||
if: ${{ github.event_name == 'repository_dispatch' }}
|
||||
if: ${{ github.event_name == 'repository_dispatch' || github.event_name == 'workflow_dispatch' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: sync-translations
|
||||
permissions:
|
||||
|
|
@ -61,6 +62,7 @@ jobs:
|
|||
- name: Checkout Repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
|
||||
# Keep a baseline so generated changes can be checked before opening a PR.
|
||||
|
|
@ -75,6 +77,12 @@ jobs:
|
|||
path: "client/src/locales"
|
||||
version: latest
|
||||
|
||||
- name: Preserve Repository Translations Missing from locize
|
||||
run: |
|
||||
node scripts/merge-locize-download.mjs \
|
||||
--base-dir "$RUNNER_TEMP/locize-locale-baseline" \
|
||||
--current-dir client/src/locales
|
||||
|
||||
- name: Validate Downloaded Translations
|
||||
run: |
|
||||
node scripts/validate-locize-download.mjs \
|
||||
|
|
@ -91,7 +99,7 @@ jobs:
|
|||
add-paths: |
|
||||
client/src/locales/**
|
||||
commit-message: "🌍 i18n: Update translation.json with latest translations"
|
||||
base: main
|
||||
base: dev
|
||||
branch: i18n/locize-translation-update
|
||||
title: "🌍 i18n: Update translation.json with latest translations"
|
||||
body: |
|
||||
|
|
|
|||
89
scripts/merge-locize-download.mjs
Normal file
89
scripts/merge-locize-download.mjs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const args = new Map();
|
||||
for (let index = 2; index < process.argv.length; index += 1) {
|
||||
if (!process.argv[index].startsWith('--')) continue;
|
||||
args.set(process.argv[index].slice(2), process.argv[index + 1]);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
const baseDir = args.get('base-dir');
|
||||
const currentDir = args.get('current-dir');
|
||||
if (!baseDir || !currentDir) {
|
||||
console.error('Usage: merge-locize-download.mjs --base-dir <path> --current-dir <path>');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
function isObject(value) {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function leafCount(value) {
|
||||
if (!isObject(value)) return 1;
|
||||
return Object.values(value).reduce((count, child) => count + leafCount(child), 0);
|
||||
}
|
||||
|
||||
function mergeValues(base, current) {
|
||||
if (!isObject(base) || !isObject(current)) return { value: current, restored: 0 };
|
||||
|
||||
let restored = 0;
|
||||
const value = {};
|
||||
for (const [key, baseValue] of Object.entries(base)) {
|
||||
if (!Object.hasOwn(current, key)) {
|
||||
value[key] = baseValue;
|
||||
restored += leafCount(baseValue);
|
||||
continue;
|
||||
}
|
||||
const merged = mergeValues(baseValue, current[key]);
|
||||
value[key] = merged.value;
|
||||
restored += merged.restored;
|
||||
}
|
||||
|
||||
for (const [key, currentValue] of Object.entries(current)) {
|
||||
if (!Object.hasOwn(base, key)) value[key] = currentValue;
|
||||
}
|
||||
|
||||
return { value, restored };
|
||||
}
|
||||
|
||||
async function jsonFiles(directory, relative = '') {
|
||||
const entries = await readdir(path.join(directory, relative), { withFileTypes: true });
|
||||
const files = [];
|
||||
for (const entry of entries) {
|
||||
const entryRelative = path.join(relative, entry.name);
|
||||
if (entry.isDirectory()) files.push(...(await jsonFiles(directory, entryRelative)));
|
||||
else if (entry.isFile() && entry.name.endsWith('.json')) files.push(entryRelative);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
async function readJson(filePath) {
|
||||
return JSON.parse(await readFile(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
let restored = 0;
|
||||
let changedFiles = 0;
|
||||
for (const relative of await jsonFiles(baseDir)) {
|
||||
const basePath = path.join(baseDir, relative);
|
||||
const currentPath = path.join(currentDir, relative);
|
||||
const base = await readJson(basePath);
|
||||
let current;
|
||||
try {
|
||||
current = await readJson(currentPath);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') throw error;
|
||||
current = {};
|
||||
}
|
||||
|
||||
const merged = mergeValues(base, current);
|
||||
if (merged.restored === 0) continue;
|
||||
await mkdir(path.dirname(currentPath), { recursive: true });
|
||||
await writeFile(currentPath, `${JSON.stringify(merged.value, null, 2)}\n`);
|
||||
restored += merged.restored;
|
||||
changedFiles += 1;
|
||||
}
|
||||
|
||||
console.log(`Preserved ${restored} repository translation value(s) across ${changedFiles} file(s).`);
|
||||
Loading…
Add table
Add a link
Reference in a new issue