Is there a method available to streamline the process of generating .json files for language translations?

Working with translation files can be a tedious task, especially when adding new keys for different languages. Making sure that each key is included in all the JSON files can lead to errors and repetitive editing.

Is there a more efficient way to handle this process and minimize mistakes?

An ideal solution would involve automating the updating of all JSON files whenever a new key is added. Imagine being able to run a script from Windows PowerShell that seamlessly updates all language files with the new key and its corresponding translation.

Answer №1

If you're looking to accomplish a similar task in Powershell, you might consider the following approach:

$mainFile = "english.json"

function Get-LanguageMap($file){

    $map = @{}

    $languageJson = ConvertFrom-Json (gc $file -Raw)
    $languageJson | gm -MemberType NoteProperty | % {
        $map.Add($_.Name, ($languageJson | select -ExpandProperty $_.Name))
    }

    return $map
}

$mainLanguage = Get-LanguageMap $mainFile

ls | ? { $_.Name -like "*.json" -and $_.Name -ne $mainFile } | % {
    $language = Get-LanguageMap $_.FullName
    $mainLanguage.GetEnumerator() | % {
        if(!$language.ContainsKey($_.Key)){
            $language.Add($_.Key, $_.Value)
        }
    }

    ConvertTo-Json $language | Out-File -FilePath $_.FullName -Force -Encoding utf8
}

This script essentially builds a dictionary from your English JSON file. It then scans all other language files, checking for keys missing that are present in the English file. It adds any missing keys and corresponding values before saving the updated language files in Unicode format.

Alternatively, if you prefer using Windows Scripting with JavaScript, here's how you can achieve a similar outcome:

var mainFile = "english.json"
var fso = new ActiveXObject("Scripting.FileSystemObject");
var scriptPath = fso.GetParentFolderName(WScript.ScriptFullName);
var encoding = 'utf-8';
var folder = fso.GetFolder(scriptPath);
var fileList = new Enumerator(folder.files);

function getLanguageMapping(fileName){
    var path = scriptPath + '\\' + fileName;
    var stream = new ActiveXObject("ADODB.Stream");

    try{
        stream.CharSet = encoding;
        stream.Open();
        stream.LoadFromFile(path);
        var text = stream.ReadText();
        var json = {};
        eval('json = ' + text); 
        return json;
    }
    finally{
        stream.Close();
    }
}

function saveAsUtf8(fileName, text){
    var path = scriptPath + '\\' + fileName;
    var stream = new ActiveXObject("ADODB.Stream"); 

    try{
        stream.CharSet = encoding;
        stream.Open();
        stream.Position = 0;
        stream.WriteText(text);
        stream.SaveToFile(path, 2); 
    }
    finally{
        stream.Close();
    }
}

var languageFiles = [];
var masterMapping = getLanguageMapping(mainFile);

for (; !fileList.atEnd(); fileList.moveNext())
{
    var file = fileList.item();
    var extension = file.Name.split('.').pop();
    if(extension != "json" || file.Name == mainFile){
       continue;
    }

    var mapping = getLanguageMapping(file.Name);
    var newLanguageText = '{\r\n';
    var index = 0;

    for(var name in masterMapping){
        var value = '';

        if(mapping[name]){
            value = mapping[name];
        }
        else{
            value = masterMapping[name];
        }

        if(index > 0){
            newLanguageText += ",\r\n";
        }

        newLanguageText += "\t'" + name + "': '" + value + "'";
        index++;
    }

    newLanguageText += '\r\n}'

    saveAsUtf8(file.Name, newLanguageText);
}

To execute the Javascript code via command line, use the following command:

Cscript.exe "C:\yourscript.js"

I trust this information will prove useful.

Answer №2

Is there a way to automate the creation of .json files for language translations?

Absolutely, automation tools like Grunt and Gulp are designed specifically for executing automatic tasks.

Manually handling these tasks is time-consuming and prone to errors, which is why using Grunt/Gulp is highly recommended.

By setting up a simple configuration in Grunt/Gulp, all relevant .json files can be monitored at the same time: any new key added to one file will trigger the execution of a custom script of your choice.


HOW GRUNT/GULP CAN MAKE IT HAPPEN:

  1. Grunt/Gulp will continuously monitor all necessary JSON files;
  2. When a change is detected in one of these files, a custom script is executed;
  3. The custom script will read the modified file to get the new key(s) and value(s);
  4. This information is then written to all other related JSON files.

SETTING UP THE GRUNT CONFIGURATION

To automatically detect file changes and run myCustomScript, you can use grunt-contrib-watch as follows:

watch: {
  scripts: {
    files: ['**/*.locale.json'],
    tasks: ['myCustomScript'],
  },
}

