generate method

String generate({
  1. required AnalyzeResult analyzeResult,
  2. Map<String, Map<String, String>> translations = const {},
  3. String? existingXcstringsPath,
})

Generates a .xcstrings JSON string.

analyzeResult provides the localizable strings from annotations. translations provides translations keyed by language code. existingXcstringsPath loads and merges with an existing .xcstrings file.

Implementation

String generate({
  required AnalyzeResult analyzeResult,
  Map<String, Map<String, String>> translations = const {},
  String? existingXcstringsPath,
}) {
  final localizableStrings = collectLocalizableStrings(analyzeResult);

  // Load existing .xcstrings for merging
  Map<String, dynamic>? existing;
  if (existingXcstringsPath != null) {
    final file = File(existingXcstringsPath);
    if (file.existsSync()) {
      existing =
          jsonDecode(file.readAsStringSync()) as Map<String, dynamic>;
    }
  }

  final existingStrings =
      (existing?['strings'] as Map<String, dynamic>?) ?? {};

  // Build strings map
  final stringsMap = <String, dynamic>{};
  final sortedKeys = localizableStrings.toList()..sort();

  for (final key in sortedKeys) {
    final localizations = <String, dynamic>{};

    // Source language entry
    localizations[sourceLanguage] = {
      'stringUnit': {
        'state': 'translated',
        'value': convertPlaceholders(key),
      },
    };

    // Add translations from YAML
    for (final entry in translations.entries) {
      final langCode = entry.key;
      final langTranslations = entry.value;
      final translation = _findTranslation(langTranslations, key);
      if (translation != null) {
        localizations[langCode] = {
          'stringUnit': {
            'state': 'translated',
            'value': convertPlaceholders(translation),
          },
        };
      }
    }

    // Merge with existing translations (preserve languages not in YAML)
    final existingEntry =
        existingStrings[key] as Map<String, dynamic>?;
    if (existingEntry != null) {
      final existingLocalizations =
          existingEntry['localizations'] as Map<String, dynamic>?;
      if (existingLocalizations != null) {
        for (final entry in existingLocalizations.entries) {
          if (!localizations.containsKey(entry.key)) {
            localizations[entry.key] = entry.value;
          }
        }
      }
    }

    stringsMap[key] = {
      'extractionState': 'manual',
      'localizations': localizations,
    };
  }

  final output = {
    'sourceLanguage': sourceLanguage,
    'strings': stringsMap,
    'version': '1.0',
  };

  final encoder = JsonEncoder.withIndent('  ');
  return '${encoder.convert(output)}\n';
}