@gjsify/vite-plugin-gettext 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Configuration options for the xgettext plugin
3
+ * Used to extract translatable strings from source files
4
+ */
5
+ export interface XGettextPluginOptions {
6
+ /** Glob patterns for source files to extract strings from */
7
+ sources: string[];
8
+ /** Output path for the POT template file */
9
+ output: string;
10
+ /** The gettext domain name, defaults to 'messages' */
11
+ domain?: string;
12
+ /** Keywords to look for when extracting strings, defaults to ['_', 'gettext', 'ngettext'] */
13
+ keywords?: string[];
14
+ /** Additional options to pass to xgettext command */
15
+ xgettextOptions?: string[];
16
+ /** Enable verbose logging */
17
+ verbose?: boolean;
18
+ /** Automatically update PO files after POT changes */
19
+ autoUpdatePo?: boolean;
20
+ /** Version of the POT file, defaults to '1.0' */
21
+ version?: string;
22
+ /** Preset to use for extracting strings, defaults to 'glib' */
23
+ preset?: "glib";
24
+ /** URL for reporting bugs in the POT file */
25
+ msgidBugsAddress?: string;
26
+ /** Copyright holder to set in the POT file */
27
+ copyrightHolder?: string;
28
+ }
29
+ /**
30
+ * Configuration options for the gettext plugin
31
+ * Used to compile PO files to binary MO format
32
+ */
33
+ export interface GettextPluginOptions {
34
+ /** Directory containing PO translation files */
35
+ poDirectory: string;
36
+ /** Output directory for compiled MO files */
37
+ moDirectory: string;
38
+ /** Filename of the MO file, defaults to 'messages.mo' */
39
+ filename?: string;
40
+ /** Enable verbose logging */
41
+ verbose?: boolean;
42
+ }
43
+ /**
44
+ * Output format types for msgfmt
45
+ */
46
+ export type MsgfmtFormat = "mo" | "java" | "java2" | "csharp" | "csharp-resources" | "tcl" | "desktop" | "xml" | "json" | "qt";
47
+ /**
48
+ * Configuration options for the msgfmt plugin
49
+ * Used to compile PO files to various formats including binary MO
50
+ */
51
+ export interface MsgfmtPluginOptions {
52
+ /** Directory containing PO translation files */
53
+ poDirectory: string;
54
+ /** Output directory for compiled files */
55
+ outputDirectory: string;
56
+ /** The gettext domain name, defaults to 'messages' */
57
+ domain?: string;
58
+ /** Output filename, defaults to 'messages.mo' */
59
+ filename?: string;
60
+ /** Output format, defaults to 'mo' */
61
+ format?: MsgfmtFormat;
62
+ /** Path to template file, required for XML format */
63
+ templateFile?: string;
64
+ /** Enable verbose logging */
65
+ verbose?: boolean;
66
+ /** Additional options to pass to msgfmt command */
67
+ msgfmtOptions?: string[];
68
+ /** Whether to use the standard locale structure (locale/LANG/LC_MESSAGES/domain.mo) */
69
+ useLocaleStructure?: boolean;
70
+ }
71
+ export interface PluginOptions {
72
+ pluginName: string;
73
+ verbose?: boolean;
74
+ }
75
+ /**
76
+ * Options for the PO to JSON conversion plugin
77
+ */
78
+ export interface GettextPo2JsonPluginOptions {
79
+ /**
80
+ * Directory containing PO files
81
+ */
82
+ poDirectory: string;
83
+ /**
84
+ * Directory where JSON files will be saved
85
+ */
86
+ jsonDirectory: string;
87
+ /**
88
+ * Default language code (default: 'en')
89
+ */
90
+ defaultLanguage?: string;
91
+ /**
92
+ * Enable verbose logging
93
+ */
94
+ verbose?: boolean;
95
+ /**
96
+ * Additional translations to include in all language files
97
+ * Keys are identifiers and values are the English text
98
+ * The English text will be translated for non-default languages if translations exist
99
+ */
100
+ additionalTranslations?: Record<string, string>;
101
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Checks if a gettext utility is installed and available
3
+ * @param command The command to check (msgfmt, xgettext, etc.)
4
+ * @param pluginName Name of the plugin for logging
5
+ * @param verbose Enable verbose logging
6
+ * @throws Error if the command is not found
7
+ */
8
+ export declare function checkDependencies(command: string, pluginName: string, verbose: boolean): Promise<void>;
9
+ /**
10
+ * Scans the PO directory to find available language translations
11
+ * @param poDirectory Directory containing PO files
12
+ * @param pluginName Name of the plugin for logging
13
+ * @param verbose Enable verbose logging
14
+ * @returns Array of language codes found (e.g. ['de', 'fr', 'es'])
15
+ */
16
+ export declare function findAvailableLanguages(poDirectory: string, pluginName: string, verbose: boolean): Promise<string[]>;
17
+ /**
18
+ * Generates a LINGUAS file containing the list of available languages
19
+ * @param languages List of language codes
20
+ * @param poDirectory Directory where the LINGUAS file should be created
21
+ * @param verbose Enable verbose logging
22
+ */
23
+ export declare function generateLinguasFile(languages: string[], poDirectory: string, verbose?: boolean): Promise<void>;
24
+ /**
25
+ * Creates directory structure recursively
26
+ * @param directory Directory path to create
27
+ */
28
+ export declare function ensureDirectory(directory: string): Promise<void>;
29
+ /**
30
+ * Processes a filename with potential .in suffix
31
+ * @param filePath Original file path or filename
32
+ * @returns Object with processed filename and extension
33
+ */
34
+ export declare function processFilename(filePath: string): {
35
+ filename: string;
36
+ extension: string;
37
+ };
package/dist/utils.js ADDED
@@ -0,0 +1,94 @@
1
+ import { execa } from "execa";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ /**
5
+ * Checks if a gettext utility is installed and available
6
+ * @param command The command to check (msgfmt, xgettext, etc.)
7
+ * @param pluginName Name of the plugin for logging
8
+ * @param verbose Enable verbose logging
9
+ * @throws Error if the command is not found
10
+ */
11
+ export async function checkDependencies(command, pluginName, verbose) {
12
+ try {
13
+ await execa(command, ["--version"]);
14
+ if (verbose) {
15
+ console.log(`[${pluginName}] Found ${command}`);
16
+ }
17
+ }
18
+ catch (error) {
19
+ throw new Error(`${command} not found. Please install gettext:\n` +
20
+ " Ubuntu/Debian: sudo apt-get install gettext\n" +
21
+ " Fedora: sudo dnf install gettext\n" +
22
+ " Arch: sudo pacman -S gettext\n" +
23
+ " macOS: brew install gettext");
24
+ }
25
+ }
26
+ /**
27
+ * Scans the PO directory to find available language translations
28
+ * @param poDirectory Directory containing PO files
29
+ * @param pluginName Name of the plugin for logging
30
+ * @param verbose Enable verbose logging
31
+ * @returns Array of language codes found (e.g. ['de', 'fr', 'es'])
32
+ */
33
+ export async function findAvailableLanguages(poDirectory, pluginName, verbose) {
34
+ try {
35
+ const files = await fs.readdir(poDirectory);
36
+ const languages = files
37
+ .filter((file) => file.endsWith(".po"))
38
+ .map((file) => path.basename(file, ".po"));
39
+ if (verbose) {
40
+ console.log(`[${pluginName}] Found languages: ${languages.join(", ")}`);
41
+ }
42
+ return languages;
43
+ }
44
+ catch (error) {
45
+ if (verbose) {
46
+ console.log(`[${pluginName}] No PO directory found at ${poDirectory}`);
47
+ }
48
+ return [];
49
+ }
50
+ }
51
+ /**
52
+ * Generates a LINGUAS file containing the list of available languages
53
+ * @param languages List of language codes
54
+ * @param poDirectory Directory where the LINGUAS file should be created
55
+ * @param verbose Enable verbose logging
56
+ */
57
+ export async function generateLinguasFile(languages, poDirectory, verbose = false) {
58
+ const linguasPath = path.join(poDirectory, "LINGUAS");
59
+ const content = languages.join("\n");
60
+ try {
61
+ await fs.writeFile(linguasPath, content);
62
+ if (verbose) {
63
+ console.log(`Generated LINGUAS file with languages: ${languages.join(", ")}`);
64
+ }
65
+ }
66
+ catch (error) {
67
+ console.error("Error writing LINGUAS file:", error);
68
+ }
69
+ }
70
+ /**
71
+ * Creates directory structure recursively
72
+ * @param directory Directory path to create
73
+ */
74
+ export async function ensureDirectory(directory) {
75
+ await fs.mkdir(directory, { recursive: true });
76
+ }
77
+ /**
78
+ * Processes a filename with potential .in suffix
79
+ * @param filePath Original file path or filename
80
+ * @returns Object with processed filename and extension
81
+ */
82
+ export function processFilename(filePath) {
83
+ // Extract just the filename if a path is provided
84
+ const filename = path.basename(filePath);
85
+ let extension = path.extname(filename).toLowerCase();
86
+ let processedFilename = filename;
87
+ // Handle .in extension
88
+ if (filename.endsWith(".in")) {
89
+ processedFilename = filename.substring(0, filename.length - 3);
90
+ extension = path.extname(processedFilename).toLowerCase();
91
+ }
92
+ return { filename: processedFilename, extension };
93
+ }
94
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type Plugin } from "vite";
2
+ import type { XGettextPluginOptions } from "./types.js";
3
+ /**
4
+ * Creates a Vite plugin that extracts translatable strings from source files
5
+ * Uses GNU xgettext to generate a POT template file that can be used as basis for translations
6
+ * @param options Configuration options for the plugin
7
+ * @returns A Vite plugin that handles string extraction
8
+ */
9
+ export declare function xgettextPlugin(options: XGettextPluginOptions): Plugin;
@@ -0,0 +1,261 @@
1
+ import {} from "vite";
2
+ import { execa } from "execa";
3
+ import path from "node:path";
4
+ import fs from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
6
+ import glob from "fast-glob";
7
+ import { checkDependencies, ensureDirectory, processFilename, } from "./utils.js";
8
+ // Add GLib preset constants
9
+ // From https://github.com/mesonbuild/meson/blob/467da051c859ba3112803b035e317bddadd756ef/mesonbuild/modules/i18n.py
10
+ const GLIB_PRESET_ARGS = [
11
+ "--from-code=UTF-8",
12
+ "--add-comments",
13
+ // https://developer.gnome.org/glib/stable/glib-I18N.html
14
+ "--keyword=_",
15
+ "--keyword=N_",
16
+ "--keyword=C_:1c,2",
17
+ "--keyword=NC_:1c,2",
18
+ "--keyword=g_dcgettext:2",
19
+ "--keyword=g_dngettext:2,3",
20
+ "--keyword=g_dpgettext2:2c,3",
21
+ "--flag=N_:1:pass-c-format",
22
+ "--flag=C_:2:pass-c-format",
23
+ "--flag=NC_:2:pass-c-format",
24
+ "--flag=g_dngettext:2:pass-c-format",
25
+ "--flag=g_strdup_printf:1:c-format",
26
+ "--flag=g_string_printf:2:c-format",
27
+ "--flag=g_string_append_printf:2:c-format",
28
+ "--flag=g_error_new:3:c-format",
29
+ "--flag=g_set_error:4:c-format",
30
+ "--flag=g_markup_printf_escaped:1:c-format",
31
+ "--flag=g_log:3:c-format",
32
+ "--flag=g_print:1:c-format",
33
+ "--flag=g_printerr:1:c-format",
34
+ "--flag=g_printf:1:c-format",
35
+ "--flag=g_fprintf:2:c-format",
36
+ "--flag=g_sprintf:2:c-format",
37
+ "--flag=g_snprintf:3:c-format",
38
+ ];
39
+ /**
40
+ * Creates a Vite plugin that extracts translatable strings from source files
41
+ * Uses GNU xgettext to generate a POT template file that can be used as basis for translations
42
+ * @param options Configuration options for the plugin
43
+ * @returns A Vite plugin that handles string extraction
44
+ */
45
+ export function xgettextPlugin(options) {
46
+ const pluginName = "vite-plugin-xgettext";
47
+ return {
48
+ name: pluginName,
49
+ async buildStart() {
50
+ await checkDependencies("xgettext", pluginName, options.verbose ?? false);
51
+ const files = await glob(options.sources);
52
+ await extractStrings(files, options, pluginName);
53
+ },
54
+ configureServer(server) {
55
+ server.watcher.add(options.sources);
56
+ server.watcher.on("change", async (file) => {
57
+ if (options.sources.some((pattern) => file.match(pattern))) {
58
+ if (options.verbose) {
59
+ console.log(`[${pluginName}] Source file changed: ${file}, re-running extraction`);
60
+ }
61
+ const files = await glob(options.sources);
62
+ await extractStrings(files, options, pluginName);
63
+ }
64
+ });
65
+ },
66
+ };
67
+ }
68
+ async function generatePotfiles(files, outputDir, pluginName, verbose = false) {
69
+ // Group files by extension
70
+ const fileGroups = new Map();
71
+ files.forEach((file) => {
72
+ const filename = path.basename(file);
73
+ const group = getFileGroup(filename);
74
+ if (!fileGroups.has(group)) {
75
+ fileGroups.set(group, []);
76
+ }
77
+ fileGroups.get(group)?.push(file);
78
+ });
79
+ // Generate POTFILES for each group
80
+ const potFiles = [];
81
+ for (const [group, groupFiles] of fileGroups) {
82
+ const potfilePath = path.join(outputDir, `${group}.POTFILES`);
83
+ const content = groupFiles.join("\n");
84
+ try {
85
+ await fs.writeFile(potfilePath, content);
86
+ potFiles.push(potfilePath);
87
+ if (verbose) {
88
+ console.log(`[${pluginName}] Generated ${group}.POTFILES with ${groupFiles.length} source files`);
89
+ }
90
+ }
91
+ catch (error) {
92
+ console.error(`[${pluginName}] Error writing ${group}.POTFILES:`, error);
93
+ }
94
+ }
95
+ return potFiles;
96
+ }
97
+ function getFileGroup(fullFilename) {
98
+ // Process filename to handle .in extension
99
+ const { filename, extension } = processFilename(fullFilename);
100
+ // Special handling for metainfo.xml files
101
+ if (filename.endsWith(".metainfo.xml") || filename.endsWith(".appdata.xml")) {
102
+ return "metainfo";
103
+ }
104
+ switch (extension) {
105
+ case ".ts":
106
+ case ".js":
107
+ case ".tsx":
108
+ return "js";
109
+ case ".ui":
110
+ case ".xml":
111
+ return "ui";
112
+ case ".blp":
113
+ return "blp";
114
+ case ".desktop":
115
+ return "desktop";
116
+ default:
117
+ return "other";
118
+ }
119
+ }
120
+ async function extractStrings(files, options, pluginName) {
121
+ const { output, domain = "messages", keywords = [], preset, verbose = false, } = options;
122
+ try {
123
+ const outputDir = path.dirname(output);
124
+ await ensureDirectory(outputDir);
125
+ // Generate grouped POTFILES
126
+ const potFiles = await generatePotfiles(files, outputDir, pluginName, verbose);
127
+ // Create temporary POT files for each group
128
+ const tempPotFiles = [];
129
+ for (const potFile of potFiles) {
130
+ const group = path.basename(potFile).split(".")[0];
131
+ const tempOutput = path.join(outputDir, `temp_${group}.pot`);
132
+ // Base arguments
133
+ let args = [
134
+ "--package-name=" + domain,
135
+ options.version ? "--package-version=" + options.version : "",
136
+ "--output=" + tempOutput,
137
+ "--files-from=" + potFile,
138
+ "--from-code=UTF-8",
139
+ "--add-comments",
140
+ ];
141
+ // Add bug report address if specified
142
+ if (options.msgidBugsAddress) {
143
+ args.push("--msgid-bugs-address=" + options.msgidBugsAddress);
144
+ }
145
+ // Add copyright holder if specified
146
+ if (options.copyrightHolder) {
147
+ args.push("--copyright-holder=" + options.copyrightHolder);
148
+ }
149
+ // Add language-specific settings
150
+ switch (group) {
151
+ case "js":
152
+ case "blp":
153
+ args.push("--language=JavaScript");
154
+ args.push(...keywords.map((k) => `--keyword=${k}`));
155
+ if (preset === "glib") {
156
+ args.push(...GLIB_PRESET_ARGS);
157
+ }
158
+ break;
159
+ case "ui":
160
+ args.push("--language=Glade");
161
+ break;
162
+ case "metainfo":
163
+ // Find the first existing metainfo.its file
164
+ const metainfoItsPath = await findMetainfoItsPath();
165
+ if (!metainfoItsPath) {
166
+ console.warn("Warning: Could not find metainfo.its in any of the expected locations");
167
+ // Continue without the ITS file
168
+ }
169
+ else {
170
+ args.push(`--its=${metainfoItsPath}`);
171
+ }
172
+ break;
173
+ case "desktop":
174
+ args.push("--language=Desktop");
175
+ break;
176
+ }
177
+ if (verbose) {
178
+ console.log(`[${pluginName}] Running xgettext for ${group}:`, args.join(" "));
179
+ }
180
+ await execa("xgettext", args);
181
+ // Check if file exists before adding to tempPotFiles
182
+ try {
183
+ await fs.access(tempOutput);
184
+ tempPotFiles.push(tempOutput);
185
+ if (verbose) {
186
+ console.log(`[${pluginName}] Successfully created temporary POT file: ${tempOutput}`);
187
+ }
188
+ }
189
+ catch (error) {
190
+ console.warn(`[${pluginName}] Failed to create temporary POT file: ${tempOutput}`);
191
+ }
192
+ }
193
+ // Combine all temporary POT files using msgcat
194
+ if (tempPotFiles.length > 0) {
195
+ const msgcatArgs = ["--use-first", "-o", output, ...tempPotFiles];
196
+ await execa("msgcat", msgcatArgs);
197
+ // Clean up temporary files
198
+ for (const tempFile of tempPotFiles) {
199
+ await fs.unlink(tempFile);
200
+ }
201
+ for (const potFile of potFiles) {
202
+ await fs.unlink(potFile);
203
+ }
204
+ }
205
+ if (options.autoUpdatePo) {
206
+ await updatePoFiles(options.output, pluginName, options.verbose || false);
207
+ }
208
+ }
209
+ catch (error) {
210
+ throw new Error(`Failed to extract translations: ${error}`);
211
+ }
212
+ }
213
+ async function updatePoFiles(potFile, pluginName, verbose) {
214
+ try {
215
+ const linguasPath = path.join(path.dirname(potFile), "LINGUAS");
216
+ const languages = (await fs.readFile(linguasPath, "utf-8"))
217
+ .split("\n")
218
+ .filter(Boolean);
219
+ for (const lang of languages) {
220
+ const poFile = path.join(path.dirname(potFile), `${lang}.po`);
221
+ if (verbose) {
222
+ console.log(`[${pluginName}] Updating ${poFile}`);
223
+ }
224
+ await execa("msgmerge", ["--update", "--backup=none", poFile, potFile]);
225
+ }
226
+ }
227
+ catch (error) {
228
+ console.error(`[${pluginName}] Error updating PO files:`, error);
229
+ }
230
+ }
231
+ /**
232
+ * Finds the first existing metainfo.its file from installed gettext versions
233
+ * @returns The path to the metainfo.its file if found, otherwise undefined
234
+ */
235
+ async function findMetainfoItsPath() {
236
+ // Default path
237
+ const defaultPath = "/usr/share/gettext/its/metainfo.its";
238
+ // Check default path first
239
+ if (existsSync(defaultPath)) {
240
+ return defaultPath;
241
+ }
242
+ try {
243
+ // Use glob to find all potential gettext version directories
244
+ const getTextDirs = await glob("/usr/share/gettext-*");
245
+ // Sort by version (newest first) if possible
246
+ getTextDirs.sort((a, b) => {
247
+ const versionA = a.replace("/usr/share/gettext-", "");
248
+ const versionB = b.replace("/usr/share/gettext-", "");
249
+ return versionB.localeCompare(versionA);
250
+ });
251
+ // Add specific version paths we know about
252
+ const metainfoItsPaths = getTextDirs.map((dir) => `${dir}/its/metainfo.its`);
253
+ // Find first existing path
254
+ return metainfoItsPaths.find((path) => existsSync(path));
255
+ }
256
+ catch (error) {
257
+ console.warn("Error searching for metainfo.its:", error);
258
+ return undefined;
259
+ }
260
+ }
261
+ //# sourceMappingURL=xgettext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xgettext.js","sourceRoot":"","sources":["../src/xgettext.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@gjsify/vite-plugin-gettext",
3
+ "version": "0.2.0",
4
+ "description": "Vite plugin for compiling Gettext PO files",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "clear": "rm -rf dist",
10
+ "check": "tsc --noEmit",
11
+ "build": "tsc --declaration"
12
+ },
13
+ "author": "Pascal Garber <pascal@mailfreun.de>",
14
+ "license": "MIT",
15
+ "devDependencies": {
16
+ "@types/gettext-parser": "^8.0.0",
17
+ "@types/node": "^24.3.0",
18
+ "gettext-parser": "^8.0.0",
19
+ "typescript": "^5.9.2",
20
+ "vite": "^7.1.2"
21
+ },
22
+ "peerDependencies": {
23
+ "vite": "*"
24
+ },
25
+ "dependencies": {
26
+ "execa": "^9.6.0",
27
+ "fast-glob": "^3.3.3"
28
+ }
29
+ }
package/src/gettext.ts ADDED
@@ -0,0 +1,104 @@
1
+ import { type Plugin } from "vite";
2
+ import { execa } from "execa";
3
+ import path from "node:path";
4
+ import type { GettextPluginOptions } from "./types.js";
5
+ import {
6
+ checkDependencies,
7
+ findAvailableLanguages,
8
+ generateLinguasFile,
9
+ ensureDirectory,
10
+ } from "./utils.js";
11
+
12
+ /**
13
+ * Creates a Vite plugin that compiles PO translation files to binary MO format
14
+ * The MO files are placed in the standard gettext directory structure:
15
+ * {moDirectory}/locale/{lang}/LC_MESSAGES/messages.mo
16
+ * @param options Configuration options for the plugin
17
+ * @returns A Vite plugin that handles PO compilation
18
+ */
19
+ export function gettextPlugin(options: GettextPluginOptions): Plugin {
20
+ const {
21
+ poDirectory,
22
+ moDirectory,
23
+ filename = "messages.mo",
24
+ verbose = false,
25
+ } = options;
26
+
27
+ const pluginName = "vite-plugin-gettext";
28
+
29
+ async function compileMoFiles() {
30
+ try {
31
+ // Check if PO directory exists
32
+ try {
33
+ await ensureDirectory(poDirectory);
34
+ } catch {
35
+ if (verbose) {
36
+ console.log(
37
+ `[${pluginName}] PO directory ${poDirectory} does not exist yet, skipping compilation`
38
+ );
39
+ }
40
+ return;
41
+ }
42
+
43
+ // Find available languages
44
+ const languages = await findAvailableLanguages(
45
+ poDirectory,
46
+ pluginName,
47
+ verbose
48
+ );
49
+
50
+ if (languages.length === 0) {
51
+ if (verbose) {
52
+ console.log(`[${pluginName}] No translation files found`);
53
+ }
54
+ return;
55
+ }
56
+
57
+ // Generate LINGUAS file
58
+ await generateLinguasFile(languages, poDirectory, verbose);
59
+
60
+ // Create MO directory
61
+ await ensureDirectory(path.join(moDirectory, "locale"));
62
+
63
+ for (const lang of languages) {
64
+ const poFile = path.join(poDirectory, `${lang}.po`);
65
+ const moPath = path.join(moDirectory, "locale", lang, "LC_MESSAGES");
66
+ const moFile = path.join(moPath, filename);
67
+
68
+ await ensureDirectory(moPath);
69
+
70
+ if (verbose) {
71
+ console.log(`[${pluginName}] Compiling ${poFile} to ${moFile}`);
72
+ }
73
+
74
+ await execa("msgfmt", ["--output-file=" + moFile, poFile]);
75
+ }
76
+ } catch (error) {
77
+ throw new Error(`Failed to compile MO files: ${error}`);
78
+ }
79
+ }
80
+
81
+ return {
82
+ name: pluginName,
83
+
84
+ async buildStart() {
85
+ await checkDependencies("msgfmt", pluginName, verbose);
86
+ await compileMoFiles();
87
+ },
88
+
89
+ configureServer(server) {
90
+ server.watcher.add(poDirectory);
91
+
92
+ server.watcher.on("change", async (file) => {
93
+ if (file.endsWith(".po")) {
94
+ if (verbose) {
95
+ console.log(
96
+ `[${pluginName}] PO file changed: ${file}, recompiling`
97
+ );
98
+ }
99
+ await compileMoFiles();
100
+ }
101
+ });
102
+ },
103
+ };
104
+ }