Skip to content

feat(i18n): answer storefront customers in their language - #164

Merged
roncodes merged 3 commits into
release/v0.4.30from
feat/server-localization
Oct 27, 2026
Merged

roncodes merged 3 commits into
release/v0.4.30from
feat/server-localization

Conversation

@roncodes

Copy link
Copy Markdown
Member

Summary

The storefront customer app already sends Accept-Language (e.g. mn) on every request (storefront-app/src/hooks/use-storefront.ts), but the server ignored it. Customers using the app in Mongolian or Ukrainian still got English API errors, push notifications, inbox items and SMS verification codes. With this PR, the server answers in the customer's language.

Related Issue

None.

Type of Change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Test
  • Chore

Implementation Notes

  • Request language. SetStorefrontLocale is first in the storefront.api group. It picks the closest shipped language from Accept-Language (mn-MN → mn; unsupported → en; no header → unchanged) and runs the request in it. It restores the previous locale afterwards, which matters for Octane workers. A language is supported when server/resources/lang/<code> exists.

  • Remembered language. When a signed-in customer (Customer-Token) sends a language different from the one stored, it is written to their contact meta (meta.locale). The write goes straight to the table, with no model events. A request where the language is unchanged costs one select.

  • Notifications. Queued notifications have no request, so StorefrontOrderNotification and StorefrontOrderChatMessage set the notification locale from the order customer's stored language. Laravel then sends every channel in that locale. Order notification text is built through trans(), and times use Carbon in that locale.

    • The order-edit summary is composed once in English for the console and the driver. A non-English customer therefore gets the translated "your order was updated" line instead of a mixed-language push. Translating the summary would mean reworking OrderEditor, so it was left out.
  • Strings. Customer-facing messages are now translation keys in storefront::api, checkout, customers and notifications, each in en, mn and uk. They cover:

    • the v1 controllers
    • SetStorefrontSession
    • the validation rules
    • ReviewEligibility
    • Cart
    • SMS verification and account-closure texts

    English text is byte-identical.

  • Left unchanged: error codes, gateway and other $e->getMessage() passthroughs, merchant-authored text (store and product names, campaigns, order activity labels), console and internal routes, and core-api / FleetOps messages. That includes AppReview::OFF_MESSAGE, phone validation, and Laravel's own validation messages, whose mn/uk files belong in fleetbase/fleetbase api/resources/lang.

  • Test bootstrap. scripts/pest-bootstrap.php gains a translator, __()/trans(), and getLocale()/setLocale() on the test container.

Validation

  • Tests
  • Lint
  • Build (no frontend change)
  • Manual validation (not run against the customer app)
php-cs-fixer on the changed files: clean.

Full Pest suite with coverage (local server_vendor, core-api 1.6.69):
  1158 tests, 0 failures, 40 errors. The 40 errors are "Fleetbase\Support\AppReview /
  ReviewAccess not found" (+2 networks.slug, +1 filesystem binding). The unchanged
  release/v0.4.30 gives the same 40 errors in the same five files.

Same suite with server_vendor's core-api pointed at the current core-api checkout:
  1158 tests, 0 failures, 0 errors.
  Coverage 13060 / 13112 statements. Every changed line is covered. The 52 uncovered
  lines are in files this PR does not touch (OAuth StorefrontController,
  StorefrontOrderCreated, Support/AppReview, OAuthScopes).

New: server/tests/Unit/Http/Middleware/SetStorefrontLocaleTest.php (10 tests)
  - language matching
  - locale restore
  - remembering only on change, signed-in only
  - mn/uk notification text
  - the English-only edit summary
  - en/mn/uk key and placeholder parity for every lang file

Heads-up, not from this PR: composer.lock pins fleetbase/core-api 1.6.69 while composer.json requires ^1.6.74. That is why PHP CI on the release PR (#162) fails, and why the App Review tests error locally.

Documentation Impact

  • No documentation changes needed
  • Documentation updated in fleetbase/fleetbase.io
  • Documentation needed but not included

API Reference Impact

  • No API reference changes needed
  • Updated fleetbase/postman
  • API reference updates required but not included

API reference notes: the Storefront API now honours an optional Accept-Language request header (en, mn, uk), which sets the language of error/message text. Payloads, status codes and error codes are unchanged. The Storefront collection could document the header.

Documentation Notes

Add a "Localization" note to the Storefront API docs in fleetbase/fleetbase.io. It should cover:

  • the Accept-Language header and the supported languages
  • that order notifications follow the customer's last-used language
  • how to add a language (copy server/resources/lang/en)

Risk

  • Customer messages change language only when the request sends Accept-Language. Requests without the header behave as before.
  • Browsers send Accept-Language, so storefront-web customers now get their browser language when it is mn or uk.
  • One select per signed-in request, plus a write when the language changes.
  • The Mongolian and Ukrainian copy was written without native review. Please have a native speaker read server/resources/lang/{mn,uk}, especially the Mongolian suffixes after store names (:store-ийн).

The customer app already sends Accept-Language on every request, but the
server ignored it, so API errors, push notifications, inbox items and SMS
codes stayed English for a customer using the app in Mongolian or Ukrainian.

SetStorefrontLocale (first in the storefront.api group) picks the closest
shipped language from Accept-Language, answers the request in it, restores
the previous locale afterwards, and stores it on the signed-in customer's
contact meta when it changed. Order notifications read that stored language
(CustomerLocale::for) and set it as the notification locale, so queued pushes
are written in it too. An order edit summary is English for the console and
driver, so non-English customers get the general "order was updated" line.

Customer API messages in the v1 controllers, SetStorefrontSession, the
validation rules and ReviewEligibility move to storefront::api, checkout,
customers and notifications translation files under server/resources/lang,
with en, mn and uk. English text is unchanged. Error codes and
gateway/passthrough messages stay as they are.

The Pest bootstrap gains a translator, __() and app()->getLocale()/setLocale().
@roncodes roncodes added needs-docs Requires documentation updates needs-api-spec Requires API specification updates needs-human-review Requires human review before proceeding type:feature Feature or enhancement labels Oct 27, 2026
From a native speaker's review: customer_not_found, no_customer_with_phone
and customer_user_not_found now add "Шинэ хэрэглэгчээр бүртгүүлнэ үү."
customer_not_found (update, find, delete) and customer_user_not_found (account
closure) are not sign-in errors, so telling the customer to register there
read wrongly. Only no_customer_with_phone (loginWithPhone) keeps the hint.
@roncodes

Copy link
Copy Markdown
Member Author

API reference: fleetbase/postman#72 documents Accept-Language for the Storefront collection and adds a locale variable that sends it.

@roncodes
roncodes merged commit 4599d28 into release/v0.4.30 Oct 27, 2026
@roncodes
roncodes deleted the feat/server-localization branch October 27, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-api-spec Requires API specification updates needs-docs Requires documentation updates needs-human-review Requires human review before proceeding type:feature Feature or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant