Refactor project for i18n support: Rename project to "i18n-translator" and update package.json and package-lock.json accordingly. Enhance localization by integrating translation functions across various components, including AddToCartButton, Content, GoogleLoginButton, and others, to provide dynamic text rendering based on user language preferences. Update localization files for multiple languages, ensuring comprehensive support for internationalization.
This commit is contained in:
340
translate-i18n.js
Executable file
340
translate-i18n.js
Executable file
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const OpenAI = require('openai');
|
||||
|
||||
// Configuration
|
||||
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
||||
const LOCALES_DIR = './src/i18n/locales';
|
||||
const GERMAN_FILE = path.join(LOCALES_DIR, 'de/translation.js');
|
||||
const ENGLISH_FILE = path.join(LOCALES_DIR, 'en/translation.js');
|
||||
|
||||
// Model configuration
|
||||
const GERMAN_TO_ENGLISH_MODEL = 'gpt-4.1'; // High-quality model for German -> English (critical step)
|
||||
const ENGLISH_TO_OTHER_MODEL = 'gpt-4.1-mini'; // Faster/cheaper model for English -> Other languages
|
||||
|
||||
// Supported languages for translation
|
||||
const TARGET_LANGUAGES = {
|
||||
'bg': 'Bulgarian',
|
||||
'cs': 'Czech',
|
||||
'es': 'Spanish',
|
||||
'fr': 'French',
|
||||
'el': 'Greek',
|
||||
'hr': 'Croatian',
|
||||
'hu': 'Hungarian',
|
||||
'it': 'Italian',
|
||||
'pl': 'Polish',
|
||||
'ro': 'Romanian',
|
||||
'ru': 'Russian',
|
||||
'sk': 'Slovak',
|
||||
'sl': 'Slovenian',
|
||||
'sr': 'Serbian',
|
||||
'sv': 'Swedish',
|
||||
'tr': 'Turkish',
|
||||
'uk': 'Ukrainian',
|
||||
'ar': 'Arabic (Egyptian)',
|
||||
'zh': 'Chinese (Simplified)'
|
||||
};
|
||||
|
||||
// Initialize OpenAI client
|
||||
const openai = new OpenAI({
|
||||
apiKey: OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
// System prompt for German to English translation
|
||||
const GERMAN_TO_ENGLISH_SYSTEM_PROMPT = `
|
||||
You MUST translate German strings to English AND add the original German text as a comment after EVERY translated string.
|
||||
|
||||
CRITICAL REQUIREMENT: Every translated string must have the original German text as a comment.
|
||||
|
||||
Rules:
|
||||
1. Translate all German strings to English
|
||||
2. MANDATORY: Add the original German text as a comment after EVERY translated string using // format
|
||||
3. Preserve all existing comments from the German version
|
||||
4. Maintain the exact JavaScript object structure and formatting
|
||||
5. Keep all interpolation variables like {{count}}, {{vat}}, etc. unchanged
|
||||
6. Keep locale codes appropriate for English
|
||||
7. For the locale section, use "en-US" as code
|
||||
8. Do not translate technical terms that are already in English
|
||||
9. Preserve any special formatting or HTML entities
|
||||
10. Return a valid JavaScript object (not JSON) that can be exported
|
||||
|
||||
MANDATORY FORMAT for every string:
|
||||
"englishTranslation": "English Translation", // Original German Text
|
||||
|
||||
Examples:
|
||||
"login": "Login", // Anmelden
|
||||
"email": "Email", // E-Mail
|
||||
"password": "Password", // Passwort
|
||||
"home": "Home", // Startseite
|
||||
|
||||
DO NOT output any string without its German comment. Every single translated string needs the German original as a comment.
|
||||
`;
|
||||
|
||||
// System prompt template for English to other languages (file content will be inserted)
|
||||
const ENGLISH_TO_OTHER_SYSTEM_PROMPT_TEMPLATE = `
|
||||
Translate the English strings in the following file to {{targetLanguage}}, preserving the German comments.
|
||||
|
||||
Rules:
|
||||
1. Translate only the English strings to {{targetLanguage}}
|
||||
2. Keep all German comments unchanged
|
||||
3. Maintain the exact JavaScript object structure and formatting
|
||||
4. Keep all interpolation variables like {{count}}, {{vat}}, etc. unchanged
|
||||
5. Update locale code appropriately for the target language
|
||||
6. Do not translate technical terms, API keys, or code-related strings
|
||||
7. Preserve any special formatting or HTML entities
|
||||
8. Keep existing comments from the English version
|
||||
9. Return a valid JavaScript object (not JSON) that can be exported
|
||||
|
||||
Here is the English translation file to translate:
|
||||
|
||||
{{englishFileContent}}
|
||||
`;
|
||||
|
||||
// Function to read and parse JavaScript export file
|
||||
function readTranslationFile(filePath) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
// Remove the export default and evaluate the object
|
||||
const objectContent = content.replace(/^export default\s*/, '').replace(/;\s*$/, '');
|
||||
return eval(`(${objectContent})`);
|
||||
} catch (error) {
|
||||
console.error(`Error reading ${filePath}:`, error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to write translation file (preserving comments as string)
|
||||
function writeTranslationFile(filePath, translationString) {
|
||||
try {
|
||||
// Ensure directory exists
|
||||
const dir = path.dirname(filePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
// Ensure the string has proper export format
|
||||
const content = translationString.startsWith('export default')
|
||||
? translationString
|
||||
: `export default ${translationString}`;
|
||||
|
||||
// Ensure it ends with semicolon and newline
|
||||
const finalContent = content.endsWith(';\n') ? content : content.replace(/;?\s*$/, ';\n');
|
||||
|
||||
fs.writeFileSync(filePath, finalContent, 'utf8');
|
||||
console.log(`✅ Successfully wrote ${filePath}`);
|
||||
} catch (error) {
|
||||
console.error(`Error writing ${filePath}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to translate content using OpenAI (for German to English)
|
||||
async function translateContent(content, systemPrompt, targetLanguage = null, model = 'gpt-4') {
|
||||
try {
|
||||
const prompt = targetLanguage
|
||||
? systemPrompt.replace(/{{targetLanguage}}/g, targetLanguage)
|
||||
: systemPrompt;
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: model,
|
||||
messages: [
|
||||
{ role: 'system', content: prompt },
|
||||
{ role: 'user', content: `Please translate this translation file content:\n\n${content}` }
|
||||
],
|
||||
temperature: 0.1,
|
||||
max_tokens: 4000
|
||||
});
|
||||
|
||||
return response.choices[0].message.content;
|
||||
} catch (error) {
|
||||
console.error('OpenAI API error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to translate English to other languages (optimized for caching)
|
||||
async function translateToTargetLanguage(englishContent, targetLanguage, model = 'gpt-4o-mini') {
|
||||
try {
|
||||
// Create system prompt with file content (cacheable)
|
||||
const systemPrompt = ENGLISH_TO_OTHER_SYSTEM_PROMPT_TEMPLATE
|
||||
.replace(/{{targetLanguage}}/g, targetLanguage)
|
||||
.replace(/{{englishFileContent}}/g, englishContent);
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: model,
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
{ role: 'user', content: `Please translate to ${targetLanguage}` }
|
||||
],
|
||||
temperature: 0.1,
|
||||
max_tokens: 4000
|
||||
});
|
||||
|
||||
return response.choices[0].message.content;
|
||||
} catch (error) {
|
||||
console.error('OpenAI API error:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to extract JavaScript object string from OpenAI response (preserving comments)
|
||||
function extractJSObjectString(response) {
|
||||
try {
|
||||
// Remove code block markers if present
|
||||
let cleaned = response.replace(/```javascript|```json|```/g, '').trim();
|
||||
|
||||
// Try to find the object in the response
|
||||
const objectMatch = cleaned.match(/\{[\s\S]*\}/);
|
||||
if (objectMatch) {
|
||||
return objectMatch[0];
|
||||
}
|
||||
|
||||
// If no object found, return the cleaned response
|
||||
return cleaned;
|
||||
} catch (error) {
|
||||
console.error('Error parsing OpenAI response:', error.message);
|
||||
console.log('Response was:', response);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Main translation function
|
||||
async function translateToEnglish() {
|
||||
console.log(`🔄 Step 1: Translating German to English using ${GERMAN_TO_ENGLISH_MODEL}...`);
|
||||
|
||||
// Read German translation file
|
||||
const germanContent = fs.readFileSync(GERMAN_FILE, 'utf8');
|
||||
|
||||
try {
|
||||
const translatedContent = await translateContent(germanContent, GERMAN_TO_ENGLISH_SYSTEM_PROMPT, null, GERMAN_TO_ENGLISH_MODEL);
|
||||
const englishObjectString = extractJSObjectString(translatedContent);
|
||||
|
||||
if (englishObjectString) {
|
||||
writeTranslationFile(ENGLISH_FILE, englishObjectString);
|
||||
console.log('✅ German to English translation completed');
|
||||
return englishObjectString;
|
||||
} else {
|
||||
throw new Error('Failed to parse English translation');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error translating to English:', error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to translate English to other languages
|
||||
async function translateToOtherLanguages(englishObjectString) {
|
||||
console.log(`🔄 Step 2: Translating English to other languages using ${ENGLISH_TO_OTHER_MODEL}...`);
|
||||
|
||||
const englishContent = `export default ${englishObjectString};`;
|
||||
|
||||
for (const [langCode, langName] of Object.entries(TARGET_LANGUAGES)) {
|
||||
try {
|
||||
console.log(`🔄 Translating to ${langName} (${langCode})...`);
|
||||
|
||||
const translatedContent = await translateToTargetLanguage(
|
||||
englishContent,
|
||||
langName,
|
||||
ENGLISH_TO_OTHER_MODEL
|
||||
);
|
||||
|
||||
const translatedObjectString = extractJSObjectString(translatedContent);
|
||||
|
||||
if (translatedObjectString) {
|
||||
// Update locale code in the string
|
||||
const updatedString = translatedObjectString.replace(
|
||||
/"code":\s*"[^"]*"/,
|
||||
`"code": "${getLocaleCode(langCode)}"`
|
||||
);
|
||||
|
||||
const targetFile = path.join(LOCALES_DIR, `${langCode}/translation.js`);
|
||||
writeTranslationFile(targetFile, updatedString);
|
||||
console.log(`✅ ${langName} translation completed`);
|
||||
} else {
|
||||
console.error(`❌ Failed to parse ${langName} translation`);
|
||||
}
|
||||
|
||||
// Add delay to avoid rate limiting
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error translating to ${langName}:`, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get locale codes
|
||||
function getLocaleCode(langCode) {
|
||||
const localeCodes = {
|
||||
'bg': 'bg-BG',
|
||||
'cs': 'cs-CZ',
|
||||
'es': 'es-ES',
|
||||
'fr': 'fr-FR',
|
||||
'el': 'el-GR',
|
||||
'hr': 'hr-HR',
|
||||
'hu': 'hu-HU',
|
||||
'it': 'it-IT',
|
||||
'pl': 'pl-PL',
|
||||
'ro': 'ro-RO',
|
||||
'ru': 'ru-RU',
|
||||
'sk': 'sk-SK',
|
||||
'sl': 'sl-SI',
|
||||
'sr': 'sr-RS',
|
||||
'sv': 'sv-SE',
|
||||
'tr': 'tr-TR',
|
||||
'uk': 'uk-UA',
|
||||
'ar': 'ar-EG',
|
||||
'zh': 'zh-CN'
|
||||
};
|
||||
return localeCodes[langCode] || `${langCode}-${langCode.toUpperCase()}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Main execution
|
||||
async function main() {
|
||||
console.log('🚀 Starting translation process...');
|
||||
|
||||
// Check if OpenAI API key is set
|
||||
if (!OPENAI_API_KEY) {
|
||||
console.error('❌ OPENAI_API_KEY environment variable is not set');
|
||||
console.log('Please set your OpenAI API key: export OPENAI_API_KEY="your-api-key-here"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if German file exists
|
||||
if (!fs.existsSync(GERMAN_FILE)) {
|
||||
console.error(`❌ German translation file not found: ${GERMAN_FILE}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Step 1: Translate German to English
|
||||
const englishObjectString = await translateToEnglish();
|
||||
|
||||
if (englishObjectString) {
|
||||
// Step 2: Translate English to other languages
|
||||
await translateToOtherLanguages(englishObjectString);
|
||||
console.log('🎉 All translations completed successfully!');
|
||||
} else {
|
||||
console.error('❌ Failed to create English translation, stopping process');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Translation process failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
translateToEnglish,
|
||||
translateToOtherLanguages,
|
||||
readTranslationFile,
|
||||
writeTranslationFile
|
||||
};
|
||||
Reference in New Issue
Block a user