tolgee-platform icon indicating copy to clipboard operation
tolgee-platform copied to clipboard

New json format: JSON_ANGULAR

Open zip-fa opened this issue 7 months ago • 1 comments

Is your feature request related to a problem? Please describe. Modern Angular has very specific json format for translated files:

{
  "locale": "ru",
  "translations": {
    "system.request_timeout": "Превышено время отправки запроса"
  }
}

it is possible to download "almost" angular-like json with current config:

{
  "$schema": "https://docs.tolgee.io/cli-schema.json",
  "format": "JSON_ICU",
  "push": {
    "removeOtherKeys": true,
    "forceMode": "OVERRIDE",
    "namespaces": [
      "frontend"
    ],
    "filesTemplate": "./{namespace}.{languageTag}.json",
    "languages": [
      "ru"
    ]
  },
  "pull": {
    "namespaces": [
      "frontend"
    ],
    "path": "./apps/frontend/src/locale",
    "fileStructureTemplate": "messages.{languageTag}.{extension}",
    "supportArrays": false,
    "delimiter": null
  }
}

which downloads in this format:

{
  "locale": "ru",
  "translations.system.request_timeout": "Превышено время отправки запроса"
}

then, it should be processed via hack-ish script to match Angular's format

Describe the solution you'd like Add native JSON_ANGULAR format

Describe alternatives you've considered

Script, which i use as a workaround (maybe it can help someone):

#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const readline = require('readline');

// Create readline interface for user input
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Function to prompt user for input
function prompt(question) {
  return new Promise((resolve) => {
    rl.question(question, (answer) => {
      resolve(answer);
    });
  });
}

// Main function
async function main() {
  try {
    console.log('Checking environment variables...');
    
    // Check for required environment variables
    let projectId = process.env.PROJECT_ID;
    let apiKey = process.env.API_KEY;
    let apiUrl = process.env.API_URL;
    
    // Prompt for missing variables
    if (!projectId) {
      projectId = await prompt('PROJECT_ID not found. Please enter PROJECT_ID: ');
    }
    
    if (!apiKey) {
      apiKey = await prompt('API_KEY not found. Please enter API_KEY: ');
    }
    
    if (!apiUrl) {
      apiUrl = await prompt('API_URL not found. Please enter API_URL: ');
    }
    
    console.log('Running tolgee pull command...');
    
    // Execute tolgee pull command
    const command = `npx tolgee pull --project-id=${projectId} --api-url=${apiUrl} --api-key=${apiKey}`;
    execSync(command, { stdio: 'inherit' });
    
    console.log('Tolgee pull completed successfully.');
    
    // Read .tolgeerc.json to get the path to downloaded translations
    const tolgeeConfigPath = path.resolve(process.cwd(), '.tolgeerc.json');
    const tolgeeConfig = JSON.parse(fs.readFileSync(tolgeeConfigPath, 'utf8'));
    
    if (!tolgeeConfig.pull || !tolgeeConfig.pull.path) {
      throw new Error('Could not find pull.path in .tolgeerc.json');
    }
    
    const translationsPath = path.resolve(process.cwd(), tolgeeConfig.pull.path);
    console.log(`Processing translation files in ${translationsPath}...`);
    
    // Get all JSON files in the translations directory
    const files = fs.readdirSync(translationsPath)
      .filter(file => file.startsWith('messages.') && file.endsWith('.json'));
    
    if (files.length === 0) {
      console.log('No translation files found.');
      rl.close();
      return;
    }
    
    // Process each translation file
    for (const file of files) {
      const filePath = path.join(translationsPath, file);
      console.log(`Processing ${filePath}...`);
      
      const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
      const locale = content.locale;
      
      // Create a new object with the transformed structure
      const transformedContent = {
        locale: locale,
        translations: {}
      };
      
      // Process each key in the original content
      for (const [key, value] of Object.entries(content)) {
        if (key === 'locale') continue;
        
        // Extract the part after "translations." prefix
        if (key.startsWith('translations.')) {
          const newKey = key.substring('translations.'.length);
          transformedContent.translations[newKey] = value;
        }
      }
      
      // Write the transformed content back to the file
      fs.writeFileSync(filePath, JSON.stringify(transformedContent, null, 4), 'utf8');
      console.log(`Transformed ${filePath} successfully.`);
    }
    
    console.log('All translation files processed successfully.');
    rl.close();
    
  } catch (error) {
    console.error('Error:', error.message);
    rl.close();
    process.exit(1);
  }
}

// Run the main function
main();

Additional context

zip-fa avatar May 21 '25 16:05 zip-fa

Hi! Thanks for the report! I'm sorry for the delay. It looks like no one has addressed your issue yet.

Our team doesn't have any Angular experts, but it would be great to have proper support for it. If anyone wants to implement this format, please feel free to do so. We will gladly accept PRs.

Anty0 avatar Sep 02 '25 14:09 Anty0