@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.
- package/README.md +154 -0
- package/dist/gettext.d.ts +10 -0
- package/dist/gettext.js +73 -0
- package/dist/gettext.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/msgfmt.d.ts +9 -0
- package/dist/msgfmt.js +147 -0
- package/dist/msgfmt.js.map +1 -0
- package/dist/po2json.d.ts +9 -0
- package/dist/po2json.js +190 -0
- package/dist/po2json.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +37 -0
- package/dist/utils.js +94 -0
- package/dist/utils.js.map +1 -0
- package/dist/xgettext.d.ts +9 -0
- package/dist/xgettext.js +261 -0
- package/dist/xgettext.js.map +1 -0
- package/package.json +29 -0
- package/src/gettext.ts +104 -0
- package/src/index.ts +11 -0
- package/src/msgfmt.ts +209 -0
- package/src/po2json.ts +281 -0
- package/src/types.ts +120 -0
- package/src/utils.ts +119 -0
- package/src/xgettext.ts +337 -0
- package/tsconfig.json +22 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
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 {
|
|
6
|
+
GettextPluginOptions,
|
|
7
|
+
MsgfmtPluginOptions,
|
|
8
|
+
MsgfmtFormat,
|
|
9
|
+
XGettextPluginOptions,
|
|
10
|
+
} from "./types.js";
|
|
11
|
+
export * from "./utils.js";
|
package/src/msgfmt.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import type { MsgfmtPluginOptions, MsgfmtFormat } from "./types.js";
|
|
5
|
+
import {
|
|
6
|
+
checkDependencies,
|
|
7
|
+
findAvailableLanguages,
|
|
8
|
+
ensureDirectory,
|
|
9
|
+
} from "./utils.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get output file extension based on the format
|
|
13
|
+
* @param format The output format
|
|
14
|
+
* @returns The file extension for the given format
|
|
15
|
+
*/
|
|
16
|
+
function getOutputExtension(format: MsgfmtFormat): string {
|
|
17
|
+
switch (format) {
|
|
18
|
+
case "mo":
|
|
19
|
+
return ".mo";
|
|
20
|
+
case "java":
|
|
21
|
+
case "java2":
|
|
22
|
+
return ".class";
|
|
23
|
+
case "csharp":
|
|
24
|
+
return ".dll";
|
|
25
|
+
case "csharp-resources":
|
|
26
|
+
return ".resources.dll";
|
|
27
|
+
case "tcl":
|
|
28
|
+
return ".msg";
|
|
29
|
+
case "desktop":
|
|
30
|
+
return ".desktop";
|
|
31
|
+
case "xml":
|
|
32
|
+
return ".xml";
|
|
33
|
+
case "json":
|
|
34
|
+
return ".json";
|
|
35
|
+
case "qt":
|
|
36
|
+
return ".qm";
|
|
37
|
+
default:
|
|
38
|
+
return ".mo";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a Vite plugin that compiles PO translation files to various formats
|
|
44
|
+
* Supports metainfo files with special processing
|
|
45
|
+
* @param options Configuration options for the plugin
|
|
46
|
+
* @returns A Vite plugin that handles PO compilation
|
|
47
|
+
*/
|
|
48
|
+
export function msgfmtPlugin(options: MsgfmtPluginOptions): Plugin {
|
|
49
|
+
const {
|
|
50
|
+
poDirectory,
|
|
51
|
+
outputDirectory,
|
|
52
|
+
domain = "messages",
|
|
53
|
+
format = "mo",
|
|
54
|
+
templateFile,
|
|
55
|
+
verbose = false,
|
|
56
|
+
msgfmtOptions = [],
|
|
57
|
+
useLocaleStructure = true,
|
|
58
|
+
} = options;
|
|
59
|
+
|
|
60
|
+
const pluginName = "vite-plugin-msgfmt";
|
|
61
|
+
|
|
62
|
+
async function compilePoFiles() {
|
|
63
|
+
try {
|
|
64
|
+
// Check if PO directory exists
|
|
65
|
+
try {
|
|
66
|
+
await ensureDirectory(poDirectory);
|
|
67
|
+
} catch {
|
|
68
|
+
if (verbose) {
|
|
69
|
+
console.log(
|
|
70
|
+
`[${pluginName}] PO directory ${poDirectory} does not exist yet, skipping compilation`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Create output directory
|
|
77
|
+
await ensureDirectory(outputDirectory);
|
|
78
|
+
|
|
79
|
+
// For XML format, we can use the bulk mode if a template is provided
|
|
80
|
+
if (format === "xml" && templateFile) {
|
|
81
|
+
// Use bulk mode for XML format
|
|
82
|
+
const outputFile = path.join(
|
|
83
|
+
outputDirectory,
|
|
84
|
+
options.filename || `${domain}${getOutputExtension(format)}`
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (verbose) {
|
|
88
|
+
console.log(
|
|
89
|
+
`[${pluginName}] Compiling all languages to ${outputFile} using bulk mode`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Base arguments for bulk mode
|
|
94
|
+
const args = [
|
|
95
|
+
"--output-file=" + outputFile,
|
|
96
|
+
"--xml",
|
|
97
|
+
"--template=" + templateFile,
|
|
98
|
+
"-d",
|
|
99
|
+
poDirectory,
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
// Add any additional options
|
|
103
|
+
args.push(...msgfmtOptions);
|
|
104
|
+
|
|
105
|
+
if (verbose) {
|
|
106
|
+
console.log(`[${pluginName}] Running msgfmt with: ${args.join(" ")}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await execa("msgfmt", args);
|
|
110
|
+
} else {
|
|
111
|
+
// Find available languages for individual processing
|
|
112
|
+
const languages = await findAvailableLanguages(
|
|
113
|
+
poDirectory,
|
|
114
|
+
pluginName,
|
|
115
|
+
verbose
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (languages.length === 0) {
|
|
119
|
+
if (verbose) {
|
|
120
|
+
console.log(`[${pluginName}] No translation files found`);
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Process each language individually for other formats
|
|
126
|
+
for (const lang of languages) {
|
|
127
|
+
const poFile = path.join(poDirectory, `${lang}.po`);
|
|
128
|
+
|
|
129
|
+
let outputPath: string;
|
|
130
|
+
let outputFile: string;
|
|
131
|
+
|
|
132
|
+
if (useLocaleStructure && format === "mo") {
|
|
133
|
+
// Use standard gettext locale structure
|
|
134
|
+
outputPath = path.join(
|
|
135
|
+
outputDirectory,
|
|
136
|
+
"locale",
|
|
137
|
+
lang,
|
|
138
|
+
"LC_MESSAGES"
|
|
139
|
+
);
|
|
140
|
+
outputFile = path.join(
|
|
141
|
+
outputPath,
|
|
142
|
+
options.filename || `${domain}${getOutputExtension(format)}`
|
|
143
|
+
);
|
|
144
|
+
} else {
|
|
145
|
+
// Use simple language-based structure
|
|
146
|
+
outputPath = path.join(outputDirectory, lang);
|
|
147
|
+
outputFile = path.join(
|
|
148
|
+
outputPath,
|
|
149
|
+
options.filename || `${domain}${getOutputExtension(format)}`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Create the directory structure
|
|
154
|
+
await ensureDirectory(outputPath);
|
|
155
|
+
|
|
156
|
+
if (verbose) {
|
|
157
|
+
console.log(`[${pluginName}] Compiling ${poFile} to ${outputFile}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Base arguments
|
|
161
|
+
const args = ["--output-file=" + outputFile];
|
|
162
|
+
|
|
163
|
+
// Add format-specific arguments
|
|
164
|
+
args.push(`--${format}`);
|
|
165
|
+
|
|
166
|
+
// Add any additional options
|
|
167
|
+
args.push(...msgfmtOptions);
|
|
168
|
+
|
|
169
|
+
// Add the input PO file
|
|
170
|
+
args.push(poFile);
|
|
171
|
+
|
|
172
|
+
if (verbose) {
|
|
173
|
+
console.log(
|
|
174
|
+
`[${pluginName}] Running msgfmt with: ${args.join(" ")}`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await execa("msgfmt", args);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
throw new Error(`Failed to compile files: ${error}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
name: pluginName,
|
|
188
|
+
|
|
189
|
+
async buildStart() {
|
|
190
|
+
await checkDependencies("msgfmt", pluginName, verbose);
|
|
191
|
+
await compilePoFiles();
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
configureServer(server) {
|
|
195
|
+
server.watcher.add(poDirectory);
|
|
196
|
+
|
|
197
|
+
server.watcher.on("change", async (file) => {
|
|
198
|
+
if (file.endsWith(".po")) {
|
|
199
|
+
if (verbose) {
|
|
200
|
+
console.log(
|
|
201
|
+
`[${pluginName}] PO file changed: ${file}, recompiling`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
await compilePoFiles();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
package/src/po2json.ts
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import * as gettextParser from "gettext-parser";
|
|
5
|
+
import type { GettextPo2JsonPluginOptions } from "./types.js";
|
|
6
|
+
import {
|
|
7
|
+
checkDependencies,
|
|
8
|
+
findAvailableLanguages,
|
|
9
|
+
ensureDirectory,
|
|
10
|
+
} from "./utils.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Simplifies the gettext-parser output to a clean key-value object
|
|
14
|
+
* where the key is the original text and the value is the translation
|
|
15
|
+
* @param translations The parsed PO file from gettext-parser
|
|
16
|
+
* @returns A simplified object with just the translations
|
|
17
|
+
*/
|
|
18
|
+
function simplifyTranslations(translations: any): Record<string, string> {
|
|
19
|
+
const result: Record<string, string> = {};
|
|
20
|
+
|
|
21
|
+
// Go through all translation contexts
|
|
22
|
+
Object.keys(translations.translations).forEach((context) => {
|
|
23
|
+
const contextTranslations = translations.translations[context];
|
|
24
|
+
|
|
25
|
+
// Skip the header (empty msgid)
|
|
26
|
+
Object.keys(contextTranslations).forEach((key) => {
|
|
27
|
+
if (key === "") return;
|
|
28
|
+
|
|
29
|
+
const translation = contextTranslations[key];
|
|
30
|
+
// Get the original text (msgid)
|
|
31
|
+
const original = translation.msgid;
|
|
32
|
+
// Get the translated text (first item in msgstr array)
|
|
33
|
+
const translated = translation.msgstr[0];
|
|
34
|
+
|
|
35
|
+
// Only add the translation if it exists and is not empty
|
|
36
|
+
if (translated && translated.trim() !== "") {
|
|
37
|
+
result[original] = translated;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a dictionary of all original strings from all translations
|
|
47
|
+
* For the default language, we need to gather all possible keys
|
|
48
|
+
* @param jsonDirectory Directory with JSON files
|
|
49
|
+
* @param allTranslations Collection of all translations
|
|
50
|
+
* @param defaultLanguage The default language code
|
|
51
|
+
* @param verbose Whether to log verbose messages
|
|
52
|
+
* @param pluginName The name of the plugin
|
|
53
|
+
* @param additionalTranslations Additional translations to include
|
|
54
|
+
* @returns Object with original strings as both keys and values
|
|
55
|
+
*/
|
|
56
|
+
async function createDefaultLanguageJson(
|
|
57
|
+
jsonDirectory: string,
|
|
58
|
+
allTranslations: Record<string, Record<string, string>>,
|
|
59
|
+
defaultLanguage: string,
|
|
60
|
+
verbose: boolean,
|
|
61
|
+
pluginName: string,
|
|
62
|
+
additionalTranslations: Record<string, string> = {}
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
// Create a set of all original strings from all translations
|
|
65
|
+
const allOriginalStrings = new Set<string>();
|
|
66
|
+
|
|
67
|
+
// Collect all original strings from all translations
|
|
68
|
+
Object.values(allTranslations).forEach((translations) => {
|
|
69
|
+
Object.keys(translations).forEach((key) => {
|
|
70
|
+
allOriginalStrings.add(key);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Create the default language JSON with keys matching values
|
|
75
|
+
const defaultLanguageJson: Record<string, string> = {};
|
|
76
|
+
allOriginalStrings.forEach((str) => {
|
|
77
|
+
defaultLanguageJson[str] = str;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Process additional translations
|
|
81
|
+
const finalTranslations = { ...defaultLanguageJson };
|
|
82
|
+
|
|
83
|
+
// For each additional translation, try to find a translation or use the original
|
|
84
|
+
Object.entries(additionalTranslations).forEach(([key, originalText]) => {
|
|
85
|
+
// If there's a translation for the original text, use it
|
|
86
|
+
if (defaultLanguageJson[originalText]) {
|
|
87
|
+
finalTranslations[key] = defaultLanguageJson[originalText];
|
|
88
|
+
} else {
|
|
89
|
+
// Otherwise use the original text
|
|
90
|
+
finalTranslations[key] = originalText;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Write the default language file with .default.json extension
|
|
95
|
+
const defaultLangDefaultFile = path.join(
|
|
96
|
+
jsonDirectory,
|
|
97
|
+
`${defaultLanguage}.default.json`
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (verbose) {
|
|
101
|
+
console.log(
|
|
102
|
+
`[${pluginName}] Creating default language file: ${defaultLangDefaultFile}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
await fs.writeFile(
|
|
107
|
+
defaultLangDefaultFile,
|
|
108
|
+
JSON.stringify(finalTranslations, null, 2)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Creates a Vite plugin that converts PO translation files to JSON format
|
|
114
|
+
* The JSON files are placed in the specified output directory
|
|
115
|
+
* @param options Configuration options for the plugin
|
|
116
|
+
* @returns A Vite plugin that handles PO to JSON conversion
|
|
117
|
+
*/
|
|
118
|
+
export function po2jsonPlugin(options: GettextPo2JsonPluginOptions): Plugin {
|
|
119
|
+
const {
|
|
120
|
+
poDirectory,
|
|
121
|
+
jsonDirectory,
|
|
122
|
+
defaultLanguage = "en",
|
|
123
|
+
verbose = false,
|
|
124
|
+
additionalTranslations = {},
|
|
125
|
+
} = options;
|
|
126
|
+
|
|
127
|
+
const pluginName = "vite-plugin-gettext-po2json";
|
|
128
|
+
|
|
129
|
+
async function convertPoToJson() {
|
|
130
|
+
try {
|
|
131
|
+
// Check if PO directory exists
|
|
132
|
+
try {
|
|
133
|
+
await ensureDirectory(poDirectory);
|
|
134
|
+
} catch {
|
|
135
|
+
if (verbose) {
|
|
136
|
+
console.log(
|
|
137
|
+
`[${pluginName}] PO directory ${poDirectory} does not exist yet, skipping conversion`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Find available languages
|
|
144
|
+
const languages = await findAvailableLanguages(
|
|
145
|
+
poDirectory,
|
|
146
|
+
pluginName,
|
|
147
|
+
verbose
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (languages.length === 0) {
|
|
151
|
+
if (verbose) {
|
|
152
|
+
console.log(`[${pluginName}] No translation files found`);
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Create JSON directory
|
|
158
|
+
await ensureDirectory(jsonDirectory);
|
|
159
|
+
|
|
160
|
+
// Collection of all translations to create the default language file
|
|
161
|
+
const allTranslations: Record<string, Record<string, string>> = {};
|
|
162
|
+
|
|
163
|
+
// Skip the default language if it exists in the list
|
|
164
|
+
const nonDefaultLanguages = languages.filter(
|
|
165
|
+
(lang) => lang !== defaultLanguage
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Handle default language if it exists in the list
|
|
169
|
+
if (languages.includes(defaultLanguage)) {
|
|
170
|
+
const poFile = path.join(poDirectory, `${defaultLanguage}.po`);
|
|
171
|
+
const jsonFile = path.join(jsonDirectory, `${defaultLanguage}.json`);
|
|
172
|
+
|
|
173
|
+
if (verbose) {
|
|
174
|
+
console.log(
|
|
175
|
+
`[${pluginName}] Converting default language ${poFile} to ${jsonFile}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Read and parse PO file
|
|
180
|
+
const poContent = await fs.readFile(poFile);
|
|
181
|
+
const translations = gettextParser.po.parse(poContent);
|
|
182
|
+
|
|
183
|
+
// Convert the translations to a simple JSON object
|
|
184
|
+
const simplifiedTranslations = simplifyTranslations(translations);
|
|
185
|
+
|
|
186
|
+
// Process additional translations for default language
|
|
187
|
+
const finalTranslations = { ...simplifiedTranslations };
|
|
188
|
+
|
|
189
|
+
// For each additional translation, add it to the default language file
|
|
190
|
+
Object.entries(additionalTranslations).forEach(
|
|
191
|
+
([key, originalText]) => {
|
|
192
|
+
finalTranslations[key] = originalText;
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Write JSON file for default language
|
|
197
|
+
await fs.writeFile(
|
|
198
|
+
jsonFile,
|
|
199
|
+
JSON.stringify(finalTranslations, null, 2)
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Add additional translations for all languages
|
|
204
|
+
for (const lang of nonDefaultLanguages) {
|
|
205
|
+
const poFile = path.join(poDirectory, `${lang}.po`);
|
|
206
|
+
const jsonFile = path.join(jsonDirectory, `${lang}.json`);
|
|
207
|
+
|
|
208
|
+
if (verbose) {
|
|
209
|
+
console.log(`[${pluginName}] Converting ${poFile} to ${jsonFile}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Read and parse PO file
|
|
213
|
+
const poContent = await fs.readFile(poFile);
|
|
214
|
+
const translations = gettextParser.po.parse(poContent);
|
|
215
|
+
|
|
216
|
+
// Convert the translations to a simple JSON object
|
|
217
|
+
const simplifiedTranslations = simplifyTranslations(translations);
|
|
218
|
+
|
|
219
|
+
// Store translations for creating the default language file
|
|
220
|
+
allTranslations[lang] = simplifiedTranslations;
|
|
221
|
+
|
|
222
|
+
// Process additional translations
|
|
223
|
+
const finalTranslations = { ...simplifiedTranslations };
|
|
224
|
+
|
|
225
|
+
// For each additional translation, try to find a translation or use the original
|
|
226
|
+
Object.entries(additionalTranslations).forEach(
|
|
227
|
+
([key, originalText]) => {
|
|
228
|
+
// If there's a translation for the original text, use it
|
|
229
|
+
if (simplifiedTranslations[originalText]) {
|
|
230
|
+
finalTranslations[key] = simplifiedTranslations[originalText];
|
|
231
|
+
} else {
|
|
232
|
+
// Otherwise use the original text
|
|
233
|
+
finalTranslations[key] = originalText;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// Write JSON file
|
|
239
|
+
await fs.writeFile(
|
|
240
|
+
jsonFile,
|
|
241
|
+
JSON.stringify(finalTranslations, null, 2)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Create the default language file (with all original strings as both keys and values)
|
|
246
|
+
await createDefaultLanguageJson(
|
|
247
|
+
jsonDirectory,
|
|
248
|
+
allTranslations,
|
|
249
|
+
defaultLanguage,
|
|
250
|
+
verbose,
|
|
251
|
+
pluginName,
|
|
252
|
+
additionalTranslations
|
|
253
|
+
);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
throw new Error(`Failed to convert PO files to JSON: ${error}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
name: pluginName,
|
|
261
|
+
|
|
262
|
+
async buildStart() {
|
|
263
|
+
await convertPoToJson();
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
configureServer(server) {
|
|
267
|
+
server.watcher.add(poDirectory);
|
|
268
|
+
|
|
269
|
+
server.watcher.on("change", async (file) => {
|
|
270
|
+
if (file.endsWith(".po")) {
|
|
271
|
+
if (verbose) {
|
|
272
|
+
console.log(
|
|
273
|
+
`[${pluginName}] PO file changed: ${file}, reconverting`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
await convertPoToJson();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
/**
|
|
31
|
+
* Configuration options for the gettext plugin
|
|
32
|
+
* Used to compile PO files to binary MO format
|
|
33
|
+
*/
|
|
34
|
+
export interface GettextPluginOptions {
|
|
35
|
+
/** Directory containing PO translation files */
|
|
36
|
+
poDirectory: string;
|
|
37
|
+
/** Output directory for compiled MO files */
|
|
38
|
+
moDirectory: string;
|
|
39
|
+
/** Filename of the MO file, defaults to 'messages.mo' */
|
|
40
|
+
filename?: string;
|
|
41
|
+
/** Enable verbose logging */
|
|
42
|
+
verbose?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Output format types for msgfmt
|
|
47
|
+
*/
|
|
48
|
+
export type MsgfmtFormat =
|
|
49
|
+
| "mo"
|
|
50
|
+
| "java"
|
|
51
|
+
| "java2"
|
|
52
|
+
| "csharp"
|
|
53
|
+
| "csharp-resources"
|
|
54
|
+
| "tcl"
|
|
55
|
+
| "desktop"
|
|
56
|
+
| "xml"
|
|
57
|
+
| "json"
|
|
58
|
+
| "qt";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Configuration options for the msgfmt plugin
|
|
62
|
+
* Used to compile PO files to various formats including binary MO
|
|
63
|
+
*/
|
|
64
|
+
export interface MsgfmtPluginOptions {
|
|
65
|
+
/** Directory containing PO translation files */
|
|
66
|
+
poDirectory: string;
|
|
67
|
+
/** Output directory for compiled files */
|
|
68
|
+
outputDirectory: string;
|
|
69
|
+
/** The gettext domain name, defaults to 'messages' */
|
|
70
|
+
domain?: string;
|
|
71
|
+
/** Output filename, defaults to 'messages.mo' */
|
|
72
|
+
filename?: string;
|
|
73
|
+
/** Output format, defaults to 'mo' */
|
|
74
|
+
format?: MsgfmtFormat;
|
|
75
|
+
/** Path to template file, required for XML format */
|
|
76
|
+
templateFile?: string;
|
|
77
|
+
/** Enable verbose logging */
|
|
78
|
+
verbose?: boolean;
|
|
79
|
+
/** Additional options to pass to msgfmt command */
|
|
80
|
+
msgfmtOptions?: string[];
|
|
81
|
+
/** Whether to use the standard locale structure (locale/LANG/LC_MESSAGES/domain.mo) */
|
|
82
|
+
useLocaleStructure?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface PluginOptions {
|
|
86
|
+
pluginName: string;
|
|
87
|
+
verbose?: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Options for the PO to JSON conversion plugin
|
|
92
|
+
*/
|
|
93
|
+
export interface GettextPo2JsonPluginOptions {
|
|
94
|
+
/**
|
|
95
|
+
* Directory containing PO files
|
|
96
|
+
*/
|
|
97
|
+
poDirectory: string;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Directory where JSON files will be saved
|
|
101
|
+
*/
|
|
102
|
+
jsonDirectory: string;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Default language code (default: 'en')
|
|
106
|
+
*/
|
|
107
|
+
defaultLanguage?: string;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Enable verbose logging
|
|
111
|
+
*/
|
|
112
|
+
verbose?: boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Additional translations to include in all language files
|
|
116
|
+
* Keys are identifiers and values are the English text
|
|
117
|
+
* The English text will be translated for non-default languages if translations exist
|
|
118
|
+
*/
|
|
119
|
+
additionalTranslations?: Record<string, string>;
|
|
120
|
+
}
|