How to detect unused string keys in Flutter ARB files?

Keeping code and translations tidy is essential for ensuring smooth development and localization processes. Unused translations in localization files can cause problems such as cluttered files, slower translation times, increased maintenance costs, and longer app loading times. Therefore, it's important to regularly review and remove any unused translations to avoid these problems.

One simple way to achieve this is by creating a command line program that lists all unused string keys in your Flutter project. An example code that can be used as a starting point in creating such a program is provided below.

import 'dart:convert';
import 'dart:io';

import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';

void main() {
  final root = Directory.current.path;
  final rootPosix = root.replaceAll("\\", "/");

  final stringKeys = getStringKeys(rootPosix);
  final dartFiles = getDartFiles(rootPosix);
  final unusedStringKeys = findUnusedStringKeys(stringKeys, dartFiles);
  for (final stringKey in unusedStringKeys) {
    print(stringKey);
  }
}

Set<String> getStringKeys(String path) {
  final arbFilesGlob = Glob('$path/**.arb');

  final arbFiles = <String>[];
  for (var entity in arbFilesGlob.listSync(followLinks: false)) {
    arbFiles.add(entity.path);
  }

  final stringKeys = <String>{};
  for (final file in arbFiles) {
    final content = File(file).readAsStringSync();
    final map = jsonDecode(content) as Map<String, dynamic>;
    for (final entry in map.entries) {
      if (!entry.key.startsWith('@')) {
        stringKeys.add(entry.key);
      }
    }
  }

  return stringKeys;
}

List<String> getDartFiles(String path) {
  final dartFilesGlob = Glob('$path/lib/**.dart');
  final dartFilesExcludeGlob = Glob('$path/lib/generated/**.dart');
  
  final dartFilesExclude = <String>[];
  for (var entity in dartFilesExcludeGlob.listSync(followLinks: false)) {
    dartFilesExclude.add(entity.path);
  }

  final dartFiles = <String>[];
  for (var entity in dartFilesGlob.listSync(followLinks: false)) {
    if (!dartFilesExclude.contains(entity.path)) {
      dartFiles.add(entity.path);
    }
  }

  return dartFiles;
}

Set<String> findUnusedStringKeys(Set<String> stringKeys, List<String> files) {
  final unusedStringKeys = stringKeys.toSet();

  for (final file in files) {
    final content = File(file).readAsStringSync();
    for (final stringKey in stringKeys) {
      if (content.contains(stringKey)) {
        unusedStringKeys.remove(stringKey);
      }
    }
  }
  
  return unusedStringKeys;
}

Flutter internationalization tutorial:

Copyrights 2024 © Localizely