Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add locales generator #544

Merged
merged 9 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions generators/locales/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# LOCALES-GENERATOR

Скрипт для автоматической генерации переводов `react-inl`.

Создаёт на уровне энтрипоинта `locales/[localName].json` файл, содержащий все переводы энтрипоинта на основе `id` и `defaultMessages`

Для работы обязательно указывать в `formatMessage` или `FormattedMessage` уникальный `id` и текст перевода в `defaultMessages`.

Пример валидного компонента:

```html
<Hello
hello={formatMessage({ id: 'app.home_page.hello', defaultMessage: 'HELLO FROM HOME' })}
/>
<Text>
<FormattedMessage id='app.home_page.content' defaultMessage='CONTENT' />
</Text>
```

---

## Установка

Устанавливаем на уровне энтрипоинта: `yarn add -D @atls-ui-generators/locales`

## Запуск

Для запуска необходимо добавить скрипты в `package.json` ентрипоинта.

Пример скриптов:

```json
{
"scripts": {
"generate-locales": "generate-locales ../../fragments ../../pages --out=en",
"postinstall": "run generate-locales"
}
}
```

### Входные аргументы

`generate-locales` принимает следующие аргументы:

1. **componentPaths** - Относительный список путей от `package.json` энтрипоинта по которым будет производится поиск переводов \*_необязательный_.
- Принимается неограниченное кол-во аргументов. "generate-locales path1 path2 ...pathN"
- Если не указать **componentPaths** , скрипт будет проходить по фрагментам и страницам текущего энтрипоинта
2. **localName** - Название локали \*_необязательный_.
- Указывается после `-out=`
- Если не указывать **localName**, то умолчанию берётся локаль `ru`

---

# Changelog

---
33 changes: 33 additions & 0 deletions generators/locales/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@atls-ui-generators/locales",
"version": "0.0.1",
Nelfimov marked this conversation as resolved.
Show resolved Hide resolved
"license": "BSD-3-Clause",
"main": "src/index.ts",
"typings": "dist/index.d.ts",
Nelfimov marked this conversation as resolved.
Show resolved Hide resolved
"bin": {
"generate-locales": "dist/generator.js"
},
"files": [
"dist"
],
"scripts": {
"build": "yarn library build",
"prepack": "yarn run build",
"postpack": "rm -rf dist"
},
"dependencies": {
"@atls/config-prettier": "0.0.5",
"@atls/prettier-plugin": "0.0.7",
"@babel/standalone": "7.22.20",
"camelcase": "6.3.0",
"commander": "9.5.0",
"prettier": "2.8.8"
},
"devDependencies": {
"@types/babel__standalone": "7.1.6",
"@types/prettier": "2.7.3"
},
"publishConfig": {
Nelfimov marked this conversation as resolved.
Show resolved Hide resolved
"access": "public"
}
}
22 changes: 22 additions & 0 deletions generators/locales/src/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defaultPaths } from './locales-generator.constants'
import { mergeLocales } from './merge-locales'
import { processDirectory } from './process-directory'

const allLocales = []
let outputFile = 'ru'
const argPaths: string[] = []

// @ts-ignore
process.argv.slice(2).forEach((arg) => {
if (!arg.startsWith('--out=')) {
argPaths.push(arg)
} else {
// eslint-disable-next-line prefer-destructuring
outputFile = arg.split('=')[1]
}
})
const paths = argPaths.length ? argPaths : defaultPaths

paths.forEach((path) => processDirectory(path, 'locales', allLocales, outputFile))

mergeLocales(allLocales, `./locales/${outputFile}.json`)
1 change: 1 addition & 0 deletions generators/locales/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './generator'
1 change: 1 addition & 0 deletions generators/locales/src/locales-generator.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const defaultPaths = ['../../fragments', '../../pages']
1 change: 1 addition & 0 deletions generators/locales/src/merge-locales/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './merge-locales'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type MergeLocalesType = (files: string[], outputPath: string) => void
26 changes: 26 additions & 0 deletions generators/locales/src/merge-locales/merge-locales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { existsSync } from 'fs'
import { readFileSync } from 'fs'
import { mkdirSync } from 'fs'
import { writeFileSync } from 'fs'
import { dirname } from 'path'

import { MergeLocalesType } from './merge-locales.interfaces'

export const mergeLocales: MergeLocalesType = (files, outputPath) => {
if (!files.length) return

const mergedLocales = {}
files.forEach((file) => {
if (existsSync(file)) {
const content = JSON.parse(readFileSync(file, 'utf8'))
Object.assign(mergedLocales, content)
}
})
const directory = dirname(outputPath)

if (!existsSync(directory)) {
mkdirSync(directory, { recursive: true })
}

writeFileSync(outputPath, JSON.stringify(mergedLocales, null, 2), 'utf8')
}
1 change: 1 addition & 0 deletions generators/locales/src/process-directory/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './process-directory'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type ProcessDirectoryType = (
startPath: string,
folderName: string,
allLocales: string[],
outputLocale: string
) => void
37 changes: 37 additions & 0 deletions generators/locales/src/process-directory/process-directory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* eslint-disable */
Nelfimov marked this conversation as resolved.
Show resolved Hide resolved

import { execSync } from 'child_process'
import { existsSync } from 'fs'
import { readdirSync } from 'fs'
import { join } from 'path'

import { ProcessDirectoryType } from './process-directory.interfaces'
import { removeEmptyLocale } from '../remove-empty-locale'

export const processDirectory: ProcessDirectoryType = (
startPath,
folderName,
allLocales,
outputLocale
) => {
if (!existsSync(startPath)) {
console.log('No directory ', startPath)
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
return
}

const directories = readdirSync(startPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)

directories.forEach((dir) => {
const localePath = join(startPath, dir)
if (existsSync(localePath)) {
const outputFilePath = `${localePath}/${folderName}/${outputLocale}.json`
const command = `formatjs extract "${localePath}/**/*.tsx" --out-file "${outputFilePath}" --format simple`
console.log(`Running: ${command}`)
execSync(command, { stdio: 'inherit' })

removeEmptyLocale(outputFilePath, localePath, folderName, allLocales)
}
})
}
1 change: 1 addition & 0 deletions generators/locales/src/remove-empty-locale/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './remove-empty-locale'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type RemoveEmptyLocaleType = (
outputFilePath: string,
localePath: string,
folderName: string,
allLocales: string[]
) => void
28 changes: 28 additions & 0 deletions generators/locales/src/remove-empty-locale/remove-empty-locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { existsSync } from 'fs'
import { readFileSync } from 'fs'
import { readdirSync } from 'fs'
import { rmSync } from 'fs'
import { unlinkSync } from 'fs'
import { join } from 'path'

import { RemoveEmptyLocaleType } from './remove-empty-locale.interfaces'

export const removeEmptyLocale: RemoveEmptyLocaleType = (
outputFilePath,
localePath,
folderName,
allLocales
) => {
if (existsSync(outputFilePath)) {
const content = readFileSync(outputFilePath, 'utf8').trim()
if (content === '{}' || content === '{\n}') {
unlinkSync(outputFilePath)

if (readdirSync(join(localePath, folderName)).length === 0) {
rmSync(join(localePath, folderName), { recursive: true, force: true })
}
} else {
allLocales.push(outputFilePath)
}
}
}
17 changes: 17 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,23 @@ __metadata:
languageName: unknown
linkType: soft

"@atls-ui-generators/locales@workspace:generators/locales":
version: 0.0.0-use.local
resolution: "@atls-ui-generators/locales@workspace:generators/locales"
dependencies:
"@atls/config-prettier": "npm:0.0.5"
"@atls/prettier-plugin": "npm:0.0.7"
"@babel/standalone": "npm:7.22.20"
"@types/babel__standalone": "npm:7.1.6"
"@types/prettier": "npm:2.7.3"
camelcase: "npm:6.3.0"
commander: "npm:9.5.0"
prettier: "npm:2.8.8"
bin:
generate-locales: dist/generator.js
languageName: unknown
linkType: soft

"@atls-ui-generators/utils@workspace:*, @atls-ui-generators/utils@workspace:generators/utils":
version: 0.0.0-use.local
resolution: "@atls-ui-generators/utils@workspace:generators/utils"
Expand Down