diff --git a/slang/lib/builder/utils/file_utils.dart b/slang/lib/builder/utils/file_utils.dart index 2a3fa5cb..cbccbce5 100644 --- a/slang/lib/builder/utils/file_utils.dart +++ b/slang/lib/builder/utils/file_utils.dart @@ -44,7 +44,55 @@ class FileUtils { } return json2yaml(content, yamlStyle: YamlStyle.generic); case FileType.csv: - throw UnimplementedError('CSV is not supported yet'); + String escapeRow(String value) { + final escaped = value.replaceAll('"', '""'); + if (escaped.contains(RegExp(r'[,"]'))) { + return '"$escaped"'; + } + return escaped; + } + + Map encodeRow({ + String key = '', + required dynamic value, + }) { + if (value is Map) { + final keyPrefix = key.isEmpty ? '' : '$key.'; + return value.map((k, v) { + final map = encodeRow(key: '$keyPrefix$k', value: v); + final mapEntry = map.entries.first; + + return MapEntry(mapEntry.key, mapEntry.value); + }); + } else if (value is String) { + return {key: escapeRow(value)}; + } + return {}; + } + + final Map> columns = {}; + if (content.containsKey(INFO_KEY)) { + final info = content.remove(INFO_KEY); + columns[INFO_KEY] = {INFO_KEY: escapeRow(info.join('\\n'))}; + } + content.entries.forEach((e) { + columns[e.key] = encodeRow(value: e.value); + }); + + // get all translation keys + final translationKeys = columns.values + .map((e) => e.entries.map((e) => e.key)) + .expand((e) => e) + .toSet(); + + final headers = ['key', ...columns.keys].join(','); + final rows = translationKeys.map((key) { + final values = + columns.values.map((e) => e.containsKey(key) ? e[key] : ''); + return "$key,${values.join(',')}"; + }); + + return "$headers\n${rows.join('\n')}"; case FileType.arb: // this encoder does not append \n automatically return JsonEncoder.withIndent(' ').convert(content) + '\n';