Skip to content

i18n: sync translation files with current .pot template (#173)#186

Merged
parhumm merged 2 commits intodevelopmentfrom
fix/173-sync-translation-files
Mar 13, 2026
Merged

i18n: sync translation files with current .pot template (#173)#186
parhumm merged 2 commits intodevelopmentfrom
fix/173-sync-translation-files

Conversation

@ParhamTehrani
Copy link
Copy Markdown
Contributor

@ParhamTehrani ParhamTehrani commented Mar 13, 2026

  • Merge wp-slimstat.pot into all 12 .po files using msgmerge
  • Remove obsolete #~ entries with msgattrib --no-obsolete
  • Rebuild .mo binary files with msgfmt

All language files now contain the current 1,438 message strings, enabling proper localization support for new features.

Describe your changes

...

Submission Review Guidelines:

  • I have performed a self-review of my code
  • If it is a core feature, I have added thorough tests.
  • Will this be part of a product update? If yes, please write one phrase about this update.
  • I have reviewed my code for security best practices.
  • Following the above guidelines will result in quick merges and clear and detailed feedback when appropriate.
  • My code follows the style guidelines of this project
  • I have updated the change-log in CHANGELOG.md.

Type of change

  • Fix - Fixes an existing bug
  • Add - Adds functionality
  • Update - Update existing functionality
  • Dev - Development related task
  • Tweak - A minor adjustment to the codebase
  • Performance - Address performance issues
  • Enhancement - Improvement to existing functionality

Summary by CodeRabbit

  • Tests
    • Added end-to-end test suite for internationalization features.
    • Tests validate translation catalog completeness across supported languages.
    • Tests verify proper rendering of translations for multiple locales.
    • Tests ensure untranslated strings fall back to English correctly.
    • Tests include database state management and regression checks.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

Warning

Rate limit exceeded

@parhumm has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 58 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f7874314-49fd-46be-a554-707d58b4cdea

📥 Commits

Reviewing files that changed from the base of the PR and between b511d72 and 9fab085.

📒 Files selected for processing (25)
  • languages/wp-slimstat-bel.mo
  • languages/wp-slimstat-bel.po
  • languages/wp-slimstat-de_DE.mo
  • languages/wp-slimstat-de_DE.po
  • languages/wp-slimstat-fa_IR.mo
  • languages/wp-slimstat-fa_IR.po
  • languages/wp-slimstat-fr_CA.mo
  • languages/wp-slimstat-fr_CA.po
  • languages/wp-slimstat-fr_FR.mo
  • languages/wp-slimstat-fr_FR.po
  • languages/wp-slimstat-id_ID.mo
  • languages/wp-slimstat-id_ID.po
  • languages/wp-slimstat-ja.mo
  • languages/wp-slimstat-ja.po
  • languages/wp-slimstat-pl_PL.mo
  • languages/wp-slimstat-pl_PL.po
  • languages/wp-slimstat-ru_RU.mo
  • languages/wp-slimstat-ru_RU.po
  • languages/wp-slimstat-sv_SE.mo
  • languages/wp-slimstat-sv_SE.po
  • languages/wp-slimstat-tr_TR.mo
  • languages/wp-slimstat-tr_TR.po
  • languages/wp-slimstat-zh_CN.mo
  • languages/wp-slimstat-zh_CN.po
  • tests/e2e/i18n-catalog-sync.spec.ts
📝 Walkthrough

Walkthrough

A new Playwright end-to-end test suite validates internationalization catalog sync for issue #173. Tests verify .po/.pot file completeness, translation rendering in German and French locales on the SlimStat Settings page, fallback behavior for untranslated strings, and restoration to English without regressions.

Changes

Cohort / File(s) Summary
E2E Test Suite: i18n Catalog Sync
tests/e2e/i18n-catalog-sync.spec.ts
New Playwright test suite with global DB pool setup for WPLANG manipulation, helper functions for reading/setting WPLANG via MySQL, and five test cases validating catalog completeness, German/French translation rendering, untranslated string fallback, and English restoration without regressions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • Issue #173: The new E2E tests directly verify .po/.pot catalog completeness and runtime translation rendering for de_DE and fr_FR locales, addressing missing/outdated translation files described in the issue.

Poem

🐰 A test suite hops with care so bright,
Checking catalogs left and right,
De, French, English—all aligned,
No translation left behind! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title describes the translation file synchronization work (msgmerge with .pot template), which is the primary change in the commits. However, the PR also includes an end-to-end test suite, and the reviewer identified critical issues (placeholder loss, URL drift, broken markup) requiring fixes before merge.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/173-sync-translation-files
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@parhumm parhumm force-pushed the fix/173-sync-translation-files branch from ccca279 to 518e876 Compare March 13, 2026 10:24
@parhumm
Copy link
Copy Markdown
Contributor

parhumm commented Mar 13, 2026

Deep standards pass on this PR found a few issues that should be fixed before merge.

  1. Placeholder regressions in runtime-formatted strings
  • Several changed translations drop or alter %s / %d placeholders that are still interpolated with sprintf() at runtime, so the localized UI loses dynamic values.
  • Representative examples:
    • languages/wp-slimstat-de_DE.po:3763-3764 changes Showing %s - %s of %s to Ergbnisse filtern wo %s ist gleich %s, but this string is formatted in admin/view/wp-slimstat-reports.php:1055.
    • languages/wp-slimstat-de_DE.po:1756-1757 changes Pageviews in the last %s days to Seitenaufrufe (Diagramm), but this string is formatted in admin/index.php:1472.
    • languages/wp-slimstat-de_DE.po:6594-6595 changes Network error downloading MaxMind database: %s to Fehler beim Aktualisieren folgender Optionen:, but this string is formatted in src/Services/Geolocation/Provider/MaxmindGeoIPProvider.php:141.
  • Affected locale files: bel, de_DE, fa_IR, fr_CA, fr_FR, id_ID, ja, pl_PL, ru_RU, sv_SE, tr_TR, zh_CN.
  • Please preserve the exact placeholder set from the source string in every translated msgstr used by a formatter.
  1. Translator-controlled URL drift
  • WordPress’ i18n guidance is explicit here: don’t leave URLs in translatable strings, and use placeholders for links instead. This PR reintroduces localized strings whose URLs no longer match the source string, including legacy or alternate domains.
  • Representative examples:
    • languages/wp-slimstat-bel.po switches the FAQ URL to https://slimstat.freshdesk.com/....
    • languages/wp-slimstat-id_ID.po:847 replaces the source geolocation URL with http://www.infosniper.net/?ip_address=.
    • Multiple locale files replace the source WordPress capability docs URL with http://codex.wordpress.org/Roles_and_Capabilities.
  • Affected locale files: bel, de_DE, fa_IR, fr_CA, fr_FR, id_ID, ja, pl_PL, ru_RU, sv_SE, tr_TR, zh_CN.
  • Please keep URLs out of translated text or preserve the source URL exactly.
  1. Broken translated HTML markup
  • A few changed translations now contain malformed HTML, which is risky here because some of these strings are rendered as allowed HTML in admin settings/help text.
  • Representative examples:
    • languages/wp-slimstat-zh_CN.po:889 contains a broken closing tag: </ a>.
    • languages/wp-slimstat-id_ID.po:847 contains malformed <code> markup: </ code>.
    • languages/wp-slimstat-id_ID.po:1404 has malformed anchor attributes around the capabilities link.
  • Please validate translated markup against the source string structure before regenerating the .mo files.

I re-ran msgfmt --check --check-format and confirmed the committed .mo files match the .po sources, so the fix here is content cleanup rather than binary drift. After correcting the affected translations, regenerating the .mo files should be enough.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
tests/e2e/i18n-catalog-sync.spec.ts (5)

12-12: Unused import: BASE_URL.

BASE_URL is imported but never used—page.goto() calls use relative URLs. Either remove the import or use BASE_URL explicitly for clarity.

-import { BASE_URL, MYSQL_CONFIG } from './helpers/env';
+import { MYSQL_CONFIG } from './helpers/env';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/i18n-catalog-sync.spec.ts` at line 12, Remove the unused import
BASE_URL from the top import list (currently importing BASE_URL, MYSQL_CONFIG)
or alternatively update the page navigation calls (page.goto) to use BASE_URL so
the import is actually used; reference the import symbol BASE_URL and the test
navigation calls like page.goto to decide whether to delete the import or switch
to absolute URLs.

41-46: Consider typing the MySQL result instead of as any.

+interface WpOption {
+  option_value: string;
+}
+
 async function getWplang(): Promise<string> {
-  const [rows] = await pool.execute(
+  const [rows] = await pool.execute<mysql.RowDataPacket[]>(
     "SELECT option_value FROM wp_options WHERE option_name = 'WPLANG'"
-  ) as any;
-  return rows.length > 0 ? rows[0].option_value : '';
+  );
+  return rows.length > 0 ? (rows[0] as WpOption).option_value : '';
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/i18n-catalog-sync.spec.ts` around lines 41 - 46, getWplang
currently casts the result of pool.execute to any; replace that with a proper
MySQL result type so the row shape is typed. Use the mysql2 types (e.g. import
RowDataPacket from 'mysql2' or use the pool.execute generic) and cast the
execute call to something like [RowDataPacket[]] or
pool.execute<RowDataPacket[]>("...") so rows is typed and rows[0].option_value
is checked safely inside getWplang.

55-59: Add defensive check in afterAll for pool initialization failure.

If beforeAll fails to create the pool, afterAll will throw when accessing pool. A defensive check prevents cascading errors.

 test.afterAll(async () => {
+  if (!pool) return;
   // Restore locale to en_US
   await pool.execute("UPDATE wp_options SET option_value = '' WHERE option_name = 'WPLANG'");
   await pool.end();
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/i18n-catalog-sync.spec.ts` around lines 55 - 59, The afterAll
teardown references the database connection variable pool and will throw if
beforeAll failed to initialize it; update the test.afterAll block to defensively
check that pool is defined/non-null (and optionally that pool.execute/end are
functions) before calling pool.execute(...) and pool.end(), so guard the
existing UPDATE and end() calls with an if (pool) or equivalent check to avoid
cascading errors from a failed beforeAll initialization.

103-106: Consider strengthening translation assertions.

Checking for "at least one" German translation is lenient and could pass even if most translations are broken. If the goal is to verify comprehensive translation loading, consider checking for multiple distinct translated strings.

-    expect(
-      hasGermanTracker || hasGermanEnable || hasGermanSettings,
-      `Expected at least one German translation. Page text snippet: ${bodyText!.substring(0, 500)}`
-    ).toBeTruthy();
+    // Require at least two distinct translations to ensure catalog loaded properly
+    const translationCount = [hasGermanTracker, hasGermanEnable, hasGermanSettings].filter(Boolean).length;
+    expect(
+      translationCount,
+      `Expected multiple German translations but found ${translationCount}. Page text: ${bodyText!.substring(0, 500)}`
+    ).toBeGreaterThanOrEqual(2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/i18n-catalog-sync.spec.ts` around lines 103 - 106, The current
assertion only requires a single German translation (hasGermanTracker ||
hasGermanEnable || hasGermanSettings) which is too weak; update the test to
assert that multiple distinct translated strings are present by counting truthy
flags (hasGermanTracker, hasGermanEnable, hasGermanSettings, and any additional
German checks you add) and expecting the count to be >= 2 (or another
threshold), preserving the diagnostic message using bodyText!.substring(0,500)
so failures still show the page snippet.

90-90: Replace waitForTimeout with explicit wait conditions.

waitForTimeout is a Playwright anti-pattern—it's flaky and slows tests unnecessarily. Wait for a specific element or network idle instead.

-    await page.waitForTimeout(2000); // settle
+    // Wait for a known element to be visible
+    await page.locator('text=SlimStat').first().waitFor({ state: 'visible', timeout: 10_000 });

Apply the same pattern to lines 118, 144, and 170.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/i18n-catalog-sync.spec.ts` at line 90, Replace each instance of
await page.waitForTimeout(2000); with an explicit wait for the specific
condition that indicates the app is ready: use await
page.waitForSelector('<expected-selector>') for DOM changes, or await
page.waitForResponse(resp => resp.url().includes('<api-endpoint>') &&
resp.status() === 200) when waiting for network results, or await
page.waitForLoadState('networkidle') when waiting for background requests to
finish; update the four occurrences of page.waitForTimeout(2000) in this test
(search for the exact call string) and choose the appropriate selector or
response predicate that matches the action immediately before/after each call
(or use Promise.all([page.waitForResponse(...), page.click(...)]) when the wait
should follow a click).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/e2e/i18n-catalog-sync.spec.ts`:
- Around line 20-24: The dbConfig object overrides MYSQL_CONFIG with a hardcoded
machine-specific socketPath which breaks CI/other developers; remove that
hardcoded path and rely on MYSQL_CONFIG (and the existing MYSQL_SOCKET env var)
instead—update the dbConfig declaration (the object named dbConfig and its
socketPath property) to stop injecting '/Users/parhumm/...' and either omit
socketPath entirely or only set it from process.env.MYSQL_SOCKET so local
overrides come from the environment.
- Around line 179-181: The negative test assertion using
expect(htmlContent).not.toContain('Warning:') is too broad and may flag
legitimate UI copy; update the assertion to search for a more specific warning
pattern (e.g., a runtime stack/trace) by checking htmlContent for a regex like a
"Warning:" followed by a newline and an "at " stack frame or a file path (for
example use a regexp that matches /Warning:.*\n\s+at / or
/Warning:.*(node_modules|webpack)/) instead of the plain 'Warning:' string;
replace the expect(htmlContent).not.toContain('Warning:') call with an assertion
that htmlContent does not match that specific regexp so only actual runtime
warnings with stack traces are caught.
- Around line 30-46: The queries in setWplang, getWplang, and the afterAll
cleanup use the hardcoded table name "wp_options", which will break for installs
with a custom table prefix; read a table prefix from config/env (e.g.,
process.env.WP_DB_PREFIX or a shared test config constant, defaulting to "wp_"
if unset), compute the options table name as `${prefix}options`, and use that
variable in the SQL strings for pool.execute in setWplang, getWplang, and the
cleanup query so all calls target the correct table across environments.

---

Nitpick comments:
In `@tests/e2e/i18n-catalog-sync.spec.ts`:
- Line 12: Remove the unused import BASE_URL from the top import list (currently
importing BASE_URL, MYSQL_CONFIG) or alternatively update the page navigation
calls (page.goto) to use BASE_URL so the import is actually used; reference the
import symbol BASE_URL and the test navigation calls like page.goto to decide
whether to delete the import or switch to absolute URLs.
- Around line 41-46: getWplang currently casts the result of pool.execute to
any; replace that with a proper MySQL result type so the row shape is typed. Use
the mysql2 types (e.g. import RowDataPacket from 'mysql2' or use the
pool.execute generic) and cast the execute call to something like
[RowDataPacket[]] or pool.execute<RowDataPacket[]>("...") so rows is typed and
rows[0].option_value is checked safely inside getWplang.
- Around line 55-59: The afterAll teardown references the database connection
variable pool and will throw if beforeAll failed to initialize it; update the
test.afterAll block to defensively check that pool is defined/non-null (and
optionally that pool.execute/end are functions) before calling pool.execute(...)
and pool.end(), so guard the existing UPDATE and end() calls with an if (pool)
or equivalent check to avoid cascading errors from a failed beforeAll
initialization.
- Around line 103-106: The current assertion only requires a single German
translation (hasGermanTracker || hasGermanEnable || hasGermanSettings) which is
too weak; update the test to assert that multiple distinct translated strings
are present by counting truthy flags (hasGermanTracker, hasGermanEnable,
hasGermanSettings, and any additional German checks you add) and expecting the
count to be >= 2 (or another threshold), preserving the diagnostic message using
bodyText!.substring(0,500) so failures still show the page snippet.
- Line 90: Replace each instance of await page.waitForTimeout(2000); with an
explicit wait for the specific condition that indicates the app is ready: use
await page.waitForSelector('<expected-selector>') for DOM changes, or await
page.waitForResponse(resp => resp.url().includes('<api-endpoint>') &&
resp.status() === 200) when waiting for network results, or await
page.waitForLoadState('networkidle') when waiting for background requests to
finish; update the four occurrences of page.waitForTimeout(2000) in this test
(search for the exact call string) and choose the appropriate selector or
response predicate that matches the action immediately before/after each call
(or use Promise.all([page.waitForResponse(...), page.click(...)]) when the wait
should follow a click).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fbea612a-25f1-4df3-ba7d-413afa5b480e

📥 Commits

Reviewing files that changed from the base of the PR and between a680509 and b511d72.

📒 Files selected for processing (25)
  • languages/wp-slimstat-bel.mo
  • languages/wp-slimstat-bel.po
  • languages/wp-slimstat-de_DE.mo
  • languages/wp-slimstat-de_DE.po
  • languages/wp-slimstat-fa_IR.mo
  • languages/wp-slimstat-fa_IR.po
  • languages/wp-slimstat-fr_CA.mo
  • languages/wp-slimstat-fr_CA.po
  • languages/wp-slimstat-fr_FR.mo
  • languages/wp-slimstat-fr_FR.po
  • languages/wp-slimstat-id_ID.mo
  • languages/wp-slimstat-id_ID.po
  • languages/wp-slimstat-ja.mo
  • languages/wp-slimstat-ja.po
  • languages/wp-slimstat-pl_PL.mo
  • languages/wp-slimstat-pl_PL.po
  • languages/wp-slimstat-ru_RU.mo
  • languages/wp-slimstat-ru_RU.po
  • languages/wp-slimstat-sv_SE.mo
  • languages/wp-slimstat-sv_SE.po
  • languages/wp-slimstat-tr_TR.mo
  • languages/wp-slimstat-tr_TR.po
  • languages/wp-slimstat-zh_CN.mo
  • languages/wp-slimstat-zh_CN.po
  • tests/e2e/i18n-catalog-sync.spec.ts

Comment on lines +20 to +24
// Use the correct MySQL socket for Local by Flywheel
const dbConfig = {
...MYSQL_CONFIG,
socketPath: process.env.MYSQL_SOCKET || '/Users/parhumm/Library/Application Support/Local/run/X-JdmZXIa/mysql/mysqld.sock',
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Hardcoded developer-local socket path breaks CI and other environments.

The socketPath override contains a machine-specific path (/Users/parhumm/...) that will fail for other developers and in CI. The MYSQL_CONFIG from helpers/env.ts already handles environment-based configuration properly.

Proposed fix: Remove the local override
-// Use the correct MySQL socket for Local by Flywheel
-const dbConfig = {
-  ...MYSQL_CONFIG,
-  socketPath: process.env.MYSQL_SOCKET || '/Users/parhumm/Library/Application Support/Local/run/X-JdmZXIa/mysql/mysqld.sock',
-};
+const dbConfig = MYSQL_CONFIG;

If a local override is needed during development, set the MYSQL_SOCKET environment variable instead.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Use the correct MySQL socket for Local by Flywheel
const dbConfig = {
...MYSQL_CONFIG,
socketPath: process.env.MYSQL_SOCKET || '/Users/parhumm/Library/Application Support/Local/run/X-JdmZXIa/mysql/mysqld.sock',
};
const dbConfig = MYSQL_CONFIG;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/i18n-catalog-sync.spec.ts` around lines 20 - 24, The dbConfig
object overrides MYSQL_CONFIG with a hardcoded machine-specific socketPath which
breaks CI/other developers; remove that hardcoded path and rely on MYSQL_CONFIG
(and the existing MYSQL_SOCKET env var) instead—update the dbConfig declaration
(the object named dbConfig and its socketPath property) to stop injecting
'/Users/parhumm/...' and either omit socketPath entirely or only set it from
process.env.MYSQL_SOCKET so local overrides come from the environment.

- Merge wp-slimstat.pot into all 12 .po files using msgmerge
- Remove fuzzy translations to prevent placeholder/URL/HTML mismatches
- Remove obsolete #~ entries with msgattrib
- Rebuild .mo binary files with msgfmt --check --check-format

Fuzzy translations are removed because they can contain:
- Mismatched %s/%d placeholders breaking sprintf() calls
- Outdated URLs that don't match source strings
- Malformed HTML markup

Untranslated strings will fall back to English, which is safer
than displaying broken placeholders or incorrect URLs.
@ParhamTehrani ParhamTehrani force-pushed the fix/173-sync-translation-files branch from b511d72 to dab1d44 Compare March 13, 2026 10:51
@parhumm
Copy link
Copy Markdown
Contributor

parhumm commented Mar 13, 2026

E2E QA Results — Issue #173: i18n Catalog Sync

Branch: fix/173-sync-translation-files (rebased on development)
Plugin: wp-slimstat v5.4.3 + wp-slimstat-pro
Env: Local by Flywheel · WordPress 6.9.4 · PHP 8.5.0

Catalog Verification

Check Result
12 .po files match .pot msgid count (1,438) ✅ Pass
All .po files pass msgfmt --check-format ✅ Pass
All 12 .mo files exist and non-empty (13–35 KB) ✅ Pass
load_plugin_textdomain path correct ✅ Pass

Playwright E2E Tests — 5/5 Passing

# Scenario Result
1 All .po files contain expected msgid count ✅ Pass
2 de_DE locale renders known German translations (Settings page) ✅ Pass
3 fr_FR locale renders known French translations (Settings page) ✅ Pass
4 Untranslated strings fall back to English without broken markup ✅ Pass
5 Restoring en_US shows English strings without regressions ✅ Pass

Translation Coverage (informational — not a blocker)

Locale Translated Fuzzy Untranslated
bel 304 229 905
de_DE 186 314 938
fa_IR 200 286 952
fr_CA 225 318 895
fr_FR 225 318 895
id_ID 252 360 826
ja 205 302 931
pl_PL 229 282 927
ru_RU 238 233 967
sv_SE 304 266 868
tr_TR 298 269 871
zh_CN 206 317 915

Note: This PR delivers catalog sync (all .po files now contain all 1,438 msgid entries from the .pot). Most strings remain untranslated/fuzzy — this provides the structural baseline for community translators and GlotPress.

Verdict

Ready to merge — catalog sync verified, translations load correctly per locale, graceful fallback confirmed, no regressions.


🤖 Generated with Claude Code

- Remove hardcoded Local by Flywheel socket path, use MYSQL_CONFIG from env
- Use configurable WP_DB_PREFIX for table names instead of hardcoded wp_
- Type DB queries with RowDataPacket instead of `as any`
- Guard afterAll teardown against uninitialized pool
- Remove unused BASE_URL import
- Narrow Warning assertion to match PHP file paths, not UI copy
- Document fuzzy-string limitation in German translation assertion
@parhumm parhumm merged commit cd2d37e into development Mar 13, 2026
1 check passed
@parhumm parhumm deleted the fix/173-sync-translation-files branch March 13, 2026 11:07
@parhumm parhumm mentioned this pull request Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants