Skip to content

Commit e0a49fd

Browse files
authored
chore(condo): language customization (#5911)
* chore(condo): draft: language customization * chore(condo): draft: language customization * chore(condo): draft: add translations linter * chore(condo): remove test translation * chore(condo): add guide
1 parent a42d776 commit e0a49fd

File tree

7 files changed

+79
-7
lines changed

7 files changed

+79
-7
lines changed

apps/condo/README.md

+27-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,30 @@ Condo development guide
55
* [Project structure](./docs/project-structure.md)
66
* [Migrations guide](./docs/migrations.md)
77
* [Utils](./docs/utils)
8-
* [Checklists](./docs/checklists)
8+
* [Checklists](./docs/checklists)
9+
10+
## Customization guide for open source developers
11+
12+
### Adding translations
13+
14+
Sometimes, in your custom forks you need to add or override translations, for this you should write custom translations in `lang/<locale>/<locale>.custom.json`
15+
16+
These translations then can be used just as regular translations in condo
17+
18+
`lang/en/en.custom.json`
19+
```json
20+
{
21+
"custom.example": "Test"
22+
}
23+
```
24+
25+
`any component`
26+
```js
27+
const TestCustomTranslation = intl.formatMessage({ id: 'custom.example' })
28+
29+
console.log(TestCustomTranslation) // Test
30+
```
31+
32+
Translations inside the custom translations file **will overwrite** other translations.
33+
34+
Best practice is to start your custom translation with prefix `custom`, that way it is guaranteed that these translations will not collide with translations in condo
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
export async function messagesImporter (locale: string) {
2-
const locale_data = await import(`@app/condo/lang/${locale}/${locale}`)
3-
return locale_data.default
2+
const localeData = await import(`@app/condo/lang/${locale}/${locale}`)
3+
4+
const customLocaleData = await import(`@app/condo/lang/${locale}/${locale}.custom`)
5+
if (customLocaleData) {
6+
return { ...localeData.default, ...customLocaleData.default }
7+
}
8+
9+
return localeData.default
410
}

apps/condo/global.d.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { GetActiveOrganizationEmployeeQuery, AuthenticatedUserQuery } from '@app/condo/gql'
2+
import enCustom from '@app/condo/lang/en/en.custom.json'
23
import en from '@app/condo/lang/en/en.json'
4+
import es from '@app/condo/lang/es/es.json'
5+
import esCustom from '@app/condo/lang/ru/es.custom.json'
6+
import ruCustom from '@app/condo/lang/ru/ru.custom.json'
37
import ru from '@app/condo/lang/ru/ru.json'
48

59

610
// NOTE: Combine all keys together
7-
const translations = [en, ru] as const
8-
type MessagesKeysType = keyof typeof translations[number]
11+
const translations = [en, ru, es] as const
12+
const customTranslations = [enCustom, ruCustom, esCustom] as const
13+
14+
type TranslationsKeysType = keyof typeof translations[number]
15+
type CustomTranslationsKeysType = keyof typeof customTranslations[number]
16+
type MessagesKeysType = TranslationsKeys | CustomTranslationsKeys
917

1018
type LinkExtendsType = GetActiveOrganizationEmployeeQuery['employees'][number]
1119
type OrganizationExtendsType = GetActiveOrganizationEmployeeQuery['employees'][number]['organization']

apps/condo/lang/en/en.custom.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"custom.example": "Test"
3+
}

apps/condo/lang/es/es.custom.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"custom.example": "ES Test"
3+
}

apps/condo/lang/ru/ru.custom.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"custom.example": "Тест"
3+
}

bin/lint-i18n-translations.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const util = require('util')
55

66
const execPromise = util.promisify(exec)
77

8+
const RESERVED_PREFIXES = ['custom']
9+
810
async function writeTranslationData (translationFilePath, translationData) {
911
const sortedTranslationData = Object.keys(translationData).sort().reduce((acc, key) => {
1012
acc[key] = translationData[key]
@@ -55,7 +57,23 @@ async function saveTranslations (langDir, translations) {
5557
}
5658
}
5759

58-
async function validateTranslations (translations) {
60+
async function getKeysWithReservedPrefixes (translations) {
61+
const keysWithReservedPrefixes = []
62+
63+
translations.forEach(([, keys]) => {
64+
keys.forEach(key => {
65+
RESERVED_PREFIXES.forEach(reservedPrefix => {
66+
if (key.startsWith(reservedPrefix)) {
67+
keysWithReservedPrefixes.push(key)
68+
}
69+
})
70+
})
71+
})
72+
73+
return keysWithReservedPrefixes
74+
}
75+
76+
async function getMissingKeys (translations) {
5977
const allKeys = new Set()
6078
translations.forEach(([, keys]) => {
6179
keys.forEach(key => allKeys.add(key))
@@ -143,7 +161,7 @@ async function main () {
143161
const langDir = path.join(root, 'lang')
144162

145163
const translations = await loadTranslations(langDir)
146-
const missingKeysReport = await validateTranslations(translations)
164+
const missingKeysReport = await getMissingKeys(translations)
147165

148166
if (missingKeysReport.length > 0) {
149167
if (shouldFix) {
@@ -153,6 +171,11 @@ async function main () {
153171
}
154172
}
155173

174+
const keysWithReservedPrefixes = await getKeysWithReservedPrefixes(translations)
175+
if (keysWithReservedPrefixes.length > 0) {
176+
throw new Error(`Keys starting with one of reserved prefixes: [${RESERVED_PREFIXES.join(', ')}] found. Please check your translation files and fix those keys`)
177+
}
178+
156179
if (shouldFix) {
157180
await saveTranslations(langDir, translations)
158181
}

0 commit comments

Comments
 (0)