Skip to content

feat(i18n): use Vazirmatn font for Persian locale - #1781

Merged
giswqs merged 3 commits into
opengeos:mainfrom
RMA1313:feat/persian-vazirmatn-font
Aug 8, 2026
Merged

feat(i18n): use Vazirmatn font for Persian locale#1781
giswqs merged 3 commits into
opengeos:mainfrom
RMA1313:feat/persian-vazirmatn-font

Conversation

@RMA1313

@RMA1313 RMA1313 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Use Vazirmatn as the UI font for the Persian (fa) locale.

IBM Plex Sans does not provide Persian glyph coverage, so Persian text currently falls back to the system font. This change adds Vazirmatn as a self-hosted font and applies it only when the active locale is Persian.

Changes

  • Add @fontsource-variable/vazirmatn
  • Load Vazirmatn using the existing self-hosted font setup
  • Apply Vazirmatn only to the Persian (fa) locale
  • Keep the existing IBM Plex Sans font stack unchanged for other locales

Testing

  • npm run build -w geolibre-desktop

Summary by CodeRabbit

  • Style
    • Added improved Persian and Arabic typography using the self-hosted Vazirmatn variable font.
    • Updated the font stack to prioritize Vazirmatn while retaining existing and system fallback fonts.
    • Clarified how application font stylesheets are loaded.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 65935bdd-04d0-4ce0-a9aa-b42abf8e2cfc

📥 Commits

Reviewing files that changed from the base of the PR and between f98343c and 67bbb91.

📒 Files selected for processing (1)
  • apps/geolibre-desktop/src/index.css

📝 Walkthrough

Walkthrough

The desktop app adds the Vazirmatn Variable package, defines an offline Arabic-subset font face, and applies a Persian-specific font stack while retaining IBM Plex Sans and system fallbacks.

Changes

Persian Font Support

Layer / File(s) Summary
Font loading and language selection
apps/geolibre-desktop/package.json, apps/geolibre-desktop/src/index.css
The app adds Vazirmatn Variable, documents Fontsource loading from main.tsx, defines an Arabic-subset WOFF2 font face, and prioritizes Vazirmatn for Persian text.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: giswqs, craun718

Poem

A rabbit hops through Persian type,
Vazirmatn makes each glyph shine bright.
Arabic marks now load with care,
IBM Plex still waits there.
Fallbacks guard the final line.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the Persian locale font change described in the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.6)
apps/geolibre-desktop/src/index.css

File contains syntax errors that prevent linting: Line 6: Tailwind-specific syntax is disabled.; Line 22: Tailwind-specific syntax is disabled.; Line 102: Tailwind-specific syntax is disabled.; Line 138: Tailwind-specific syntax is disabled.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


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.

@giswqs

giswqs commented Aug 8, 2026

Copy link
Copy Markdown
Member

/claude-review

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

🔍 Cloudflare PR preview

Item Value
Site https://4146f401.geolibre-preview.pages.dev
Demo app https://4146f401.geolibre-preview.pages.dev/demo/
Commit f98343c