CUSTOM SCRIPT FOR ADDING NEW KEYS TO THE RELEVANT .JSON FILES:

  grunt.event.on('watch', function(action, filepath) {
    // Path to the file with detected changes
    grunt.config.set('filepath', grunt.config.escape(filepath));
   });

  var myCustomScript=function(changedFile,keyFile){

     var project = grunt.file.readJSON(changedFile);
     // Store the changed file as a json object

     var keys=grunt.file.readJSON(keyFile);
     // Store keyFile as a json object

     // Check if keys from changedFile are in keyFile
     for (var key in project) {
       if (project.hasOwnProperty(key)) {
         if(!keys.hasOwnProperty(key)){
           // New key detected
           newKeyArray.push(key);
         }
       }
     }

  // Update all the other relevant JSON files with `grunt.file.write`, by adding all keys in newKeyArray:

  var filesToChangeArray=grunt.file.match('**/*.locale.json');
  // Array containing all filepaths where changes should be made
  filesToChangeArray.forEach(function(path){
    // Add new keys to the addedContent string from newKeyArray
    newKeyArray.forEach(function(key){
    addedContent+='"'+key+'":"to be set",';
    // Write all new keys to be set in addedContent string
    }
    grunt.file.write(path,addedContent);
    });
  }

Ideally I would like to be able to run a script from Windows PowerShell

While Grunt/Gulp are commonly used to execute javascript/nodejs files, they can also handle the execution of scripts written in different languages.

To run a PowerShell script, you could utilize a Grunt plugin called grunt-shell, like this:

grunt.initConfig({
shell: {
    ps: {
        options: {
            stdout: true
        },
        command: 'powershell myScript.ps1'
    }
}
});

as explained in this StackOverflow post.

If PowerShell is your preference, you can combine both approaches:

  • Effortless detection with Grunt/Gulp watch;
  • PowerShell script execution upon detecting changes.

You could alternatively use only Grunt/Gulp for this task: since Grunt/Gulp already handles the detection process, you just need it to run a custom script that reads and copies new keys (grunt.file.readJSON and grunt.file.write) to the relevant files.

Answer №3

Implemented automation through a nodejs-powered javascript solution executed via the command line.

$ node localeUpdater.js

This script monitors changes in the default locale file (locale-en.json) and updates all other locale files accordingly.

  1. Create or initialize the necessary locale files if they are not present
  2. Add new keys based on the default locale
  3. Remove keys that are no longer present in the default locale

localeUpdater.js

var fs = require("fs");

var localeFileDefault = "locale-en.json";
var localeFileList = ["locale-jp.json", "locale-ph.json"];

fs.watchFile(localeFileDefault, function() {

  var localeDefault = readFile(localeFileDefault);
  var localeCurrent = null;
  var fileNameCurrent = null;

  for (var i in localeFileList) {
    fileNameCurrent = localeFileList[i];

    console.log("Adding new keys from the default locale to file " + fileNameCurrent);
    localeCurrent = readFile(fileNameCurrent);
    for (var key in localeDefault) {
      if (!localeCurrent[key]) {
        console.log(key + " key added.");
        localeCurrent[key] = localeDefault[key];
      }
    }

    console.log("Removing keys not found in the default locale from file " + fileNameCurrent);
    for (var key in localeCurrent) {
      if (!localeDefault[key]) {
        console.log(key + " key removed.");
        delete localeCurrent[key];
      }
    }

    writeFile(fileNameCurrent, JSON.stringify(localeCurrent));
    console.log("File " + fileNameCurrent + " updated.");
  }

});

function readFile(fileName) {
  var result = null;
  if (fs.existsSync(fileName)) {
    result = fs.readFileSync(fileName, "utf8");
    result = result ? JSON.parse(result) : {};
  } else {
    writeFile(fileName, "{}");
    result = {};
  }
  return result;
}

function writeFile(fileName, content) {
  fs.writeFileSync(fileName, content, "utf8");
}

Answer №4

It's important to have various protective measures in place.

First and foremost, your translation function should include safeguards. Consider something like this:

function safeguardTranslation(text) {
    if (translationDatabase[text]) {
        return text;
    }

    return text;
}

As for registering new strings, we typically use regex to search our codebase for instances of safeguardTranslation('...'), compile a list of translations, and send it to a third-party translation service multiple times a day. This external service identifies new strings, adds them to the database, and sends back the translated content. The process of fetching translations involves compiling language files, always defaulting back to English as a failsafe. For example:

_.map(strings, function(string) {
    return localeTranslations[locale][text] || localeTranslations['en_US'][text];
}

By implementing this method, we ensure that even if a specific locale's translation is missing, the English US version will be used as a backup.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to transfer content to clipboard from a Popper element nested within a Dialogue component in Material-UI

If you want to see the bug, try opening this link in Firefox. It almost crashed Chrome :P https://codesandbox.io/s/73z5293391 Click on the OPEN SIMPLE DIALOGUE button. A dialogue will appear, then click on the TOGGLE POPPER button. Now a Popper will be d ...

BOOTSTRAP: changing the layout of panels in various sizes for mobile devices

I'm struggling with how to reorganize my panels on mobile devices. Each panel has a different size. Please refer to the attached screenshot for the page layout on large screens (col-lg): https://i.sstatic.net/xcqaT.png EDIT: The layout on large scre ...

Implementing Ajax Like Button functionality in a Ruby on Rails application

I have a Ruby on Rails project that includes a User model and a Content model, among others. I recently implemented a feature where users can "like" content using the acts_as_votable gem. Currently, the liking functionality works as expected but it requir ...

Android JSON to POJO deserializer with support for polymorphic classes

I'm facing an issue with REST and Android. The problem arises when working with a transport object within the context of a Human class that has Male and Female subclasses. My goal is to use JSON as the transport medium for the human object. Typically, ...

"Discovering the method to showcase content based on page number or when the 'previous' or 'next'

Here is how my Vue component looks like: <template> ... <b-col v-for="item in items" v-bind:key="item.id" cols="2"> ... <strong slot="header" class="text-dark" :title ...

How can Codeception/Selenium help in testing the tooltip or customValidity message?

I am trying to implement a feature in my Codeception scenario where I want to validate a form submission with user errors, such as confirming a wrong email address, and display a custom tooltip message in the browser. HTML <form ... > <label ...

The text field is unable to receive input by clicking a button, only registering values when physically typed into the field

I have three text fields with buttons to increment or decrement the value, and a final text field that displays the calculation of the values in each field. Everything is functioning correctly, however, the issue arises when I try to add values using the ...

Please ensure that the menu is included within the HTML file

How can I include a menu from menu.html into index.html? The content of menu.html is: <li><a href="home.html">Home</a></li> <li><a href="news.html">News</a></li> In index.html, the code is: <!docty ...

Route parameters do not function correctly with computed properties

I'm facing an issue with my getter function that stores all products. When I try to retrieve a single product dynamically based on this.$route.params.id, it doesn't return any value. The code works fine when I navigate to a specific product, but ...

Trouble with Swiper carousel loading new slides

Currently, I am working on a project using Cordova and jQuery. I have implemented the Swiper library by idangero for handling slides. The problem arises when I add new slides and try to display them. Here is the relevant jQuery code snippet: if(row.pict ...

Retaining UI state across views in Ionic and AngularJS

In my experience with Ionic and AngularJS, I often run into the same issue when working on projects. I manipulate data with various displays such as lists and maps, and when a user clicks on an element, they are taken to a detailed page. It would be benef ...

Postman - Gather all data points from Nested JSON in a single array

Currently, I am utilizing Postman to execute various APIs and retrieve the responses. One of these APIs provides me with the following response: { "Id": 412, "properties": { "instruction01": "R ...

The response from the $http POST request is not returning the expected

I am facing an issue where the $http POST method is not returning the expected response. The required data is located within config instead of data This is my Http POST request: for (var i = 0; i < filmService.filmData.length; i++) { filmData.pu ...

Is it possible to create an input field exclusively for tags using only CSS?

I am currently facing some limitations with a website I am managing. Unfortunately, I do not have the ability to incorporate additional libraries such as custom jQuery or JavaScript scripts. My goal is to customize an input field for tags so that when us ...

Understanding and Decoding Nested Arrays within JSON using Swift

Looking to Decode JSON using code with a structure like this: userApiService.getAllUsers { (responseDict:NSDictionary?, error:NSError?) -> Void in //Parsing responseDict for the key "result" } This is the JSON Structure we are working wi ...

Hold off on creating the directive until the page has fully loaded and is operating smoothly

I'm currently developing a one-page application, but I'm facing performance issues with certain parts that are running too slow. The sluggishness is mainly due to the fact that I'm displaying 400 complex elements in a repeater for users to s ...

Altering the properties of every item within a v-for loop's array

I'm currently exploring Vue's approach to writing JavaScript. Let's consider this situation: In the .vue template <button v-on:click="biggerFont()" class="btn btn-s btn-default" type="button" name="button">A</button> < ...

Obtaining input value when button is clicked

I am currently working on a feature where, upon clicking the Submit button, I aim to retrieve the value entered into the input field in order to update the h1 element. import React from "react"; function App() { const [headingText, setHeadingT ...

Unable to redirect to Jade page in Node.js

I recently delved into the world of node js a few days ago. When I click on a button, a function with an ajax call is triggered. function goToUser(){ $.ajax({ url:"/users/UserPage", type:'get', async:false, su ...

Looking for a jquery plugin that allows you to easily toggle between showing and hiding elements

I'm in need of some guidance on finding a slide window plugin in jQuery. My goal is to create a feature similar to Yahoo Mail, where users can hide the advertisement pane shown on the right side by clicking a button. I would greatly appreciate any as ...