Skip to content

Commit 565faad

Browse files
authored
Support for published package language vendor files (#177)
* Support for published package language vendor files * Added published package lang examples * Added test for php_vendor.json merge into other langs .json * Refactored merge logic to parseAll from prepareExtendedParsedLangFiles * Adjusted loader test to correspond with parseAll function's output * Isolate fixture folder in loader.test
1 parent 66a2e6d commit 565faad

File tree

5 files changed

+131
-17
lines changed

5 files changed

+131
-17
lines changed

src/loader.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ export const parseAll = (folderPath: string): ParsedLangFileInterface[] => {
5050
})
5151
}
5252

53+
// If data contains an object with folder name 'vendor'
54+
const vendorIndex = data.findIndex(({ folder }) => folder === 'vendor');
55+
56+
if (vendorIndex !== -1) {
57+
const vendorTranslations = data[vendorIndex].translations;
58+
data.splice(vendorIndex, 1);
59+
60+
data.forEach(langFile =>
61+
langFile.translations = mergeVendorTranslations(langFile.folder, langFile.translations, vendorTranslations));
62+
}
63+
5364
return data
5465
.filter(({ translations }) => {
5566
return Object.keys(translations).length > 0
@@ -62,6 +73,20 @@ export const parseAll = (folderPath: string): ParsedLangFileInterface[] => {
6273
})
6374
}
6475

