@gjsify/vite-plugin-gettext 0.4.27 → 0.4.29

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.
package/lib/gettext.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type Plugin } from "vite";
2
- import type { GettextPluginOptions } from "./types.js";
1
+ import { type Plugin } from 'vite';
2
+ import type { GettextPluginOptions } from './types.js';
3
3
  /**
4
4
  * Creates a Vite plugin that compiles PO translation files to binary MO format
5
5
  * The MO files are placed in the standard gettext directory structure:
package/lib/gettext.js CHANGED
@@ -1,6 +1,6 @@
1
- import { execa } from "execa";
2
- import path from "node:path";
3
- import { checkDependencies, findAvailableLanguages, generateLinguasFile, ensureDirectory, } from "./utils.js";
1
+ import { execa } from 'execa';
2
+ import path from 'node:path';
3
+ import { checkDependencies, findAvailableLanguages, generateLinguasFile, ensureDirectory } from './utils.js';
4
4
  /**
5
5
  * Creates a Vite plugin that compiles PO translation files to binary MO format
6
6
  * The MO files are placed in the standard gettext directory structure:
@@ -9,8 +9,8 @@ import { checkDependencies, findAvailableLanguages, generateLinguasFile, ensureD
9
9
  * @returns A Vite plugin that handles PO compilation
10
10
  */
