Skip to content

Commit 4951bc7

Browse files
committed
Prepare v1.1.0 release
- Update version to 1.1.0 in package.json - Add v1.1.0 changelog with fixes, additions, and changes - Update @SInCE tags from TBD to 1.1.0 - Apply code formatting improvements
1 parent aa69320 commit 4951bc7

10 files changed

Lines changed: 196 additions & 80 deletions

File tree

CHANGELOG.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
11
# Changelog
22

3-
## [1.0.0] - 2025-06-27
3+
## 1.1.0 — 2025-11-12
4+
5+
### Fixed
6+
7+
* Fix plural-form translations in languages with complex rules (e.g., Arabic, Russian,).
8+
* Treat environment variables such as `FORCE_TRANSLATE=false` correctly instead of interpreting them as `true`.
9+
* Ensure configuration files are detected when running Potomatic from paths containing spaces (such as `~/Local Sites/`) and on Windows systems.
410

511
### Added
612

7-
- 🚀 Initial release!
13+
* Support multiple API key formats (`OPENAI_API_KEY`, `POTOMATIC_OPENAI_API_KEY`, etc.) with automatic provider detection based on the key name.
14+
* Add CLI options `--prompt-file-path` and `--po-header-template-path` to customize the locations of translation prompt and header template files.
15+
16+
### Changed
17+
18+
* Update OpenAI pricing data to reflect current model costs (November 2025).
19+
20+
## 1.0.1 — 2025-06-27
21+
22+
* Version bump for [npm release](https://www.npmjs.com/package/potomatic).
23+
24+
## 1.0.0 — 2025-06-27
25+
26+
* Initial release.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "potomatic",
3-
"version": "1.0.1",
3+
"version": "1.1.0",
44
"description": "AI-powered translation utility for translating .po(t) files into multiple languages",
55
"author": "GravityKit",
66
"keywords": [

src/config/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ function parseEnvironmentConfig() {
144144
* Creates a Zod schema for parsing boolean values from strings (env vars and CLI args).
145145
* Handles common string representations: "true", "false", "1", "0".
146146
*
147-
* @since TBD
147+
* @since 1.1.0
148148
*
149149
* @param {boolean} defaultValue - Default value if not provided or empty.
150150
*
@@ -163,7 +163,7 @@ function booleanStringSchema(defaultValue = false) {
163163
* Auto-detects provider from environment variable keys.
164164
* Checks for POTOMATIC_<PROVIDER>_API_KEY and <PROVIDER>_API_KEY patterns.
165165
*
166-
* @since TBD
166+
* @since 1.1.0
167167
*
168168
* @return {string|null} Detected provider name (lowercase) or null if none detected.
169169
*/

src/orchestrator/index.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ export class TranslationOrchestrator {
799799
*
800800
* @private
801801
*
802-
* @since TBD
802+
* @since 1.1.0
803803
*
804804
* @param {string} language - Language code.
805805
* @param {Object} stats - Language processing statistics.
@@ -814,9 +814,7 @@ export class TranslationOrchestrator {
814814

815815
if (totalValidationIssues > 0) {
816816
const breakdown = formatValidationBreakdown(stats.validationStats);
817-
this.mainLogger.warn(
818-
`⚠️ ${language}: ${breakdown}. Run with -v 2 for details.`
819-
);
817+
this.mainLogger.warn(`⚠️ ${language}: ${breakdown}. Run with -v 2 for details.`);
820818
}
821819
}
822820

src/providers/openai/OpenAIProvider.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -630,14 +630,7 @@ export class OpenAIProvider extends Provider {
630630
_parseApiResponse(responseContent, batch, pluralCount, dictionaryCount = 0) {
631631
try {
632632
const verbosityLevel = this.config.verboseLevel || 1;
633-
const { translations, validationStats } = parseXmlResponse(
634-
responseContent,
635-
batch,
636-
pluralCount,
637-
this.logger,
638-
dictionaryCount,
639-
verbosityLevel
640-
);
633+
const { translations, validationStats } = parseXmlResponse(responseContent, batch, pluralCount, this.logger, dictionaryCount, verbosityLevel);
641634

642635
return { translations, validationStats };
643636
} catch (error) {

src/utils/reportingUtils.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,7 @@ export function printTranslationRunSummary(allLanguageStats, logger, effectiveCh
185185
}).length;
186186

187187
// Print overall results.
188-
const summaryParts = [
189-
`Languages processed: ${getStyledColor('success')(totals.languagesProcessed)}`,
190-
`With errors: ${getStyledColor('error')(totals.languagesWithErrors)}`,
191-
];
188+
const summaryParts = [`Languages processed: ${getStyledColor('success')(totals.languagesProcessed)}`, `With errors: ${getStyledColor('error')(totals.languagesWithErrors)}`];
192189

193190
if (languagesWithWarnings > 0) {
194191
summaryParts.push(`With warnings: ${getStyledColor('warning')(languagesWithWarnings)}`);

src/utils/validationStats.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
* Validation statistics utilities.
33
* Centralized validation type definitions and helper functions.
44
*
5-
* @since TBD
5+
* @since 1.1.0
66
*/
77

88
/**
99
* Registry of all validation types with their display names.
1010
*
11-
* @since TBD
11+
* @since 1.1.0
1212
*/
1313
export const VALIDATION_TYPES = {
1414
stringsWithPluralIssues: 'strings with plural issues',
@@ -17,7 +17,7 @@ export const VALIDATION_TYPES = {
1717
/**
1818
* Creates an empty validation statistics object with all validation types initialized to 0.
1919
*
20-
* @since TBD
20+
* @since 1.1.0
2121
*
2222
* @return {Object} Empty validation stats object.
2323
*/
@@ -34,7 +34,7 @@ export function createEmptyValidationStats() {
3434
/**
3535
* Calculates the total number of validation issues.
3636
*
37-
* @since TBD
37+
* @since 1.1.0
3838
*
3939
* @param {Object} validationStats - Validation statistics object.
4040
*
@@ -57,7 +57,7 @@ export function getTotalValidationIssues(validationStats) {
5757
/**
5858
* Formats validation stats as a human-readable breakdown string.
5959
*
60-
* @since TBD
60+
* @since 1.1.0
6161
*
6262
* @param {Object} validationStats - Validation statistics object.
6363
*
@@ -83,7 +83,7 @@ export function formatValidationBreakdown(validationStats) {
8383
/**
8484
* Accumulates validation stats from a source object into a target object.
8585
*
86-
* @since TBD
86+
* @since 1.1.0
8787
*
8888
* @param {Object} target - Target validation stats object to accumulate into.
8989
* @param {Object} source - Source validation stats object to accumulate from.

src/utils/xmlTranslation.js

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export function buildDictionaryResponse(dictionaryMatches) {
102102
* Validates plural form translations.
103103
* Ensures correct form count and warns about incomplete translations.
104104
*
105-
* @since TBD
105+
* @since 1.1.0
106106
*
107107
* @param {Array<string>} forms - Array of translated forms.
108108
* @param {string} originalMsgid - Original msgid for logging context.
@@ -119,7 +119,7 @@ function validatePluralForms(forms, originalMsgid, expectedCount, itemId, logger
119119
const issues = [];
120120

121121
// Find first non-empty form to use as fallback.
122-
const fallbackForm = correctedForms.find(f => f && f.trim() !== '') || '';
122+
const fallbackForm = correctedForms.find((f) => f && f.trim() !== '') || '';
123123

124124
// Ensure we have exactly the right number of forms.
125125
if (correctedForms.length < expectedCount) {
@@ -128,10 +128,7 @@ function validatePluralForms(forms, originalMsgid, expectedCount, itemId, logger
128128
issues.push(`insufficient forms (expected ${expectedCount}, got ${correctedForms.length})`);
129129

130130
if (verbosityLevel >= 2) {
131-
logger.warn(
132-
`Insufficient plural forms at index ${itemId} for "${originalMsgid.substring(0, 50)}...": ` +
133-
`Expected ${expectedCount}, got ${correctedForms.length}. Padding with first form as fallback.`
134-
);
131+
logger.warn(`Insufficient plural forms at index ${itemId} for "${originalMsgid.substring(0, 50)}...": ` + `Expected ${expectedCount}, got ${correctedForms.length}. Padding with first form as fallback.`);
135132
}
136133

137134
while (correctedForms.length < expectedCount) {
@@ -143,36 +140,28 @@ function validatePluralForms(forms, originalMsgid, expectedCount, itemId, logger
143140
issues.push(`excess forms (expected ${expectedCount}, got ${correctedForms.length})`);
144141

145142
if (verbosityLevel >= 2) {
146-
logger.warn(
147-
`Too many plural forms at index ${itemId} for "${originalMsgid.substring(0, 50)}...": ` +
148-
`Expected ${expectedCount}, got ${correctedForms.length}. Truncating.`
149-
);
143+
logger.warn(`Too many plural forms at index ${itemId} for "${originalMsgid.substring(0, 50)}...": ` + `Expected ${expectedCount}, got ${correctedForms.length}. Truncating.`);
150144
}
151145

152146
correctedForms = correctedForms.slice(0, expectedCount);
153147
}
154148

155149
// Warn about empty forms (except when all are empty - likely not translated yet).
156-
const nonEmptyCount = correctedForms.filter(f => f && f.trim() !== '').length;
150+
const nonEmptyCount = correctedForms.filter((f) => f && f.trim() !== '').length;
157151

158152
if (nonEmptyCount > 0 && nonEmptyCount < expectedCount) {
159153
hasIssues = true;
160154

161-
const emptyIndices = correctedForms
162-
.map((f, i) => (f && f.trim() !== '' ? null : i))
163-
.filter(i => i !== null);
155+
const emptyIndices = correctedForms.map((f, i) => (f && f.trim() !== '' ? null : i)).filter((i) => i !== null);
164156

165157
issues.push(`incomplete forms (empty at indices [${emptyIndices.join(', ')}])`);
166158

167159
if (verbosityLevel >= 2) {
168-
logger.warn(
169-
`Incomplete plural forms at index ${itemId} for "${originalMsgid.substring(0, 50)}...": ` +
170-
`Forms at indices [${emptyIndices.join(', ')}] are empty. Filling with first form as fallback.`
171-
);
160+
logger.warn(`Incomplete plural forms at index ${itemId} for "${originalMsgid.substring(0, 50)}...": ` + `Forms at indices [${emptyIndices.join(', ')}] are empty. Filling with first form as fallback.`);
172161
}
173162

174163
// Fill empty forms with the first non-empty form as fallback.
175-
correctedForms = correctedForms.map(f => (f && f.trim() !== '') ? f : fallbackForm);
164+
correctedForms = correctedForms.map((f) => (f && f.trim() !== '' ? f : fallbackForm));
176165
}
177166

178167
if (hasIssues && verbosityLevel >= 3) {
@@ -270,14 +259,7 @@ export function parseXmlResponse(xmlResponse, batch, pluralCount, logger, dictio
270259
}
271260

272261
// Validate and auto-correct plural forms.
273-
const { correctedForms, hasIssues } = validatePluralForms(
274-
forms,
275-
batch[batchIndex].msgid,
276-
pluralCount,
277-
responseIndex,
278-
logger,
279-
verbosityLevel
280-
);
262+
const { correctedForms, hasIssues } = validatePluralForms(forms, batch[batchIndex].msgid, pluralCount, responseIndex, logger, verbosityLevel);
281263

282264
if (hasIssues) {
283265
validationStats.stringsWithPluralIssues++;
@@ -293,19 +275,9 @@ export function parseXmlResponse(xmlResponse, batch, pluralCount, logger, dictio
293275
// If this entry expects plural forms, validate the result
294276
if (batch[batchIndex].msgid_plural) {
295277
if (verbosityLevel >= 2) {
296-
logger.warn(
297-
`Missing plural forms at index ${responseIndex} for "${batch[batchIndex].msgid.substring(0, 50)}...": ` +
298-
`Expected ${pluralCount} forms but received singular translation. Using single translation for form 0 only.`
299-
);
278+
logger.warn(`Missing plural forms at index ${responseIndex} for "${batch[batchIndex].msgid.substring(0, 50)}...": ` + `Expected ${pluralCount} forms but received singular translation. Using single translation for form 0 only.`);
300279
}
301-
const { correctedForms, hasIssues } = validatePluralForms(
302-
[translation],
303-
batch[batchIndex].msgid,
304-
pluralCount,
305-
responseIndex,
306-
logger,
307-
verbosityLevel
308-
);
280+
const { correctedForms, hasIssues } = validatePluralForms([translation], batch[batchIndex].msgid, pluralCount, responseIndex, logger, verbosityLevel);
309281

310282
if (hasIssues) {
311283
validationStats.stringsWithPluralIssues++;

0 commit comments

Comments
 (0)