76+
function mergeVendorTranslations(folder: string, translations: any, vendorTranslations: any) {
77+
// Filter the translations from the vendor file that match the current folder
78+
const langTranslationsFromVendor = Object
79+
.entries(vendorTranslations)
80+
.filter(([key]) => key.includes(`.${folder}.`))
81+
.reduce((acc, [key, value]) => ({
82+
...acc,
83+
[key.replace(`.${folder}.`, '::')]: value,
84+
}), {});
85+
86+
// Merge the vendor translations that matched the folder with the current translations
87+
return { ...translations, ...langTranslationsFromVendor };
88+
}
89+
6590
export const parse = (content: string) => {
6691
const arr = new Engine({}).parseCode(content, 'lang').children.filter((child) => child.kind === 'return')[0] as any
6792

@@ -155,8 +180,8 @@ export const readThroughDir = (dir) => {
155180
return data
156181
}
157182

158-
export const prepareExtendedParsedLangFiles = (langPaths: string[]) =>
159-
langPaths.reduce((acc, langPath) => [...acc, ...parseAll(langPath)], new Array<ParsedLangFileInterface>())
183+
export const prepareExtendedParsedLangFiles = (langPaths: string[]): ParsedLangFileInterface[] =>
184+
langPaths.flatMap(langPath => parseAll(langPath));
160185

161186
export const generateFiles = (langPath: string, data: ParsedLangFileInterface[]): ParsedLangFileInterface[] => {
162187
data = mergeData(data)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
return [
4+
'welcome' => 'Welcome to the example package.',
5+
'success' => 'The package did the task successfully.',
6+
'foo' => [
7+
'level1' => [
8+
'level2' => 'package'
9+
]
10+
],
11+
'arr' => ['foo', 'bar'],
12+
'multiline' => 'Lorem ' .
13+
'ipsum ' .
14+
'dolor ' .
15+
'sit ' .
16+
'amet.',
17+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
return [
4+
'welcome' => 'Bem-vindo ao exemplo do pacote.',
5+
'success' => 'O pacote executou a tarefa com sucesso.',
6+
'foo' => [
7+
'level1' => [
8+
'level2' => 'pacote'
9+
]
10+
],
11+
'arr' => ['foo', 'bar'],
12+
'multiline' => 'Lorem ' .
13+
'ipsum ' .
14+
'dolor ' .
15+
'sit ' .
16+
'amet.',
17+
];

test/folderIsolationUtil.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
4+
export function isolateFolder(folderToIsolate: string, testName: string) {
5+
const isolatedFolder = folderToIsolate + '_isolated_' + testName;
6+
copyDirSync(folderToIsolate, isolatedFolder);
7+
8+
return isolatedFolder;
9+
}
10+
11+
export function removeIsolatedFolder(isolatedFolder: string) {
12+
fs.rmSync(isolatedFolder, { recursive: true, force: true })
13+
}
14+
15+
function copyDirSync(source: string, destination: string) {
16+
const exists = fs.existsSync(source);
17+
const stats = exists && fs.statSync(source);
18+
const isDirectory = exists && stats.isDirectory();
19+
if (isDirectory) {
20+
fs.mkdirSync(destination);
21+
fs.readdirSync(source).forEach(childItemName => {
22+
copyDirSync(path.join(source, childItemName), path.join(destination, childItemName));
23+
});
24+
} else {
25+
fs.copyFileSync(source, destination);
26+
}
27+
}

test/loader.test.ts

+43-15
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import fs from 'fs';
22
import { generateFiles, parseAll, parse, hasPhpTranslations, reset, prepareExtendedParsedLangFiles } from '../src/loader';
3+
import { isolateFolder, removeIsolatedFolder } from './folderIsolationUtil'
34

4-
beforeEach(() => reset(__dirname + '/fixtures/lang/'));
5+
const isolatedFixtures = isolateFolder(__dirname + '/fixtures', 'loader');
6+
afterAll(() => removeIsolatedFolder(isolatedFixtures));
7+
8+
beforeEach(() => reset(isolatedFixtures + '/lang/'));
59

610
it('creates a file for each lang', () => {
7-
const langPath = __dirname + '/fixtures/lang/';
11+
const langPath = isolatedFixtures + '/lang/';
812
const files = generateFiles(langPath, parseAll(langPath));
913

1014
expect(files.length).toBe(3);
@@ -22,8 +26,32 @@ it('creates a file for each lang', () => {
2226
expect(langPt['auth.foo.level1.level2']).toBe('barpt');
2327
});
2428

29+
it('merges published package translations into each lang .json', () => {
30+
const langPath = isolatedFixtures + '/lang/';
31+
const files = generateFiles(langPath, parseAll(langPath));
32+
33+
expect(files.length).toBe(3);
34+
expect(files[0].name).toBe('php_en.json');
35+
expect(files[1].name).toBe('php_fr.json');
36+
expect(files[2].name).toBe('php_pt.json');
37+
38+
const langEn = JSON.parse(fs.readFileSync(langPath + files[0].name).toString());
39+
expect(langEn['package-example::messages.welcome']).toBe('Welcome to the example package.');
40+
expect(langEn['package-example::messages.foo.level1.level2']).toBe('package');
41+
expect(langEn['package-example::messages.multiline']).toBe('Lorem ipsum dolor sit amet.');
42+
43+
const langFr = JSON.parse(fs.readFileSync(langPath + files[1].name).toString());
44+
expect(langFr['package-example::messages.welcome']).toBeUndefined();
45+
expect(langFr['package-example::messages.foo.level1.level2']).toBeUndefined();
46+
expect(langFr['package-example::messages.multiline']).toBeUndefined();
47+
48+
const langPt = JSON.parse(fs.readFileSync(langPath + files[2].name).toString());
49+
expect(langPt['package-example::messages.welcome']).toBe('Bem-vindo ao exemplo do pacote.');
50+
expect(langPt['package-example::messages.foo.level1.level2']).toBe('pacote');
51+
});
52+
2553
it('includes .php lang file in subdirectory in .json', () => {
26-
const langPath = __dirname + '/fixtures/lang/';
54+
const langPath = isolatedFixtures + '/lang/';
2755
const files = generateFiles(langPath, parseAll(langPath));
2856
const langEn = JSON.parse(fs.readFileSync(langPath + files[0].name).toString());
2957

@@ -33,7 +61,7 @@ it('includes .php lang file in subdirectory in .json', () => {
3361
});
3462

3563
it('includes .php lang file in nested subdirectory in .json', () => {
36-
const langPath = __dirname + '/fixtures/lang/';
64+
const langPath = isolatedFixtures + '/lang/';
3765
const files = generateFiles(langPath, parseAll(langPath));
3866
const langEn = JSON.parse(fs.readFileSync(langPath + files[0].name).toString())
3967

@@ -42,9 +70,9 @@ it('includes .php lang file in nested subdirectory in .json', () => {
4270
})
4371

4472
it('inclues additional lang paths to load from', () => {
45-
const langPath = __dirname + '/fixtures/lang/';
73+
const langPath = isolatedFixtures + '/lang/';
4674
const additionalLangPaths = [
47-
__dirname + '/fixtures/locales/'
75+
isolatedFixtures + '/locales/'
4876
];
4977

5078
const langPaths = prepareExtendedParsedLangFiles([
@@ -60,9 +88,9 @@ it('inclues additional lang paths to load from', () => {
6088
});
6189

6290
it('overwrites translations from additional lang paths', () => {
63-
const langPath = __dirname + '/fixtures/lang/';
91+
const langPath = isolatedFixtures + '/lang/';
6492
const additionalLangPaths = [
65-
__dirname + '/fixtures/locales/'
93+
isolatedFixtures + '/locales/'
6694
];
6795

6896
const langPaths = prepareExtendedParsedLangFiles([
@@ -79,33 +107,33 @@ it('overwrites translations from additional lang paths', () => {
79107
});
80108

81109
it('transforms .php lang to .json', () => {
82-
const lang = parse(fs.readFileSync(__dirname + '/fixtures/lang/en/auth.php').toString());
110+
const lang = parse(fs.readFileSync(isolatedFixtures + '/lang/en/auth.php').toString());
83111

84112
expect(lang['failed']).toBe('These credentials do not match our records.');
85113
});
86114

87115
it('transform nested .php lang files to .json', () => {
88-
const langPt = parse(fs.readFileSync(__dirname + '/fixtures/lang/pt/auth.php').toString());
116+
const langPt = parse(fs.readFileSync(isolatedFixtures + '/lang/pt/auth.php').toString());
89117
expect(langPt['foo.level1.level2']).toBe('barpt');
90118

91-
const langEn = parse(fs.readFileSync(__dirname + '/fixtures/lang/en/auth.php').toString());
119+
const langEn = parse(fs.readFileSync(isolatedFixtures + '/lang/en/auth.php').toString());
92120
expect(langEn['foo.level1.level2']).toBe('baren');
93121
});
94122

95123
it('transforms simple index array to .json', () => {
96-
const lang = parse(fs.readFileSync(__dirname + '/fixtures/lang/en/auth.php').toString());
124+
const lang = parse(fs.readFileSync(isolatedFixtures + '/lang/en/auth.php').toString());
97125
expect(lang['arr.0']).toBe('foo');
98126
expect(lang['arr.1']).toBe('bar');
99127
});
100128

101129
it('ignores empty `array` or `null` translations', () => {
102-
const lang = parse(fs.readFileSync(__dirname + '/fixtures/lang/en/ignore.php').toString());
130+
const lang = parse(fs.readFileSync(isolatedFixtures + '/lang/en/ignore.php').toString());
103131

104132
expect(lang['empty_array']).toBe(undefined);
105133
expect(lang['null']).toBe(undefined);
106134
});
107135

108136
it('checks if there is .php translations', () => {
109-
expect(hasPhpTranslations(__dirname + '/fixtures/lang/')).toBe(true);
110-
expect(hasPhpTranslations(__dirname + '/fixtures/wronglangfolder/')).toBe(false);
137+
expect(hasPhpTranslations(isolatedFixtures + '/lang/')).toBe(true);
138+
expect(hasPhpTranslations(isolatedFixtures + '/wronglangfolder/')).toBe(false);
111139
});

0 commit comments

Comments
 (0)