11
11
  export function gettextPlugin(options) {
12
- const { poDirectory, moDirectory, filename = "messages.mo", verbose = false, } = options;
13
- const pluginName = "vite-plugin-gettext";
12
+ const { poDirectory, moDirectory, filename = 'messages.mo', verbose = false } = options;
13
+ const pluginName = 'vite-plugin-gettext';
14
14
  async function compileMoFiles() {
15
15
  try {
16
16
  // Check if PO directory exists
@@ -34,16 +34,16 @@ export function gettextPlugin(options) {
34
34
  // Generate LINGUAS file
35
35
  await generateLinguasFile(languages, poDirectory, verbose);
36
36
  // Create MO directory
37
- await ensureDirectory(path.join(moDirectory, "locale"));
37
+ await ensureDirectory(path.join(moDirectory, 'locale'));
38
38
  for (const lang of languages) {
39
39
  const poFile = path.join(poDirectory, `${lang}.po`);
40
- const moPath = path.join(moDirectory, "locale", lang, "LC_MESSAGES");
40
+ const moPath = path.join(moDirectory, 'locale', lang, 'LC_MESSAGES');
41
41
  const moFile = path.join(moPath, filename);
42
42
  await ensureDirectory(moPath);
43
43
  if (verbose) {
44
44
  console.log(`[${pluginName}] Compiling ${poFile} to ${moFile}`);
45
45
  }
46
- await execa("msgfmt", ["--output-file=" + moFile, poFile]);
46
+ await execa('msgfmt', ['--output-file=' + moFile, poFile]);
47
47
  }
48
48
  }
49
49
  catch (error) {
@@ -53,13 +53,13 @@ export function gettextPlugin(options) {
53
53
  return {
54
54
  name: pluginName,
55
55
  async buildStart() {
56
- await checkDependencies("msgfmt", pluginName, verbose);
56
+ await checkDependencies('msgfmt', pluginName, verbose);
57
57
  await compileMoFiles();
58
58
  },
59
59
  configureServer(server) {
60
60
  server.watcher.add(poDirectory);
61
- server.watcher.on("change", async (file) => {
62
- if (file.endsWith(".po")) {
61
+ server.watcher.on('change', async (file) => {
62
+ if (file.endsWith('.po')) {
63
63
  if (verbose) {
64
64
  console.log(`[${pluginName}] PO file changed: ${file}, recompiling`);
65
65
  }
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { gettextPlugin } from "./gettext.js";
2
- export { msgfmtPlugin } from "./msgfmt.js";
3
- export { xgettextPlugin } from "./xgettext.js";
4
- export { po2jsonPlugin } from "./po2json.js";
5
- export type { GettextPluginOptions, MsgfmtPluginOptions, MsgfmtFormat, XGettextPluginOptions, } from "./types.js";
6
- export * from "./utils.js";
1
+ export { gettextPlugin } from './gettext.js';
2
+ export { msgfmtPlugin } from './msgfmt.js';
3
+ export { xgettextPlugin } from './xgettext.js';
4
+ export { po2jsonPlugin } from './po2json.js';
5
+ export type { GettextPluginOptions, MsgfmtPluginOptions, MsgfmtFormat, XGettextPluginOptions } from './types.js';
6
+ export * from './utils.js';
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { gettextPlugin } from "./gettext.js";
2
- export { msgfmtPlugin } from "./msgfmt.js";
3
- export { xgettextPlugin } from "./xgettext.js";
4
- export { po2jsonPlugin } from "./po2json.js";
5
- export * from "./utils.js";
1
+ export { gettextPlugin } from './gettext.js';
2
+ export { msgfmtPlugin } from './msgfmt.js';
3
+ export { xgettextPlugin } from './xgettext.js';
4
+ export { po2jsonPlugin } from './po2json.js';
5
+ export * from './utils.js';
package/lib/msgfmt.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type Plugin } from "vite";
2
- import type { MsgfmtPluginOptions } from "./types.js";
1
+ import { type Plugin } from 'vite';
2
+ import type { MsgfmtPluginOptions } from './types.js';
3
3
  /**
4
4
  * Creates a Vite plugin that compiles PO translation files to various formats
5
5
  * Supports metainfo files with special processing
package/lib/msgfmt.js CHANGED
@@ -1,7 +1,7 @@
1
- import { execa } from "execa";
2
- import path from "node:path";
3
- import fs from "node:fs/promises";
4
- import { checkDependencies, findAvailableLanguages, ensureDirectory, } from "./utils.js";
1
+ import { execa } from 'execa';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
4
+ import { checkDependencies, findAvailableLanguages, ensureDirectory } from './utils.js';
5
5
  /**
6
6
  * Remove XML comments from a file content
7
7
  * @param content The XML content as string
@@ -31,27 +31,27 @@ function buildMsgfmtArgs(baseArgs, options) {
31
31
  */
32
32
  function getOutputExtension(format) {
33
33
  switch (format) {
34
- case "mo":
35
- return ".mo";
36
- case "java":
37
- case "java2":
38
- return ".class";
39
- case "csharp":
40
- return ".dll";
41
- case "csharp-resources":
42
- return ".resources.dll";
43
- case "tcl":
44
- return ".msg";
45
- case "desktop":
46
- return ".desktop";
47
- case "xml":
48
- return ".xml";
49
- case "json":
50
- return ".json";
51
- case "qt":
52
- return ".qm";
34
+ case 'mo':
35
+ return '.mo';
36
+ case 'java':
37
+ case 'java2':
38
+ return '.class';
39
+ case 'csharp':
40
+ return '.dll';
41
+ case 'csharp-resources':
42
+ return '.resources.dll';
43
+ case 'tcl':
44
+ return '.msg';
45
+ case 'desktop':
46
+ return '.desktop';
47
+ case 'xml':
48
+ return '.xml';
49
+ case 'json':
50
+ return '.json';
51
+ case 'qt':
52
+ return '.qm';
53
53
  default:
54
- return ".mo";
54
+ return '.mo';
55
55
  }
56
56
  }
57
57
  /**
@@ -61,8 +61,8 @@ function getOutputExtension(format) {
61
61
  * @returns A Vite plugin that handles PO compilation
62
62
  */
63
63
  export function msgfmtPlugin(options) {
64
- const { poDirectory, outputDirectory, domain = "messages", format = "mo", templateFile, verbose = false, msgfmtOptions = [], useLocaleStructure = true, removeComments = true, } = options;
65
- const pluginName = "vite-plugin-msgfmt";
64
+ const { poDirectory, outputDirectory, domain = 'messages', format = 'mo', templateFile, verbose = false, msgfmtOptions = [], useLocaleStructure = true, removeComments = true, } = options;
65
+ const pluginName = 'vite-plugin-msgfmt';
66
66
  async function compilePoFiles() {
67
67
  try {
68
68
  // Check if PO directory exists
@@ -78,7 +78,7 @@ export function msgfmtPlugin(options) {
78
78
  // Create output directory
79
79
  await ensureDirectory(outputDirectory);
80
80
  // For XML format, we can use the bulk mode if a template is provided
81
- if (format === "xml" && templateFile) {
81
+ if (format === 'xml' && templateFile) {
82
82
  // Use bulk mode for XML format
83
83
  const outputFile = path.join(outputDirectory, options.filename || `${domain}${getOutputExtension(format)}`);
84
84
  if (verbose) {
@@ -86,17 +86,17 @@ export function msgfmtPlugin(options) {
86
86
  }
87
87
  // Build arguments for bulk mode
88
88
  const baseArgs = [
89
- "--output-file=" + outputFile,
90
- "--xml",
91
- "--template=" + templateFile,
92
- "-d",
89
+ '--output-file=' + outputFile,
90
+ '--xml',
91
+ '--template=' + templateFile,
92
+ '-d',
93
93
  poDirectory,
94
94
  ];
95
95
  const args = buildMsgfmtArgs(baseArgs, { msgfmtOptions });
96
96
  if (verbose) {
97
- console.log(`[${pluginName}] Running msgfmt with: ${args.join(" ")}`);
97
+ console.log(`[${pluginName}] Running msgfmt with: ${args.join(' ')}`);
98
98
  }
99
- await execa("msgfmt", args);
99
+ await execa('msgfmt', args);
100
100
  // Remove comments from XML output if requested
101
101
  if (removeComments !== false) {
102
102
  try {
@@ -128,9 +128,9 @@ export function msgfmtPlugin(options) {
128
128
  const poFile = path.join(poDirectory, `${lang}.po`);
129
129
  let outputPath;
130
130
  let outputFile;
131
- if (useLocaleStructure && format === "mo") {
131
+ if (useLocaleStructure && format === 'mo') {
132
132
  // Use standard gettext locale structure
133
- outputPath = path.join(outputDirectory, "locale", lang, "LC_MESSAGES");
133
+ outputPath = path.join(outputDirectory, 'locale', lang, 'LC_MESSAGES');
134
134
  outputFile = path.join(outputPath, options.filename || `${domain}${getOutputExtension(format)}`);
135
135
  }
136
136
  else {
@@ -144,16 +144,12 @@ export function msgfmtPlugin(options) {
144
144
  console.log(`[${pluginName}] Compiling ${poFile} to ${outputFile}`);
145
145
  }
146
146
  // Build arguments for individual processing
147
- const baseArgs = [
148
- "--output-file=" + outputFile,
149
- `--${format}`,
150
- poFile
151
- ];
147
+ const baseArgs = ['--output-file=' + outputFile, `--${format}`, poFile];
152
148
  const args = buildMsgfmtArgs(baseArgs, { msgfmtOptions });
153
149
  if (verbose) {
154
- console.log(`[${pluginName}] Running msgfmt with: ${args.join(" ")}`);
150
+ console.log(`[${pluginName}] Running msgfmt with: ${args.join(' ')}`);
155
151
  }
156
- await execa("msgfmt", args);
152
+ await execa('msgfmt', args);
157
153
  }
158
154
  }
159
155
  }
@@ -164,13 +160,13 @@ export function msgfmtPlugin(options) {
164
160
  return {
165
161
  name: pluginName,
166
162
  async buildStart() {
167
- await checkDependencies("msgfmt", pluginName, verbose);
163
+ await checkDependencies('msgfmt', pluginName, verbose);
168
164
  await compilePoFiles();
169
165
  },
170
166
  configureServer(server) {
171
167
  server.watcher.add(poDirectory);
172
- server.watcher.on("change", async (file) => {
173
- if (file.endsWith(".po")) {
168
+ server.watcher.on('change', async (file) => {
169
+ if (file.endsWith('.po')) {
174
170
  if (verbose) {
175
171
  console.log(`[${pluginName}] PO file changed: ${file}, recompiling`);
176
172
  }
package/lib/po2json.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type Plugin } from "vite";
2
- import type { GettextPo2JsonPluginOptions } from "./types.js";
1
+ import { type Plugin } from 'vite';
2
+ import type { GettextPo2JsonPluginOptions } from './types.js';
3
3
  /**
4
4
  * Creates a Vite plugin that converts PO translation files to JSON format
5
5
  * The JSON files are placed in the specified output directory
package/lib/po2json.js CHANGED
@@ -1,7 +1,7 @@
1
- import path from "node:path";
2
- import fs from "node:fs/promises";
3
- import * as gettextParser from "gettext-parser";
4
- import { findAvailableLanguages, ensureDirectory, } from "./utils.js";
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
+ import * as gettextParser from 'gettext-parser';
4
+ import { findAvailableLanguages, ensureDirectory } from './utils.js';
5
5
  /**
6
6
  * Simplifies the gettext-parser output to a clean key-value object
7
7
  * where the key is the original text and the value is the translation
@@ -15,7 +15,7 @@ function simplifyTranslations(translations) {
15
15
  const contextTranslations = translations.translations[context];
16
16
  // Skip the header (empty msgid)
17
17
  Object.keys(contextTranslations).forEach((key) => {
18
- if (key === "")
18
+ if (key === '')
19
19
  return;
20
20
  const translation = contextTranslations[key];
21
21
  // Get the original text (msgid)
@@ -23,7 +23,7 @@ function simplifyTranslations(translations) {
23
23
  // Get the translated text (first item in msgstr array)
24
24
  const translated = translation.msgstr[0];
25
25
  // Only add the translation if it exists and is not empty
26
- if (translated && translated.trim() !== "") {
26
+ if (translated && translated.trim() !== '') {
27
27
  result[original] = translated;
28
28
  }
29
29
  });
@@ -82,8 +82,8 @@ async function createDefaultLanguageJson(jsonDirectory, allTranslations, default
82
82
  * @returns A Vite plugin that handles PO to JSON conversion
83
83
  */
84
84
  export function po2jsonPlugin(options) {
85
- const { poDirectory, jsonDirectory, defaultLanguage = "en", verbose = false, additionalTranslations = {}, } = options;
86
- const pluginName = "vite-plugin-gettext-po2json";
85
+ const { poDirectory, jsonDirectory, defaultLanguage = 'en', verbose = false, additionalTranslations = {}, } = options;
86
+ const pluginName = 'vite-plugin-gettext-po2json';
87
87
  async function convertPoToJson() {
88
88
  try {
89
89
  // Check if PO directory exists
@@ -175,8 +175,8 @@ export function po2jsonPlugin(options) {
175
175
  },
176
176
  configureServer(server) {
177
177
  server.watcher.add(poDirectory);
178
- server.watcher.on("change", async (file) => {
179
- if (file.endsWith(".po")) {
178
+ server.watcher.on('change', async (file) => {
179
+ if (file.endsWith('.po')) {
180
180
  if (verbose) {
181
181
  console.log(`[${pluginName}] PO file changed: ${file}, reconverting`);
182
182
  }
package/lib/types.d.ts CHANGED
@@ -22,7 +22,7 @@ export interface XGettextPluginOptions {
22
22
  /** Version of the POT file, defaults to '1.0' */
23
23
  version?: string;
24
24
  /** Preset to use for extracting strings, defaults to 'glib' */
25
- preset?: "glib";
25
+ preset?: 'glib';
26
26
  /** URL for reporting bugs in the POT file */
27
27
  msgidBugsAddress?: string;
28
28
  /** Copyright holder to set in the POT file */
@@ -59,7 +59,7 @@ export interface GettextPluginOptions {
59
59
  /**
60
60
  * Output format types for msgfmt
61
61
  */
62
- export type MsgfmtFormat = "mo" | "java" | "java2" | "csharp" | "csharp-resources" | "tcl" | "desktop" | "xml" | "json" | "qt";
62
+ export type MsgfmtFormat = 'mo' | 'java' | 'java2' | 'csharp' | 'csharp-resources' | 'tcl' | 'desktop' | 'xml' | 'json' | 'qt';
63
63
  /**
64
64
  * Configuration options for the msgfmt plugin
65
65
  * Used to compile PO files to various formats including binary MO
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
- import { execa } from "execa";
2
- import path from "node:path";
3
- import fs from "node:fs/promises";
1
+ import { execa } from 'execa';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
4
4
  /**
5
5
  * Checks if a gettext utility is installed and available
6
6
  * @param command The command to check (msgfmt, xgettext, etc.)
@@ -10,17 +10,17 @@ import fs from "node:fs/promises";
10
10
  */
11
11
  export async function checkDependencies(command, pluginName, verbose) {
12
12
  try {
13
- await execa(command, ["--version"]);
13
+ await execa(command, ['--version']);
14
14
  if (verbose) {
15
15
  console.log(`[${pluginName}] Found ${command}`);
16
16
  }
17
17
  }
18
- catch (error) {
18
+ catch (_error) {
19
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");
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
24
  }
25
25
  }
26
26
  /**
@@ -33,15 +33,13 @@ export async function checkDependencies(command, pluginName, verbose) {
33
33
  export async function findAvailableLanguages(poDirectory, pluginName, verbose) {
34
34
  try {
35
35
  const files = await fs.readdir(poDirectory);
36
- const languages = files
37
- .filter((file) => file.endsWith(".po"))
38
- .map((file) => path.basename(file, ".po"));
36
+ const languages = files.filter((file) => file.endsWith('.po')).map((file) => path.basename(file, '.po'));
39
37
  if (verbose) {
40
- console.log(`[${pluginName}] Found languages: ${languages.join(", ")}`);
38
+ console.log(`[${pluginName}] Found languages: ${languages.join(', ')}`);
41
39
  }
42
40
  return languages;
43
41
  }
44
- catch (error) {
42
+ catch (_error) {
45
43
  if (verbose) {
46
44
  console.log(`[${pluginName}] No PO directory found at ${poDirectory}`);
47
45
  }
@@ -55,16 +53,16 @@ export async function findAvailableLanguages(poDirectory, pluginName, verbose) {
55
53
  * @param verbose Enable verbose logging
56
54
  */
57
55
  export async function generateLinguasFile(languages, poDirectory, verbose = false) {
58
- const linguasPath = path.join(poDirectory, "LINGUAS");
59
- const content = languages.join("\n");
56
+ const linguasPath = path.join(poDirectory, 'LINGUAS');
57
+ const content = languages.join('\n');
60
58
  try {
61
59
  await fs.writeFile(linguasPath, content);
62
60
  if (verbose) {
63
- console.log(`Generated LINGUAS file with languages: ${languages.join(", ")}`);
61
+ console.log(`Generated LINGUAS file with languages: ${languages.join(', ')}`);
64
62
  }
65
63
  }
66
64
  catch (error) {
67
- console.error("Error writing LINGUAS file:", error);
65
+ console.error('Error writing LINGUAS file:', error);
68
66
  }
69
67
  }
70
68
  /**
@@ -85,7 +83,7 @@ export function processFilename(filePath) {
85
83
  let extension = path.extname(filename).toLowerCase();
86
84
  let processedFilename = filename;
87
85
  // Handle .in extension
88
- if (filename.endsWith(".in")) {
86
+ if (filename.endsWith('.in')) {
89
87
  processedFilename = filename.substring(0, filename.length - 3);
90
88
  extension = path.extname(processedFilename).toLowerCase();
91
89
  }
package/lib/xgettext.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type Plugin } from "vite";
2
- import type { XGettextPluginOptions } from "./types.js";
1
+ import { type Plugin } from 'vite';
2
+ import type { XGettextPluginOptions } from './types.js';
3
3
  /**
4
4
  * Creates a Vite plugin that extracts translatable strings from source files
5
5
  * Uses GNU xgettext to generate a POT template file that can be used as basis for translations
package/lib/xgettext.js CHANGED
@@ -1,39 +1,39 @@
1
- import { execa } from "execa";
2
- import path from "node:path";
3
- import fs from "node:fs/promises";
4
- import { existsSync } from "node:fs";
5
- import glob from "fast-glob";
6
- import { checkDependencies, ensureDirectory, processFilename, } from "./utils.js";
1
+ import { execa } from 'execa';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
4
+ import { existsSync } from 'node:fs';
5
+ import glob from 'fast-glob';
6
+ import { checkDependencies, ensureDirectory, processFilename } from './utils.js';
7
7
  // Add GLib preset constants
8
8
  // From https://github.com/mesonbuild/meson/blob/467da051c859ba3112803b035e317bddadd756ef/mesonbuild/modules/i18n.py
9
9
  const GLIB_PRESET_ARGS = [
10
- "--from-code=UTF-8",
11
- "--add-comments",
10
+ '--from-code=UTF-8',
11
+ '--add-comments',
12
12
  // https://developer.gnome.org/glib/stable/glib-I18N.html
13
- "--keyword=_",
14
- "--keyword=N_",
15
- "--keyword=C_:1c,2",
16
- "--keyword=NC_:1c,2",
17
- "--keyword=g_dcgettext:2",
18
- "--keyword=g_dngettext:2,3",
19
- "--keyword=g_dpgettext2:2c,3",
20
- "--flag=N_:1:pass-c-format",
21
- "--flag=C_:2:pass-c-format",
22
- "--flag=NC_:2:pass-c-format",
23
- "--flag=g_dngettext:2:pass-c-format",
24
- "--flag=g_strdup_printf:1:c-format",
25
- "--flag=g_string_printf:2:c-format",
26
- "--flag=g_string_append_printf:2:c-format",
27
- "--flag=g_error_new:3:c-format",
28
- "--flag=g_set_error:4:c-format",
29
- "--flag=g_markup_printf_escaped:1:c-format",
30
- "--flag=g_log:3:c-format",
31
- "--flag=g_print:1:c-format",
32
- "--flag=g_printerr:1:c-format",
33
- "--flag=g_printf:1:c-format",
34
- "--flag=g_fprintf:2:c-format",
35
- "--flag=g_sprintf:2:c-format",
36
- "--flag=g_snprintf:3:c-format",
13
+ '--keyword=_',
14
+ '--keyword=N_',
15
+ '--keyword=C_:1c,2',
16
+ '--keyword=NC_:1c,2',
17
+ '--keyword=g_dcgettext:2',
18
+ '--keyword=g_dngettext:2,3',
19
+ '--keyword=g_dpgettext2:2c,3',
20
+ '--flag=N_:1:pass-c-format',
21
+ '--flag=C_:2:pass-c-format',
22
+ '--flag=NC_:2:pass-c-format',
23
+ '--flag=g_dngettext:2:pass-c-format',
24
+ '--flag=g_strdup_printf:1:c-format',
25
+ '--flag=g_string_printf:2:c-format',
26
+ '--flag=g_string_append_printf:2:c-format',
27
+ '--flag=g_error_new:3:c-format',
28
+ '--flag=g_set_error:4:c-format',
29
+ '--flag=g_markup_printf_escaped:1:c-format',
30
+ '--flag=g_log:3:c-format',
31
+ '--flag=g_print:1:c-format',
32
+ '--flag=g_printerr:1:c-format',
33
+ '--flag=g_printf:1:c-format',
34
+ '--flag=g_fprintf:2:c-format',
35
+ '--flag=g_sprintf:2:c-format',
36
+ '--flag=g_snprintf:3:c-format',
37
37
  ];
38
38
  /**
39
39
  * Build command arguments with common options
@@ -45,17 +45,17 @@ function buildCommandArgs(baseArgs, options) {
45
45
  const args = [...baseArgs];
46
46
  // Check if additional options already contain the flags to avoid duplicates
47
47
  const additionalOptions = options.additionalOptions || [];
48
- const hasNoLocation = additionalOptions.includes("--no-location");
49
- const hasNoWrap = additionalOptions.includes("--no-wrap");
50
- const hasSortOutput = additionalOptions.includes("--sort-output");
48
+ const hasNoLocation = additionalOptions.includes('--no-location');
49
+ const hasNoWrap = additionalOptions.includes('--no-wrap');
50
+ const hasSortOutput = additionalOptions.includes('--sort-output');
51
51
  if (options.noLocation && !hasNoLocation) {
52
- args.push("--no-location");
52
+ args.push('--no-location');
53
53
  }
54
54
  if (options.noWrap && !hasNoWrap) {
55
- args.push("--no-wrap");
55
+ args.push('--no-wrap');
56
56
  }
57
57
  if (options.sortOutput && !hasSortOutput) {
58
- args.push("--sort-output");
58
+ args.push('--sort-output');
59
59
  }
60
60
  if (additionalOptions.length > 0) {
61
61
  args.push(...additionalOptions);
@@ -69,17 +69,17 @@ function buildCommandArgs(baseArgs, options) {
69
69
  * @returns A Vite plugin that handles string extraction
70
70
  */
71
71
  export function xgettextPlugin(options) {
72
- const pluginName = "vite-plugin-xgettext";
72
+ const pluginName = 'vite-plugin-xgettext';
73
73
  return {
74
74
  name: pluginName,
75
75
  async buildStart() {
76
- await checkDependencies("xgettext", pluginName, options.verbose ?? false);
76
+ await checkDependencies('xgettext', pluginName, options.verbose ?? false);
77
77
  const files = await glob(options.sources);
78
78
  await extractStrings(files, options, pluginName);
79
79
  },
80
80
  configureServer(server) {
81
81
  server.watcher.add(options.sources);
82
- server.watcher.on("change", async (file) => {
82
+ server.watcher.on('change', async (file) => {
83
83
  if (options.sources.some((pattern) => file.match(pattern))) {
84
84
  if (options.verbose) {
85
85
  console.log(`[${pluginName}] Source file changed: ${file}, re-running extraction`);
@@ -106,7 +106,7 @@ async function generatePotfiles(files, outputDir, pluginName, verbose = false) {
106
106
  const potFiles = [];
107
107
  for (const [group, groupFiles] of fileGroups) {
108
108
  const potfilePath = path.join(outputDir, `${group}.POTFILES`);
109
- const content = groupFiles.join("\n");
109
+ const content = groupFiles.join('\n');
110
110
  try {
111
111
  await fs.writeFile(potfilePath, content);
112
112
  potFiles.push(potfilePath);
@@ -124,27 +124,27 @@ function getFileGroup(fullFilename) {
124
124
  // Process filename to handle .in extension
125
125
  const { filename, extension } = processFilename(fullFilename);
126
126
  // Special handling for metainfo.xml files
127
- if (filename.endsWith(".metainfo.xml") || filename.endsWith(".appdata.xml")) {
128
- return "metainfo";
127
+ if (filename.endsWith('.metainfo.xml') || filename.endsWith('.appdata.xml')) {
128
+ return 'metainfo';
129
129
  }
130
130
  switch (extension) {
131
- case ".ts":
132
- case ".js":
133
- case ".tsx":
134
- return "js";
135
- case ".ui":
136
- case ".xml":
137
- return "ui";
138
- case ".blp":
139
- return "blp";
140
- case ".desktop":
141
- return "desktop";
131
+ case '.ts':
132
+ case '.js':
133
+ case '.tsx':
134
+ return 'js';
135
+ case '.ui':
136
+ case '.xml':
137
+ return 'ui';
138
+ case '.blp':
139
+ return 'blp';
140
+ case '.desktop':
141
+ return 'desktop';
142
142
  default:
143
- return "other";
143
+ return 'other';
144
144
  }
145
145
  }
146
146
  async function extractStrings(files, options, pluginName) {
147
- const { output, domain = "messages", keywords = [], preset, verbose = false, } = options;
147
+ const { output, domain = 'messages', keywords = [], preset, verbose = false } = options;
148
148
  const noWrap = options.noWrap || false;
149
149
  try {
150
150
  const outputDir = path.dirname(output);
@@ -152,7 +152,7 @@ async function extractStrings(files, options, pluginName) {
152
152
  // Read existing POT-Creation-Date from previous POT if present (for preservation)
153
153
  let prevPotCreationDate;
154
154
  try {
155
- const existingPot = await fs.readFile(output, "utf-8");
155
+ const existingPot = await fs.readFile(output, 'utf-8');
156
156
  const m = existingPot.match(/"POT-Creation-Date:\s*([^\n]+)\\n"/);
157
157
  if (m && m[1]) {
158
158
  prevPotCreationDate = m[1];
@@ -169,71 +169,69 @@ async function extractStrings(files, options, pluginName) {
169
169
  // Create temporary POT files for each group
170
170
  const tempPotFiles = [];
171
171
  for (const potFile of potFiles) {
172
- const group = path.basename(potFile).split(".")[0];
172
+ const group = path.basename(potFile).split('.')[0];
173
173
  const tempOutput = path.join(outputDir, `temp_${group}.pot`);
174
174
  // Build base arguments
175
175
  const baseArgs = [
176
- "--package-name=" + domain,
177
- options.version ? "--package-version=" + options.version : "",
178
- "--output=" + tempOutput,
179
- "--files-from=" + potFile,
180
- "--from-code=UTF-8",
181
- "--add-comments",
176
+ '--package-name=' + domain,
177
+ options.version ? '--package-version=' + options.version : '',
178
+ '--output=' + tempOutput,
179
+ '--files-from=' + potFile,
180
+ '--from-code=UTF-8',
181
+ '--add-comments',
182
182
  ].filter(Boolean);
183
183
  // Add bug report address if specified
184
184
  if (options.msgidBugsAddress) {
185
- baseArgs.push("--msgid-bugs-address=" + options.msgidBugsAddress);
185
+ baseArgs.push('--msgid-bugs-address=' + options.msgidBugsAddress);
186
186
  }
187
187
  // Add copyright holder if specified
188
188
  if (options.copyrightHolder) {
189
- baseArgs.push("--copyright-holder=" + options.copyrightHolder);
189
+ baseArgs.push('--copyright-holder=' + options.copyrightHolder);
190
190
  }
191
191
  // Add language-specific settings
192
192
  switch (group) {
193
- case "js":
194
- case "blp":
195
- baseArgs.push("--language=JavaScript");
193
+ case 'js':
194
+ case 'blp':
195
+ baseArgs.push('--language=JavaScript');
196
196
  baseArgs.push(...keywords.map((k) => `--keyword=${k}`));
197
- if (preset === "glib") {
197
+ if (preset === 'glib') {
198
198
  baseArgs.push(...GLIB_PRESET_ARGS);
199
199
  }
200
200
  break;
201
- case "ui":
202
- baseArgs.push("--language=Glade");
201
+ case 'ui':
202
+ baseArgs.push('--language=Glade');
203
203
  break;
204
- case "metainfo":
204
+ case 'metainfo':
205
205
  // Find the first existing metainfo.its file
206
206
  const metainfoItsPath = await findMetainfoItsPath();
207
207
  if (!metainfoItsPath) {
208
- console.warn("Warning: Could not find metainfo.its in any of the expected locations");
208
+ console.warn('Warning: Could not find metainfo.its in any of the expected locations');
209
209
  // Continue without the ITS file
210
210
  }
211
211
  else {
212
212
  baseArgs.push(`--its=${metainfoItsPath}`);
213
213
  }
214
214
  break;
215
- case "desktop":
216
- baseArgs.push("--language=Desktop");
215
+ case 'desktop':
216
+ baseArgs.push('--language=Desktop');
217
217
  break;
218
218
  }
219
219
  // Build final arguments with options handling
220
220
  const args = buildCommandArgs(baseArgs, {
221
221
  noLocation: options.noLocation,
222
222
  noWrap,
223
- additionalOptions: options.xgettextOptions
223
+ additionalOptions: options.xgettextOptions,
224
224
  });
225
225
  if (verbose) {
226
- console.log(`[${pluginName}] Running xgettext for ${group}:`, args.join(" "));
226
+ console.log(`[${pluginName}] Running xgettext for ${group}:`, args.join(' '));
227
227
  }
228
228
  // Enforce deterministic timestamps if requested
229
229
  const env = { ...process.env };
230
230
  if (options.deterministic) {
231
- const epoch = typeof options.sourceDateEpoch === "number"
232
- ? options.sourceDateEpoch
233
- : 0;
231
+ const epoch = typeof options.sourceDateEpoch === 'number' ? options.sourceDateEpoch : 0;
234
232
  env.SOURCE_DATE_EPOCH = String(epoch);
235
233
  }
236
- await execa("xgettext", args, { env });
234
+ await execa('xgettext', args, { env });
237
235
  // Check if file exists before adding to tempPotFiles
238
236
  try {
239
237
  await fs.access(tempOutput);
@@ -242,27 +240,25 @@ async function extractStrings(files, options, pluginName) {
242
240
  console.log(`[${pluginName}] Successfully created temporary POT file: ${tempOutput}`);
243
241
  }
244
242
  }
245
- catch (error) {
243
+ catch (_error) {
246
244
  console.warn(`[${pluginName}] Failed to create temporary POT file: ${tempOutput}`);
247
245
  }
248
246
  }
249
247
  // Combine all temporary POT files using msgcat
250
248
  if (tempPotFiles.length > 0) {
251
- const baseMsgcatArgs = ["--use-first", "-o", output, ...tempPotFiles];
249
+ const baseMsgcatArgs = ['--use-first', '-o', output, ...tempPotFiles];
252
250
  const msgcatArgs = buildCommandArgs(baseMsgcatArgs, {
253
251
  noLocation: options.noLocation,
254
252
  sortOutput: options.sortOutput,
255
253
  noWrap,
256
- additionalOptions: options.msgcatOptions
254
+ additionalOptions: options.msgcatOptions,
257
255
  });
258
256
  const env = { ...process.env };
259
257
  if (options.deterministic) {
260
- const epoch = typeof options.sourceDateEpoch === "number"
261
- ? options.sourceDateEpoch
262
- : 0;
258
+ const epoch = typeof options.sourceDateEpoch === 'number' ? options.sourceDateEpoch : 0;
263
259
  env.SOURCE_DATE_EPOCH = String(epoch);
264
260
  }
265
- await execa("msgcat", msgcatArgs, { env });
261
+ await execa('msgcat', msgcatArgs, { env });
266
262
  // Clean up temporary files
267
263
  for (const tempFile of tempPotFiles) {
268
264
  await fs.unlink(tempFile);
@@ -287,10 +283,10 @@ async function extractStrings(files, options, pluginName) {
287
283
  }
288
284
  }
289
285
  if (!normalizedDate && options.deterministic) {
290
- normalizedDate = formatSourceDateEpoch(typeof options.sourceDateEpoch === "number" ? options.sourceDateEpoch : 0);
286
+ normalizedDate = formatSourceDateEpoch(typeof options.sourceDateEpoch === 'number' ? options.sourceDateEpoch : 0);
291
287
  }
292
288
  if (normalizedDate) {
293
- const content = await fs.readFile(output, "utf-8");
289
+ const content = await fs.readFile(output, 'utf-8');
294
290
  const replaced = content.replace(/^"POT-Creation-Date: .*\\n"$/m, `"POT-Creation-Date: ${normalizedDate}\\n"`);
295
291
  if (replaced !== content) {
296
292
  await fs.writeFile(output, replaced);
@@ -314,33 +310,29 @@ async function extractStrings(files, options, pluginName) {
314
310
  }
315
311
  async function updatePoFiles(potFile, pluginName, verbose, options) {
316
312
  try {
317
- const linguasPath = path.join(path.dirname(potFile), "LINGUAS");
318
- const languages = (await fs.readFile(linguasPath, "utf-8"))
319
- .split("\n")
320
- .filter(Boolean);
313
+ const linguasPath = path.join(path.dirname(potFile), 'LINGUAS');
314
+ const languages = (await fs.readFile(linguasPath, 'utf-8')).split('\n').filter(Boolean);
321
315
  for (const lang of languages) {
322
316
  const poFile = path.join(path.dirname(potFile), `${lang}.po`);
323
317
  if (verbose) {
324
318
  console.log(`[${pluginName}] Updating ${poFile}`);
325
319
  }
326
- const baseMsgmergeArgs = ["--update", "--backup=none", poFile, potFile];
320
+ const baseMsgmergeArgs = ['--update', '--backup=none', poFile, potFile];
327
321
  const args = buildCommandArgs(baseMsgmergeArgs, {
328
322
  noLocation: options.noLocation,
329
- noWrap: options.noWrap
323
+ noWrap: options.noWrap,
330
324
  });
331
325
  const env = { ...process.env };
332
326
  if (options.deterministic) {
333
- const epoch = typeof options.sourceDateEpoch === "number"
334
- ? options.sourceDateEpoch
335
- : 0;
327
+ const epoch = typeof options.sourceDateEpoch === 'number' ? options.sourceDateEpoch : 0;
336
328
  env.SOURCE_DATE_EPOCH = String(epoch);
337
329
  }
338
- await execa("msgmerge", args, { env });
330
+ await execa('msgmerge', args, { env });
339
331
  // Post-process with msgcat to unwrap existing wrapped lines
340
332
  if (options.noWrap) {
341
- const tempFile = poFile + ".tmp";
342
- const msgcatArgs = ["--width=0", "--no-wrap", "-o", tempFile, poFile];
343
- await execa("msgcat", msgcatArgs, { env });
333
+ const tempFile = poFile + '.tmp';
334
+ const msgcatArgs = ['--width=0', '--no-wrap', '-o', tempFile, poFile];
335
+ await execa('msgcat', msgcatArgs, { env });
344
336
  await fs.rename(tempFile, poFile);
345
337
  if (verbose) {
346
338
  console.log(`[${pluginName}] Unwrapped lines in ${poFile}`);
@@ -358,7 +350,7 @@ async function updatePoFiles(potFile, pluginName, verbose, options) {
358
350
  */
359
351
  function formatSourceDateEpoch(epochSeconds) {
360
352
  const date = new Date(epochSeconds * 1000);
361
- const pad = (n) => String(n).padStart(2, "0");
353
+ const pad = (n) => String(n).padStart(2, '0');
362
354
  const year = date.getUTCFullYear();
363
355
  const month = pad(date.getUTCMonth() + 1);
364
356
  const day = pad(date.getUTCDate());
@@ -372,18 +364,18 @@ function formatSourceDateEpoch(epochSeconds) {
372
364
  */
373
365
  async function findMetainfoItsPath() {
374
366
  // Default path
375
- const defaultPath = "/usr/share/gettext/its/metainfo.its";
367
+ const defaultPath = '/usr/share/gettext/its/metainfo.its';
376
368
  // Check default path first
377
369
  if (existsSync(defaultPath)) {
378
370
  return defaultPath;
379
371
  }
380
372
  try {
381
373
  // Use glob to find all potential gettext version directories
382
- const getTextDirs = await glob("/usr/share/gettext-*");
374
+ const getTextDirs = await glob('/usr/share/gettext-*');
383
375
  // Sort by version (newest first) if possible
384
376
  getTextDirs.sort((a, b) => {
385
- const versionA = a.replace("/usr/share/gettext-", "");
386
- const versionB = b.replace("/usr/share/gettext-", "");
377
+ const versionA = a.replace('/usr/share/gettext-', '');
378
+ const versionB = b.replace('/usr/share/gettext-', '');
387
379
  return versionB.localeCompare(versionA);
388
380
  });
389
381
  // Add specific version paths we know about
@@ -392,7 +384,7 @@ async function findMetainfoItsPath() {
392
384
  return metainfoItsPaths.find((path) => existsSync(path));
393
385
  }
394
386
  catch (error) {
395
- console.warn("Error searching for metainfo.its:", error);
387
+ console.warn('Error searching for metainfo.its:', error);
396
388
  return undefined;
397
389
  }
398
390
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/vite-plugin-gettext",
3
- "version": "0.4.27",
3
+ "version": "0.4.29",
4
4
  "description": "Gettext PO/MO/JSON pipeline for Vite / Rollup / Rolldown — extract via xgettext, compile via msgfmt, JSON for browser targets.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",