Comment on lines +30 to +35
html:lang(fa) {
--font-sans:
"Vazirmatn Variable", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
"Noto Sans Arabic", "Noto Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor doc drift: the block comment above (lines 18-21) says the locales Plex doesn't cover, including fa, "rely on per-glyph fallback to the system stack below." That's no longer true for fa once this override is in place — it now gets Vazirmatn instead of falling back. Worth updating that comment (e.g. "except fa, which uses Vazirmatn below") so it doesn't mislead the next reader about which locales are still on system fallback.

Confidence: medium.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Code review

Bugs: None found. The html:lang(fa) CSS custom-property override is a valid Tailwind v4 pattern (theme variables are resolved via var() at use time, so a higher-specificity selector on the same element correctly overrides --font-sans), and document.documentElement.lang is confirmed to be set to the exact catalog code "fa" by i18n/index.ts, matching the :lang(fa) selector. Confidence: high.

Security: None found. Font is self-hosted (consistent with existing IBM Plex Sans handling and the Tauri CSP's default-src 'self'), no new external host introduced. Confidence: high.

Performance: None found. Adds one additional self-hosted variable font import, following the exact same wght.css-from-JS pattern already used for IBM Plex Sans (needed so Vite's asset pipeline rewrites the font URLs). Confidence: high.

Quality:

  • The block comment at index.css:18-21 states that locales Plex doesn't cover — including fa — fall back to the system font stack. That's now stale: the html:lang(fa) block added directly below gives fa its own dedicated font (Vazirmatn) rather than falling back. Left an inline suggestion to update the comment. Confidence: medium.
  • Minor, unverified: the override selector name "Vazirmatn Variable" follows the same naming convention as "IBM Plex Sans Variable", but since node_modules isn't present in this environment I couldn't confirm the actual font-family declared by @fontsource-variable/vazirmatn/wght.css. A mismatch wouldn't fail the build — it would just silently fall back to the system stack. Worth a quick visual check that Persian text actually renders in Vazirmatn. Confidence: low.

CLAUDE.md: No violations found. Dependency added in the correct alphabetical position in package.json, package-lock.json updated accordingly, and no new CSP host is required since the font is self-hosted like the existing typefaces.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

🔍 GitHub Pages PR preview

Item Value
Site https://opengeos.org/pages-preview/GeoLibre/pr-1781/
Demo app https://opengeos.org/pages-preview/GeoLibre/pr-1781/demo/
Commit f98343c

Note

GitHub Pages built this preview successfully, but its serving edge returned HTTP 403 when checked. The links may still be propagating.

@giswqs

giswqs commented Aug 8, 2026

Copy link
Copy Markdown
Member

Nice fix — Persian genuinely had no glyph coverage before this. One suggestion on the bundle cost, with numbers from building main and this branch back to back.

The cost today

@fontsource-variable/vazirmatn/wght.css declares all three of the font's subsets, so Vite emits all three:

asset bytes
vazirmatn-arabic-wght-normal.woff2 46,308
vazirmatn-latin-wght-normal.woff2 34,524
vazirmatn-latin-ext-wght-normal.woff2 21,860
total 102,692

Whole-dist delta is +105,462 B (102,692 of woff2, +1,931 B of @font-face CSS in index-*.css, and the rest precache-manifest growth). No JS change.

The part worth flagging is the service worker: the three files land in the Workbox precache manifest, which goes from 458 entries / 15,707.43 KiB to 461 / 15,809.35 KiB+101.92 KiB. Precaching fetches every entry on SW install, so this isn't lazy per-unicode-range font loading; once the SW registers, every user pays the full ~100 KiB whether or not they ever select Persian.

Suggestion: load only the Arabic subset

Persian only needs the arabic subset. The two Latin subsets are 56,384 B — 55% of the added weight — and the app already ships IBM Plex Sans for Latin.

There's no subset-scoped entry point to import (the package publishes only index.css and wght.css, both listing all three subsets), but ./files/*.woff2 is exported, so a hand-written @font-face in index.css works. Drop the main.tsx import and add:

@font-face {
  font-family: "Vazirmatn Variable";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url("@fontsource-variable/vazirmatn/files/vazirmatn-arabic-wght-normal.woff2")
    format("woff2-variations");
  unicode-range: U+0600-06FF,U+0750-077F,U+0870-088E,U+0890-0891,U+0897-08E1,U+08E3-08FF,U+200C-200E,U+2010-2011,U+204F,U+2E41,U+FB50-FDFF,U+FE70-FE74,U+FE76-FEFC,U+102E0-102FB,U+10E60-10E7E,U+10EC2-10EC4,U+10EFC-10EFF,U+1EE00-1EE03,U+1EE05-1EE1F,U+1EE21-1EE22,U+1EE24,U+1EE27,U+1EE29-1EE32,U+1EE34-1EE37,U+1EE39,U+1EE3B,U+1EE42,U+1EE47,U+1EE49,U+1EE4B,U+1EE4D-1EE4F,U+1EE51-1EE52,U+1EE54,U+1EE57,U+1EE59,U+1EE5B,U+1EE5D,U+1EE5F,U+1EE61-1EE62,U+1EE64,U+1EE67-1EE6A,U+1EE6C-1EE72,U+1EE74-1EE77,U+1EE79-1EE7C,U+1EE7E,U+1EE80-1EE89,U+1EE8B-1EE9B,U+1EEA1-1EEA3,U+1EEA5-1EEA9,U+1EEAB-1EEBB,U+1EEF0-1EEF1;
}

(unicode-range copied verbatim from the package's wght.css — worth keeping it exact.) The npm dependency stays; only the CSS entry point changes.

I built this variant to check it against the Tailwind v4 hazard documented above the font imports in main.tsx — that footgun is specific to @importing fontsource's own CSS with its relative url(./files/*.woff2). A hand-written @font-face in index.css using a bare package specifier is rewritten by Vite normally, and exactly one file is emitted:

dist/assets/vazirmatn-arabic-wght-normal-Cafbb7Zc.woff2   46,308 B
dist delta vs main precache delta
this PR +105,462 B +101.92 KiB (3 entries)
arabic-only +47,477 B +45.44 KiB (1 entry)

Saves 57,985 B of shipped assets and 56.48 KiB of precache.

One thing to pair with it

The html:lang(fa) stack currently goes "Vazirmatn Variable", ui-sans-serif, system-ui, … — it doesn't include IBM Plex Sans. With all three subsets loaded, Latin text in the Persian UI renders in Vazirmatn Latin; with the Arabic subset only, it would fall through to ui-sans-serif/system-ui and no longer match the rest of the app. Adding "IBM Plex Sans" right after Vazirmatn fixes that at zero extra bytes, since Plex is already loaded:

--font-sans:
  "Vazirmatn Variable", "IBM Plex Sans", ui-sans-serif, system-ui, …;

That arguably makes Persian more consistent with every other locale than the current version does, on top of being smaller.

Importing @fontsource-variable/vazirmatn/wght.css pulled in all three of the
font's subsets — arabic (46,308 B), latin (34,524 B) and latin-ext (21,860 B).
The two Latin subsets duplicate coverage IBM Plex Sans already provides, and
because every emitted asset lands in the Workbox precache manifest, they were
not lazily fetched per unicode-range: the service worker downloaded all three
on install for every user, whatever their locale.

Fontsource publishes no subset-scoped entry point (index.css and wght.css both
declare all three), so declare the @font-face by hand against the package's
exported ./files/*.woff2. The Tailwind-inlining hazard documented in main.tsx
does not apply — that one is specific to @importing fontsource's own CSS, whose
relative url(./files/*.woff2) never reaches Vite's asset pipeline.

Also add IBM Plex Sans to the fa stack, after Vazirmatn. Without the Latin
subsets the Latin text that remains in a Persian UI (layer names, coordinates,
product names) would drop to system-ui; this keeps it on the same typeface as
every other locale, at no extra bytes since Plex is already loaded.

dist delta vs main: +105,462 B -> +48,007 B. Precache: +101.92 KiB (3 entries)
-> +45.95 KiB (1 entry).
@giswqs

giswqs commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pushed this to the branch as 67bbb91 rather than leaving it as a review note — hope that's alright, and thanks for the PR.

Verified in a real browser against the production build (?locale=fa):

  • <html lang="fa" dir="rtl">, and Persian text (پروژه) computes to "Vazirmatn Variable", "IBM Plex Sans Variable", …
  • document.fonts.check('16px "Vazirmatn Variable"')true
  • exactly three font requests, all 200: vazirmatn-arabic-wght-normal, ibm-plex-sans-latin-wght-normal, ibm-plex-mono-latin-400-normal — the Latin fallback resolves to Plex as intended, so the status bar and coordinate readouts keep the same typeface as every other locale

npm run build, the frontend suite (5,499 passing), and pre-commit are all green.

@giswqs
giswqs merged commit f2142fc into opengeos:main Aug 8, 2026
3 checks passed
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