class MultiBundleCSVFilter extends MultiBundleResourceFilter {

    @Override
    public void merge(InputStream baseStream, OutputStream outStream, Map<String, LanguageBundle> languageBundles,
            FilterOptions options) throws IOException, ResourceFilterException {
        // create key-value map for each bundle
        Map<String, Map<String, String>> kvMaps = new HashMap<String, Map<String, String>>();
        for (Entry<String, LanguageBundle> bundleEntry: languageBundles.entrySet()) {
            LanguageBundle languageBundle = bundleEntry.getValue();
            Map<String, String> kvMap = new HashMap<String, String>();
            for (ResourceString resString : languageBundle.getResourceStrings()) {
                kvMap.put(resString.getKey(), resString.getValue());
            }
            kvMaps.put(bundleEntry.getKey(), kvMap);
        }

        CSVParser parser = CSVParser.parse(baseStream, StandardCharsets.UTF_8,
                CSVFormat.RFC4180.withHeader("module", "key", "value").withSkipHeaderRecord(true));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream, StandardCharsets.UTF_8));
        CSVPrinter printer = CSVFormat.RFC4180.withHeader("module", "key", "value").print(writer);
        for (CSVRecord record : parser) {
            String module = record.get(0);
            String key = record.get(1);
            String value = record.get(2);
            Map<String, String> moduleKVMap = kvMaps.get(module);
            if (moduleKVMap != null) {
                String trValue = moduleKVMap.get(key);
                if (trValue != null) {
                    value = trValue;
                }
            }
            printer.printRecord(module, key, value);
        }
        printer.flush();
    }

}