Skip to content
GitHub Copilot실무 경험

Removing Unused Translation Keys with GitHub Copilot

1 min readWorkMoved from my Tistory blogRead the Korean original

When managing multiple languages (i18n) in a project, unused translation keys are left behind every time a feature is changed or removed.

Here is how I cleaned up the unneeded translation keys that had been piling up in the WiniLogis project.

❗️ The problem

WiniLogis manages its translations in a Google Cloud Sheet. In the development environment, a script converts the translation data written in the Sheet and saves it as public/locales/{language}/translation.json.

text
public/
 └─ locales/
     ├─ en/translation.json
     ├─ es/translation.json
     └─ ru/translation.json

But as the project grew and features were added and removed, unused translation keys gradually started to accumulate. As a result, translations that were not actually used kept remaining in the Google Sheet.

🔍 Analysis

The i18n system itself has no feature that automatically tells you “whether this key is actually in use.” That means a developer would have to search through all the project code by hand, which seemed very hard to do in practice.

So I decided to solve the problem with this approach:

  1. Extract all translation keys from the translation.json file
  2. Go through every code file in the src folder and check whether each key is actually used
  3. Print only the unused keys separately and clean them up

✅ Solution

To optimize this process, I wrote a script that finds unused i18n keys with help from GitHub Copilot.

js
// Script: find unused i18n keys
import fs from 'fs';
import path from 'path';

const LOCALES_DIR = './public/locales/en/translation.json';

// Read the JSON file
const translationData = JSON.parse(fs.readFileSync(LOCALES_DIR, 'utf8'));

// Extract all keys (nested keys are joined with a dot (.))
const getAllKeys = (obj, prefix = '') => {
  let keys = [];

  for (const key in obj) {
    const fullKey = prefix ? `${prefix}.${key}` : key;

    if (typeof obj[key] === 'object' && obj[key] !== null) {
      keys = keys.concat(getAllKeys(obj[key], fullKey));
    } else {
      keys.push(fullKey);
    }
  }

  return keys;
};

// Recursively find all files with the given extensions in the src folder (excluding node_modules)
const getFilesRecursive = (dir, extensions) => {
  let results = [];

  const list = fs.readdirSync(dir, { withFileTypes: true });

  for (const file of list) {
    const fullPath = path.join(dir, file.name);

    if (file.isDirectory()) {
      if (file.name === 'node_modules') continue;
      results = results.concat(getFilesRecursive(fullPath, extensions));
    } else {
      if (extensions.includes(path.extname(file.name))) {
        results.push(fullPath);
      }
    }
  }

  return results;
};

// Check whether a given key is used in the project files
const checkKeyUsage = (keyToCheck) => {
  const files = getFilesRecursive('src', ['.ts', '.tsx', '.js', '.jsx']);

  for (const file of files) {
    try {
      const content = fs.readFileSync(file, 'utf8');

      // Check key usage with several patterns
      const patterns = [
        `'${keyToCheck}'`,
        `"${keyToCheck}"`,
        `\`${keyToCheck}\``,
        `t('${keyToCheck}')`,
        `t("${keyToCheck}")`,
      ];

      if (patterns.some((pattern) => content.includes(pattern))) {
        return true; // In use
      }
    } catch (error) {
      console.error(`Error reading file ${file}:`, error.message);
    }
  }

  return false; // Not used
};

// Main function: print unused keys
const findUnusedKeys = () => {
  const allKeys = getAllKeys(translationData);
  const unusedKeys = [];

  for (const key of allKeys) {
    const isUsed = checkKeyUsage(key);

    if (!isUsed) {
      unusedKeys.push(key);
    }
  }

  console.log('Unused i18n Keys:', unusedKeys.length);
  unusedKeys.forEach((key) => console.log(key));
};

// Run
findUnusedKeys();

💡 Looking back

Running the script found 69 unused translation keys in total.

I removed the same keys from the Google Cloud Sheet as well, then ran the automatic sync script again so the translation.json files stay cleaned up.

Thanks to GitHub Copilot, I could write the repetitive search logic quickly. From now on, I plan to run this script regularly to keep the translation data clean at all times.

  • #GitHub Copilot
  • #i18n
  • #Translation Key Cleanup

More in Work