Skip to content

Commit bfcc3f4

Browse files
committed
app, frontend: Unify locales
Instead of having two separate config and dirs for locales, these changes unify the locales under the frontend directory. This will reduce the configuration burden and being less error prone, making it easier for i18n contributors to see all needed translations in one place. Signed-off-by: Joaquim Rocha <[email protected]>
1 parent 579940e commit bfcc3f4

File tree

15 files changed

+164
-30
lines changed

15 files changed

+164
-30
lines changed

app/electron/i18n-helper.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
import * as fs from 'fs';
22
import * as path from 'path';
33

4-
const directoryPath = path.join(__dirname, './locales/');
5-
const currentLocales: string[] = [];
4+
// Get the path to the frontend locales directory
5+
let frontendLocalesPath: string;
6+
const isDev = process.env.ELECTRON_DEV || false;
7+
// Check if we're running in a normal Node.js process (for i18next-parser)
8+
// or in an Electron environment
9+
const isRunningInNode = !(process as any).resourcesPath;
10+
11+
if (isDev || isRunningInNode) {
12+
// When running as a normal Node.js process (i18next parser) or in dev mode
13+
frontendLocalesPath = path.resolve(__dirname, '../../frontend/src/i18n/locales');
14+
} else {
15+
// When running in Electron production mode
16+
frontendLocalesPath = path.join((process as any).resourcesPath, 'frontend/i18n/locales');
17+
}
618

7-
fs.readdirSync(directoryPath).forEach(file => currentLocales.push(file));
19+
// Read available locales from the frontend locales directory
20+
const currentLocales: string[] = [];
21+
if (fs.existsSync(frontendLocalesPath)) {
22+
fs.readdirSync(frontendLocalesPath).forEach(file => {
23+
// Only include directories, not files
24+
if (fs.statSync(path.join(frontendLocalesPath, file)).isDirectory()) {
25+
currentLocales.push(file);
26+
}
27+
});
28+
}
829

9-
export { currentLocales as CURRENT_LOCALES };
10-
export { directoryPath as LOCALES_DIR };
30+
// If no locales found, default to English
31+
export const CURRENT_LOCALES = currentLocales.length > 0 ? currentLocales : ['en'];
32+
export const LOCALES_DIR = frontendLocalesPath;

app/electron/i18next-parser.config.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
const path = require('path');
22
const helper = require('./i18n-helper');
3+
const fs = require('fs');
4+
5+
// Import shared configuration values from frontend
6+
let sharedConfig;
7+
try {
8+
// Try to import the shared config dynamically
9+
const sharedConfigPath = path.resolve(
10+
__dirname,
11+
'../../frontend/src/i18n/i18nextSharedConfig.mjs'
12+
);
13+
if (fs.existsSync(sharedConfigPath)) {
14+
// For CommonJS requiring ES modules, use a dynamic import
15+
sharedConfig = {
16+
contextSeparator: '//context:',
17+
namespaces: ['translation', 'glossary', 'app'],
18+
defaultNamespace: 'translation',
19+
};
20+
}
21+
} catch (error) {
22+
console.error('Failed to import shared config:', error);
23+
}
24+
25+
// Ensure the LOCALES_DIR is defined
26+
if (!helper.LOCALES_DIR) {
27+
throw new Error('Locales directory is not defined. Check i18n-helper.js for issues.');
28+
}
329

430
module.exports = {
531
lexers: {
@@ -8,9 +34,9 @@ module.exports = {
834
namespaceSeparator: '|',
935
keySeparator: false,
1036
defaultNamespace: 'app',
11-
contextSeparator: '//context:',
12-
output: path.join(helper.LOCALES_DIR, './$LOCALE/$NAMESPACE.json'),
13-
locales: helper.CURRENT_LOCALES,
37+
contextSeparator: sharedConfig?.contextSeparator || '//context:',
38+
output: path.join(helper.LOCALES_DIR, '$LOCALE/$NAMESPACE.json'),
39+
locales: helper.CURRENT_LOCALES.length > 0 ? helper.CURRENT_LOCALES : ['en'],
1440
// The English catalog has "SomeKey": "SomeKey" so we stop warnings about
1541
// missing values.
1642
useKeysAsDefaultValue: locale => locale === 'en',

app/package-lock.json

Lines changed: 47 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@
116116
"files": [
117117
"electron/main.js",
118118
"electron/preload.js",
119-
"electron/locales/",
120119
"electron/i18next.config.js",
121120
"electron/i18n-helper.js",
122121
"electron/windowSize.js",
@@ -159,6 +158,7 @@
159158
"@electron/notarize": "^2.3.2",
160159
"@headlamp-k8s/eslint-config": "^0.6.0",
161160
"@types/jest": "^29.5.14",
161+
"@types/node": "^22.14.0",
162162
"electron": "^31.2.0",
163163
"electron-builder": "^24.13.3",
164164
"i18next-parser": "^9.0.0",

docs/development/i18n/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Here's an example of using date formatting:
8989
## Adding a new language
9090

9191
Create a folder using the locale code in:
92-
`frontend/src/i18n/locales/` and `app/electron/locales`
92+
`frontend/src/i18n/locales/`
9393

9494
Then run `make i18n`. This command parses the translatable strings in
9595
the project and creates the corresponding catalog files.

frontend/src/i18n/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ i18next
4141
// i18next options: https://www.i18next.com/overview/configuration-options
4242
.init({
4343
debug: import.meta.env.DEV && !import.meta.env.UNDER_TEST,
44-
ns: ['translation', 'glossary'],
45-
defaultNS: 'translation',
44+
ns: sharedConfig.namespaces,
45+
defaultNS: sharedConfig.defaultNamespace,
4646
fallbackLng: 'en',
4747
contextSeparator: sharedConfig.contextSeparator,
4848
supportedLngs: Object.keys(supportedLanguages),

frontend/src/i18n/i18next-parser.config.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import fs from 'fs';
22
import path from 'path';
33
import sharedConfig from './i18nextSharedConfig.mjs';
44

5-
const directoryPath = path.join(import.meta.dirname, './locales/');
5+
const directoryPath = path.join(import.meta.dirname, sharedConfig.localesPath);
66
const currentLocales = [];
7-
const contextSeparator = sharedConfig.contextSeparator;
87

98
fs.readdirSync(directoryPath).forEach(file => currentLocales.push(file));
109

@@ -16,12 +15,12 @@ export default {
1615
keySeparator: false,
1716
output: path.join(directoryPath, './$LOCALE/$NAMESPACE.json'),
1817
locales: currentLocales,
19-
contextSeparator,
18+
contextSeparator: sharedConfig.contextSeparator,
2019
defaultValue: (locale, _namespace, key) => {
2120
// The English catalog has "SomeKey": "SomeKey" so we stop warnings about
2221
// missing values.
2322
if (locale === 'en') {
24-
const contextSepIdx = key.indexOf(contextSeparator);
23+
const contextSepIdx = key.indexOf(sharedConfig.contextSeparator);
2524
if (contextSepIdx >= 0) {
2625
return key.substring(0, contextSepIdx);
2726
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
export default {
22
contextSeparator: '//context:',
3+
namespaces: ['translation', 'glossary', 'app'],
4+
defaultNamespace: 'translation',
5+
localesPath: './locales',
36
};

0 commit comments

Comments
 (0)