run method

  1. @override
FutureOr? run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
FutureOr<dynamic>? run() async {
  final infoPlist = pathFinder.getInfoPlist();
  if (infoPlist == null) {
    Logger.error('Could not find Info.plist file in the iOS project.');
    return;
  }

  final editor = PListEditor(infoPlist.readAsStringSync());
  final usageDescription = editor.getUsageDescriptions();
  if (usageDescription.isEmpty) {
    Logger.info(
      'No usage description keys found in Info.plist. Nothing to localize.',
    );
    return;
  }

  final xcstringsFile = File(
    p.join(p.dirname(infoPlist.path), xcstringsFileName),
  );
  final projectDir = p.join(
    p.dirname(p.dirname(infoPlist.path)),
    'Runner.xcodeproj',
  );

  if (!Directory(projectDir).existsSync()) {
    Logger.error('Could not find $projectDir file in the iOS project.');
    return;
  }

  final relativeProjectPath = p.relative(
    projectDir,
    from: Directory.current.path,
  );
  final relativeXcstringsPath = p.relative(
    xcstringsFile.path,
    from: Directory.current.path,
  );

  final (source, knownRegions) = await getKnownRegions(relativeProjectPath);

  final langCodesToUse = <String>{};
  for (final code in [...?argResults?.rest]) {
    if (isValidLanguageCode(code)) {
      langCodesToUse.add(code);
    } else {
      Logger.error('Invalid language code provided: $code');
      return;
    }
  }

  final xcStringsJson = loadXcStringsFile(xcstringsFile, source);

  if (langCodesToUse.isEmpty) {
    langCodesToUse.addAll(knownRegions);
    langCodesToUse.addAll(getExistingLangCodes(xcStringsJson));
  }

  // always ensure source language is included
  langCodesToUse.add(source);

  for (final entry in usageDescription) {
    xcStringsJson['strings'][entry.key] ??= {
      'localizations': <String, dynamic>{},
    };
    final localizations =
        xcStringsJson['strings'][entry.key]?['localizations'];
    if (localizations is Map<String, dynamic>) {
      for (final langCode in langCodesToUse) {
        localizations.putIfAbsent(langCode, () {
          return {
            'stringUnit': {
              'state': langCode == source ? 'translated' : 'new',
              'value': entry.description,
            },
          };
        });
      }
    }
  }

  if (await isXcodeprojInstalled() != true) {
    // prompt to try install
    final installIt = prompt(
      'xcodeproj gem is not installed. try to install it now? [y/N]: ',
      defaultValue: 'y',
    );
    if (installIt.toLowerCase() == 'y') {
      await installXcodeproj();
    } else {
      Logger.error(
        'xcodeproj gem is required to add localization references. Please install it and try again.',
      );
      return;
    }
    return;
  }

  try {
    final formatter = JsonEncoder.withIndent('  ');
    await xcstringsFile.writeAsString(formatter.convert(xcStringsJson));
    await addXcodeReference(
      projectPath: relativeProjectPath,
      xcstringsPath: relativeXcstringsPath,
    );
    Logger.success(
      'Added localization keys for the following languages: (${langCodesToUse.join(', ')})',
    );
    Logger.success(
      'Make sure you add your translations in\n${xcstringsFile.path}',
    );
  } catch (e) {
    Logger.error('Failed to write to ${xcstringsFile.path}: $e');
  }
}