summaryrefslogtreecommitdiffhomepage
path: root/gui/scripts/verify-format-specifiers.ts
blob: 2d161548d5cd2bef8c9e7b7b8f6ea3cef671b8ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import fs from 'fs';
import { GetTextTranslation, po } from 'gettext-parser';
import path from 'path';

const LOCALES_DIR = path.join('..', 'locales');

function getLocales(): string[] {
  const localesContent = fs.readdirSync(LOCALES_DIR);
  const localeDirectories = localesContent.filter((item) =>
    fs.statSync(path.join(LOCALES_DIR, item)).isDirectory(),
  );
  return localeDirectories;
}

function parseTranslationsForLocale(locale: string): GetTextTranslation[] {
  const poFileContents = fs.readFileSync(path.join(LOCALES_DIR, locale, 'messages.po'));
  const contexts = po.parse(poFileContents).translations;

  const translations = Object.values(contexts)
    .flatMap((context) => Object.values(context))
    .filter((translation) => translation.msgid !== '');

  return translations;
}

function getFormatSpecifiers(text: string): string[] {
  // Matches both %(name)s and %s.
  return text.match(/%(\(.*?\))?[a-z]/g) ?? [];
}

function formatSpecifiersEquals(source: string[], translation: string[]): boolean {
  const sortedTranslation = translation.sort();
  return (
    source.length === translation.length &&
    source.sort().every((value, index) => value === sortedTranslation[index])
  );
}

function checkTranslationImpl(msgid: string, msgstr: string): boolean {
  const sourceFormatSpecifiers = getFormatSpecifiers(msgid);
  const translationFormatSpecifiers = getFormatSpecifiers(msgstr);
  return formatSpecifiersEquals(sourceFormatSpecifiers, translationFormatSpecifiers);
}

function checkTranslation(translation: GetTextTranslation): boolean {
  return translation.msgstr
    .map((msgstr) => {
      // Make sure that the translation matches either the singular or plural.
      const equal =
        checkTranslationImpl(translation.msgid, msgstr) ||
        (translation.msgid_plural && checkTranslationImpl(translation.msgid_plural, msgstr));

      if (!equal) {
        console.error(`Error in "${translation.msgid}", "${msgstr}"`);
      }

      return equal;
    })
    .every((result) => result);
}

const isCorrect = getLocales()
  .map(parseTranslationsForLocale)
  // Map first to output all errors
  .map((translations) => translations.every(checkTranslation))
  .every((result) => result);

if (isCorrect) {
  console.log('Looks good!');
} else {
  console.error('See above errors');
  process.exit(1);
}