Skip to content

Commit f50586a

Browse files
authored
feat: Add support for laravel framework lang path (#108)
* feat: Add support for default `lang` path * fix: Check if is a folder on `parseAll` * fix: Check if is a folder on `parseAll` * fix: Add output file to the lang path itself * fix: Add output file to the lang path itself * fix: Remove dir if is empty * fix: Remove dir if is empty * fix: merge files * fix: interfaces * fix: interfaces * fix: interfaces * fix: interfaces * fix: interfaces * fix: interfaces * fix: interfaces * refactor * refactor * refactor * refactor * refactor * refactor
1 parent 7942b22 commit f50586a

File tree

6 files changed

+93
-27
lines changed

6 files changed

+93
-27
lines changed

src/interfaces/parsed-lang-file.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* The Interface that is responsible for a parsed lang file.
3+
*/
4+
export interface ParsedLangFileInterface {
5+
name: string
6+
translations: { [key: string]: string }
7+
}

src/loader.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'fs'
22
import path from 'path'
33
import { Engine } from 'php-parser'
4+
import { ParsedLangFileInterface } from './interfaces/parsed-lang-file'
45

56
export const hasPhpTranslations = (folderPath: string): boolean => {
67
folderPath = folderPath.replace(/[\\/]$/, '') + path.sep
@@ -25,9 +26,13 @@ export const hasPhpTranslations = (folderPath: string): boolean => {
2526
return false
2627
}
2728

28-
export const parseAll = (folderPath: string): { name: string; path: string }[] => {
29+
export const parseAll = (folderPath: string): ParsedLangFileInterface[] => {
2930
folderPath = folderPath.replace(/[\\/]$/, '') + path.sep
3031

32+
if (!fs.existsSync(folderPath)) {
33+
return []
34+
}
35+
3136
const folders = fs
3237
.readdirSync(folderPath)
3338
.filter((file) => fs.statSync(folderPath + path.sep + file).isDirectory())
@@ -50,11 +55,10 @@ export const parseAll = (folderPath: string): { name: string; path: string }[] =
5055
return Object.keys(translations).length > 0
5156
})
5257
.map(({ folder, translations }) => {
53-
const name = `php_${folder}.json`
54-
const path = folderPath + name
55-
56-
fs.writeFileSync(path, JSON.stringify(translations))
57-
return { name, path }
58+
return {
59+
name: `php_${folder}.json`,
60+
translations
61+
}
5862
})
5963
}
6064

@@ -142,3 +146,36 @@ export const readThroughDir = (dir) => {
142146

143147
return data
144148
}
149+
150+
export const generateFiles = (langPath: string, data: ParsedLangFileInterface[]): ParsedLangFileInterface[] => {
151+
data = mergeData(data)
152+
153+
if (!fs.existsSync(langPath)) {
154+
fs.mkdirSync(langPath)
155+
}
156+
157+
data.forEach(({ name, translations }) => {
158+
fs.writeFileSync(langPath + name, JSON.stringify(translations))
159+
})
160+
161+
return data
162+
}
163+
164+
function mergeData(data: ParsedLangFileInterface[]): ParsedLangFileInterface[] {
165+
const obj = {}
166+
167+
data.forEach(({ name, translations }) => {
168+
if (!obj[name]) {
169+
obj[name] = {}
170+
}
171+
172+
obj[name] = { ...obj[name], ...translations }
173+
})
174+
175+
const arr = []
176+
Object.entries(obj).forEach(([name, translations]) => {
177+
arr.push({ name, translations })
178+
})
179+
180+
return arr
181+
}

src/mix.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import mix from 'laravel-mix'
55
import { Component } from 'laravel-mix/src/components/Component'
66
import { EnvironmentPlugin, Configuration } from 'webpack'
77

8-
import { parseAll, hasPhpTranslations } from './loader'
8+
import { generateFiles, parseAll, hasPhpTranslations } from './loader'
99

1010
class BeforeBuildPlugin {
1111
callback: Function
@@ -23,10 +23,15 @@ mix.extend(
2323
'i18n',
2424
class extends Component {
2525
langPath: string
26+
frameworkLangPath: string
2627
context: any
2728

2829
register(langPath: string = 'lang'): void {
2930
this.langPath = this.context.paths.rootPath + path.sep + langPath
31+
this.frameworkLangPath =
32+
this.context.paths.rootPath +
33+
path.sep +
34+
'vendor/laravel/framework/src/Illuminate/Translation/lang/'.replace('/', path.sep)
3035
}
3136

3237
webpackConfig(config: Configuration): void {
@@ -46,16 +51,20 @@ mix.extend(
4651

4752
config.plugins.push(
4853
new BeforeBuildPlugin(() => {
49-
files = parseAll(this.langPath)
54+
files = generateFiles(this.langPath, [...parseAll(this.frameworkLangPath), ...parseAll(this.langPath)])
5055
})
5156
)
5257

5358
this.context.listen('build', () => {
5459
files.forEach((file) => {
55-
if (fs.existsSync(file.path)) {
56-
fs.unlinkSync(file.path)
60+
if (fs.existsSync(this.langPath + file.name)) {
61+
fs.unlinkSync(this.langPath + file.name)
5762
}
5863
})
64+
65+
if (fs.existsSync(this.langPath) && fs.readdirSync(this.langPath).length < 1) {
66+
fs.rmdirSync(this.langPath)
67+
}
5968
})
6069
}
6170
}

src/vite.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
1-
import { existsSync, unlinkSync } from 'fs'
2-
import { parseAll, hasPhpTranslations } from './loader'
1+
import path from 'path'
2+
import { existsSync, writeFileSync, unlinkSync, readdirSync, rmdirSync } from 'fs'
3+
import { parseAll, hasPhpTranslations, generateFiles } from './loader'
4+
import { ParsedLangFileInterface } from './interfaces/parsed-lang-file'
35

46
export default function i18n(langPath: string = 'lang') {
5-
let files: { name: string; path: string }[] = []
7+
langPath = langPath.replace(/[\\/]$/, '') + path.sep
8+
9+
const frameworkLangPath = 'vendor/laravel/framework/src/Illuminate/Translation/lang/'.replace('/', path.sep)
10+
let files: ParsedLangFileInterface[] = []
611
let exitHandlersBound: boolean = false
712

813
const clean = () => {
9-
files.forEach((file) => unlinkSync(file.path))
14+
files.forEach((file) => unlinkSync(langPath + file.name))
1015

1116
files = []
17+
18+
if (existsSync(langPath) && readdirSync(langPath).length < 1) {
19+
rmdirSync(langPath)
20+
}
1221
}
1322

1423
return {
1524
name: 'i18n',
1625
enforce: 'post',
1726
config(config) {
18-
if (!hasPhpTranslations(langPath)) {
27+
if (!hasPhpTranslations(frameworkLangPath) && !hasPhpTranslations(langPath)) {
1928
return
2029
}
2130

22-
files = parseAll(langPath)
31+
files = generateFiles(langPath, [...parseAll(frameworkLangPath), ...parseAll(langPath)])
2332

2433
/** @ts-ignore */
2534
process.env.VITE_LARAVEL_VUE_I18N_HAS_PHP = true
@@ -33,7 +42,7 @@ export default function i18n(langPath: string = 'lang') {
3342
buildEnd: clean,
3443
handleHotUpdate(ctx) {
3544
if (/lang\/.*\.php$/.test(ctx.file)) {
36-
files = parseAll(langPath)
45+
files = generateFiles(langPath, [...parseAll(frameworkLangPath), ...parseAll(langPath)])
3746
}
3847
},
3948
configureServer(server) {

test/loader.test.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
11
import fs from 'fs';
2-
import { parseAll, parse, hasPhpTranslations, reset } from '../src/loader';
2+
import { generateFiles, parseAll, parse, hasPhpTranslations, reset } from '../src/loader';
33

44
beforeEach(() => reset(__dirname + '/fixtures/lang/'));
55

66
it('creates a file for each lang', () => {
7-
const files = parseAll(__dirname + '/fixtures/lang/');
7+
const langPath = __dirname + '/fixtures/lang/';
8+
const files = generateFiles(langPath, parseAll(langPath));
89

910
expect(files.length).toBe(3);
1011
expect(files[0].name).toBe('php_en.json');
1112
expect(files[1].name).toBe('php_fr.json');
1213
expect(files[2].name).toBe('php_pt.json');
1314

14-
const langEn = JSON.parse(fs.readFileSync(files[0].path).toString());
15+
const langEn = JSON.parse(fs.readFileSync(langPath + files[0].name).toString());
1516
expect(langEn['auth.failed']).toBe('These credentials do not match our records.');
1617
expect(langEn['auth.foo.level1.level2']).toBe('baren');
1718
expect(langEn['auth.multiline']).toBe('Lorem ipsum dolor sit amet.');
1819

19-
const langPt = JSON.parse(fs.readFileSync(files[2].path).toString());
20+
const langPt = JSON.parse(fs.readFileSync(langPath + files[2].name).toString());
2021
expect(langPt['auth.failed']).toBe('As credenciais indicadas não coincidem com as registadas no sistema.');
2122
expect(langPt['auth.foo.level1.level2']).toBe('barpt');
2223
});
2324

2425
it('includes .php lang file in subdirectory in .json', () => {
25-
const files = parseAll(__dirname + '/fixtures/lang/');
26-
const langEn = JSON.parse(fs.readFileSync(files[0].path).toString());
26+
const langPath = __dirname + '/fixtures/lang/';
27+
const files = generateFiles(langPath, parseAll(langPath));
28+
const langEn = JSON.parse(fs.readFileSync(langPath + files[0].name).toString());
2729

2830
expect(langEn['domain.user.sub_dir_support_is_amazing']).toBe('Subdirectory support is amazing');
2931
expect(langEn['domain.car.is_electric']).toBe('Electric');
3032
expect(langEn['domain.car.foo.level1.level2']).toBe('barpt');
3133
});
3234

3335
it('includes .php lang file in nested subdirectory in .json', () => {
34-
const files = parseAll(__dirname + '/fixtures/lang/');
35-
const langEn = JSON.parse(fs.readFileSync(files[0].path).toString())
36+
const langPath = __dirname + '/fixtures/lang/';
37+
const files = generateFiles(langPath, parseAll(langPath));
38+
const langEn = JSON.parse(fs.readFileSync(langPath + files[0].name).toString())
3639

3740
expect(langEn['nested.cars.car.is_electric']).toBe('Electric');
3841
expect(langEn['nested.cars.car.foo.level1.level2']).toBe('barpt');

test/setup.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { mount } from '@vue/test-utils'
22
import { i18nVue } from '../src'
3-
import { parseAll } from '../src/loader'
3+
import { generateFiles, parseAll } from '../src/loader'
44

55
global.mountPlugin = async (template = '<div />', lang = 'pt', fallbackLang = 'pt', fallbackMissingTranslations = false) => {
66
const wrapper = mount({ template }, {
@@ -36,7 +36,8 @@ global.mountPluginWithRequire = async (template = '<div />', lang = 'pt', fallba
3636
}
3737

3838
global.mixLoader = () => {
39-
parseAll(__dirname + '/fixtures/lang/');
39+
const langPath = __dirname + '/fixtures/lang/';
40+
generateFiles(langPath, parseAll(langPath));
4041

4142
process.env = Object.assign(process.env, {
4243
LARAVEL_VUE_I18N_HAS_PHP: 'true',

0 commit comments

Comments
 (0)