diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
commit | 43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch) | |
tree | dceebdc68925362117480a5d672bcff122fb625b /spec/frontend/scripts | |
parent | 20c84b99005abd1c82101dfeff264ac50d2df211 (diff) |
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'spec/frontend/scripts')
3 files changed, 278 insertions, 0 deletions
diff --git a/spec/frontend/scripts/frontend/__fixtures__/locale/de/converted.json b/spec/frontend/scripts/frontend/__fixtures__/locale/de/converted.json new file mode 100644 index 00000000000..570980b1c27 --- /dev/null +++ b/spec/frontend/scripts/frontend/__fixtures__/locale/de/converted.json @@ -0,0 +1,21 @@ +{ + "domain": "app", + "locale_data": { + "app": { + "": { + "domain": "app", + "lang": "de" + }, + " %{start} to %{end}": [ + " %{start} bis %{end}" + ], + "%d Alert:": [ + "%d Warnung:", + "%d Warnungen:" + ], + "Example": [ + "" + ] + } + } +} diff --git a/spec/frontend/scripts/frontend/__fixtures__/locale/de/gitlab.po b/spec/frontend/scripts/frontend/__fixtures__/locale/de/gitlab.po new file mode 100644 index 00000000000..fe80cb72c29 --- /dev/null +++ b/spec/frontend/scripts/frontend/__fixtures__/locale/de/gitlab.po @@ -0,0 +1,13 @@ +# Simple translated string +msgid " %{start} to %{end}" +msgstr " %{start} bis %{end}" + +# Simple translated, pluralized string +msgid "%d Alert:" +msgid_plural "%d Alerts:" +msgstr[0] "%d Warnung:" +msgstr[1] "%d Warnungen:" + +# Simple string without translation +msgid "Example" +msgstr "" diff --git a/spec/frontend/scripts/frontend/po_to_json_spec.js b/spec/frontend/scripts/frontend/po_to_json_spec.js new file mode 100644 index 00000000000..858e3c9d3c7 --- /dev/null +++ b/spec/frontend/scripts/frontend/po_to_json_spec.js @@ -0,0 +1,244 @@ +import { join } from 'path'; +import { tmpdir } from 'os'; +import { readFile, rm, mkdtemp, stat } from 'fs/promises'; +import { + convertPoToJed, + convertPoFileForLocale, + main, +} from '../../../../scripts/frontend/po_to_json'; + +describe('PoToJson', () => { + const LOCALE = 'de'; + const LOCALE_DIR = join(__dirname, '__fixtures__/locale'); + const PO_FILE = join(LOCALE_DIR, LOCALE, 'gitlab.po'); + const CONVERTED_FILE = join(LOCALE_DIR, LOCALE, 'converted.json'); + let DE_CONVERTED = null; + + beforeAll(async () => { + DE_CONVERTED = Object.freeze(JSON.parse(await readFile(CONVERTED_FILE, 'utf-8'))); + }); + + describe('tests writing to the file system', () => { + let resultDir = null; + + afterEach(async () => { + if (resultDir) { + await rm(resultDir, { recursive: true, force: true }); + } + }); + + beforeEach(async () => { + resultDir = await mkdtemp(join(tmpdir(), 'locale-test')); + }); + + describe('#main', () => { + it('throws without arguments', () => { + return expect(main()).rejects.toThrow(/doesn't seem to be a folder/); + }); + + it('throws if outputDir does not exist', () => { + return expect( + main({ + localeRoot: LOCALE_DIR, + outputDir: 'i-do-not-exist', + }), + ).rejects.toThrow(/doesn't seem to be a folder/); + }); + + it('throws if localeRoot does not exist', () => { + return expect( + main({ + localeRoot: 'i-do-not-exist', + outputDir: resultDir, + }), + ).rejects.toThrow(/doesn't seem to be a folder/); + }); + + it('converts folder of po files to app.js files', async () => { + expect((await stat(resultDir)).isDirectory()).toBe(true); + await main({ localeRoot: LOCALE_DIR, outputDir: resultDir }); + + const resultFile = join(resultDir, LOCALE, 'app.js'); + expect((await stat(resultFile)).isFile()).toBe(true); + + window.translations = null; + await import(resultFile); + expect(window.translations).toEqual(DE_CONVERTED); + }); + }); + + describe('#convertPoFileForLocale', () => { + it('converts simple PO to app.js, which exposes translations on the window', async () => { + await convertPoFileForLocale({ locale: 'de', localeFile: PO_FILE, resultDir }); + + const resultFile = join(resultDir, 'app.js'); + expect((await stat(resultFile)).isFile()).toBe(true); + + window.translations = null; + await import(resultFile); + expect(window.translations).toEqual(DE_CONVERTED); + }); + }); + }); + + describe('#convertPoToJed', () => { + it('converts simple PO to JED compatible JSON', async () => { + const poContent = await readFile(PO_FILE, 'utf-8'); + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual(DE_CONVERTED); + }); + + it('returns null for empty string', () => { + const poContent = ''; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual(null); + }); + + describe('PO File headers', () => { + it('parses headers properly', () => { + const poContent = ` +msgid "" +msgstr "" +"Project-Id-Version: gitlab-ee\\n" +"Report-Msgid-Bugs-To: \\n" +"X-Crowdin-Project: gitlab-ee\\n" +`; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual({ + domain: 'app', + locale_data: { + app: { + '': { + 'Project-Id-Version': 'gitlab-ee', + 'Report-Msgid-Bugs-To': '', + 'X-Crowdin-Project': 'gitlab-ee', + domain: 'app', + lang: LOCALE, + }, + }, + }, + }); + }); + + // JED needs that property, hopefully we could get + // rid of this in a future iteration + it("exposes 'Plural-Forms' as 'plural_forms' for `jed`", () => { + const poContent = ` +msgid "" +msgstr "" +"Plural-Forms: nplurals=2; plural=(n != 1);\\n" +`; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual({ + domain: 'app', + locale_data: { + app: { + '': { + 'Plural-Forms': 'nplurals=2; plural=(n != 1);', + plural_forms: 'nplurals=2; plural=(n != 1);', + domain: 'app', + lang: LOCALE, + }, + }, + }, + }); + }); + + it('removes POT-Creation-Date', () => { + const poContent = ` +msgid "" +msgstr "" +"Plural-Forms: nplurals=2; plural=(n != 1);\\n" +`; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual({ + domain: 'app', + locale_data: { + app: { + '': { + 'Plural-Forms': 'nplurals=2; plural=(n != 1);', + plural_forms: 'nplurals=2; plural=(n != 1);', + domain: 'app', + lang: LOCALE, + }, + }, + }, + }); + }); + }); + + describe('escaping', () => { + it('escapes quotes in msgid and translation', () => { + const poContent = ` +# Escaped quotes in msgid and msgstr +msgid "Changes the title to \\"%{title_param}\\"." +msgstr "Ändert den Titel in \\"%{title_param}\\"." +`; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual({ + domain: 'app', + locale_data: { + app: { + '': { + domain: 'app', + lang: LOCALE, + }, + 'Changes the title to \\"%{title_param}\\".': [ + 'Ändert den Titel in \\"%{title_param}\\".', + ], + }, + }, + }); + }); + + it('escapes backslashes in msgid and translation', () => { + const poContent = ` +# Escaped backslashes in msgid and msgstr +msgid "Example: ssh\\\\:\\\\/\\\\/" +msgstr "Beispiel: ssh\\\\:\\\\/\\\\/" +`; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual({ + domain: 'app', + locale_data: { + app: { + '': { + domain: 'app', + lang: LOCALE, + }, + 'Example: ssh\\\\:\\\\/\\\\/': ['Beispiel: ssh\\\\:\\\\/\\\\/'], + }, + }, + }); + }); + + // This is potentially faulty behavior but demands further investigation + // See also the escapeMsgstr method + it('escapes \\n and \\t in translation', () => { + const poContent = ` +# Escaped \\n +msgid "Outdent line" +msgstr "Désindenter la ligne\\n" + +# Escaped \\t +msgid "Headers" +msgstr "Cabeçalhos\\t" +`; + + expect(convertPoToJed(poContent, LOCALE).jed).toEqual({ + domain: 'app', + locale_data: { + app: { + '': { + domain: 'app', + lang: LOCALE, + }, + Headers: ['Cabeçalhos\\t'], + 'Outdent line': ['Désindenter la ligne\\n'], + }, + }, + }); + }); + }); + }); +}); |