Skip to content

Migrate translations from Transifex to i18next#37

Merged
kilbot merged 8 commits intomainfrom
feature/i18next-migration-v2
Feb 3, 2026
Merged

Migrate translations from Transifex to i18next#37
kilbot merged 8 commits intomainfrom
feature/i18next-migration-v2

Conversation

@kilbot
Copy link
Copy Markdown
Contributor

@kilbot kilbot commented Feb 2, 2026

Summary

  • Replace @transifex/native and @transifex/react with i18next and react-i18next
  • Custom RxDBBackend i18next plugin fetches translations from jsDelivr CDN and caches in RxDB
  • Translation version sourced from site.wcpos_version so CDN URLs match the installed plugin release
  • Custom ElectronStoreBackend for the electron app with electron-store caching
  • Remove _tags from all t() calls across 166+ files (namespace set via defaultNS config)
  • Delete old cache.ts files superseded by backend plugins

Test plan

  • Load app with a non-English locale, confirm strings are translated
  • Check network tab for jsDelivr CDN requests
  • Confirm English fallback works when translations unavailable
  • Test electron app menu translations
  • Run TypeScript build to verify no type errors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Migrated internationalization to the i18next/react-i18next ecosystem and swapped the translation provider/backend; updated an Electron submodule pointer.
  • User-facing

    • Simplified and standardized translation usage across the UI (labels, placeholders, tooltips, dialogs) for more consistent localization.
  • Bug Fixes

    • Clarified and standardized several user-facing error messages and logging for more consistent diagnostics.

Replace @transifex/native and @transifex/react with i18next and react-i18next.
Use custom RxDBBackend that fetches translations from jsDelivr CDN and caches
them in RxDB. Translation version is sourced from site.wcpos_version via
useObservableEagerState so CDN URLs point to the correct release tag.

Remove _tags from all t() calls across 166 files since namespace is now set
via defaultNS config. Delete cache.ts files that are superseded by the
backend plugins.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 2, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Replaces Transifex-based i18n with an i18next/react-i18next provider and custom RxDBBackend, removes a custom translation cache, updates package deps, performs a bulk removal of { _tags: 'core' } from many t(...) calls, and bumps the apps/electron submodule pointer.

Changes

Cohort / File(s) Summary
i18n infra & deps
packages/core/package.json, packages/core/src/contexts/translations/index.tsx
Swap Transifex deps for i18next/react-i18next; add i18next-based TranslationProvider and custom RxDBBackend integrating with translationsState and jsDelivr.
Removed cache implementation
packages/core/src/contexts/translations/cache.ts
Deleted the previous CustomCache default-export class (translation cache implementation removed).
Bulk translation cleanup
packages/core/src/screens/... (many files under screens/main, screens/auth, pos, products, settings, reports, logs, etc.)
Removed { _tags: 'core' } (and some _context) from hundreds of t(...) calls; minor additions of errorMessage normalization and a few loading resets. No control-flow changes beyond i18n call shapes.
Auth hooks & API discovery
packages/core/src/screens/auth/hooks/...
Introduced ApiDiscoveryError and adjusted error-throwing/propagation in API discovery and related hooks; replaced several tagged translation error messages with plain t(...) usage or thrown ApiDiscoveryError.
POS / cart / checkout
packages/core/src/screens/main/pos/...
Dropped _tags from translations across POS flows; standardized several error logging paths (compute/use errorMessage) while preserving core cart/checkout logic.
UI components (forms/selects/comboboxes)
packages/core/src/screens/main/components/...
Updated many label/placeholders/empty states to call t('...') without _tags; purely translation argument updates, no control-flow changes.
Submodule & tests
apps/electron, apps/main/e2e/orders.spec.ts
Updated apps/electron submodule pointer; e2e test orders.spec.ts adjusted row selection to target the first table row instead of filtering by status.

Sequence Diagram(s)

sequenceDiagram
    participant App as App (React)
    participant Prov as TranslationProvider (i18next)
    participant DB as translationsState (RxDB)
    participant CDN as jsDelivr (CDN)

    App->>Prov: mount / render provider
    Prov->>DB: read(locale, wcposVersion)
    DB-->>Prov: return cached translations (if any)
    Prov->>i18next: createInstance/init + addResourceBundle(cached)
    Prov->>CDN: async fetch translations for locale/version
    CDN-->>Prov: respond with translations (if newer)
    Prov->>DB: persist updated translations
    Prov->>i18next: addResourceBundle(updated)
    App->>i18next: useTranslation() → t(...) calls resolve strings
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🐰 I hopped from tags to fields so wide,
i18next sings where caches hide,
RxDB keeps words safe and sound,
CDN brings fresh strings all around,
A rabbit cheers — translations glide!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.38% 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 'Migrate translations from Transifex to i18next' is clear and directly summarizes the main change across this extensive changeset.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/i18next-migration-v2

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 2, 2026

🚀 Deployment Summary

Item Status
Preview URL https://wcpos--o7nr8getdw.expo.app
E2E Tests ✅ Passed
Commit bf40cba

🔗 Quick Links


🤖 Updated by GitHub Actions

Copy link
Copy Markdown
Contributor

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/screens/main/pos/cart/buttons/pay.tsx (1)

58-65: ⚠️ Potential issue | 🟡 Minor

Preserve error details when non-Error values are thrown.

If error is a string or plain object, error.message is undefined and the user-facing message becomes the generic fallback. Consider using the same fallback logic you already use in the context so the toast can surface the actual error text.

✅ Suggested fix
-			orderLogger.error(t('{message}', {message: error.message || 'Error' }), {
+			const errorMessage = error instanceof Error ? error.message : String(error);
+			orderLogger.error(t('{message}', { message: errorMessage || 'Error' }), {
 				showToast: true,
 				saveToDb: true,
 				context: {
 					errorCode: ERROR_CODES.TRANSACTION_FAILED,
-					error: error instanceof Error ? error.message : String(error),
+					error: errorMessage,
 				},
 			});
packages/core/src/screens/main/customers/add.tsx (1)

58-66: ⚠️ Potential issue | 🟡 Minor

Extract error message once and reuse across both log message and context object.

Line 59 accesses error.message directly without type narrowing, while line 64 properly guards with instanceof Error check. This inconsistency can be resolved by deriving the safe string once at the beginning of the catch block and reusing it in both places.

Suggested fix
 			} catch (error) {
-				mutationLogger.error(t('{message}', {message: error.message || 'Error' }), {
+				const errorMessage =
+					error instanceof Error ? error.message : String(error ?? 'Error');
+				mutationLogger.error(t('{message}', { message: errorMessage }), {
 					showToast: true,
 					saveToDb: true,
 					context: {
 						errorCode: ERROR_CODES.TRANSACTION_FAILED,
-						error: error instanceof Error ? error.message : String(error),
+						error: errorMessage,
 					},
 				});
🤖 Fix all issues with AI agents
In `@packages/core/src/contexts/translations/index.tsx`:
- Around line 22-25: The i18nInstance is memoized only on translationsState so
RxDBBackend.init() captures a stale wcposVersion; update the useMemo dependency
array for i18nInstance to include wcposVersion so a new i18nInstance (and thus
RxDBBackend.init()) runs when the version changes, ensuring the backend's
captured version used by read()/reloadResources() is current; locate the useMemo
that creates i18nInstance and add wcposVersion to its dependency list.

In `@packages/core/src/screens/auth/components/wp-user.tsx`:
- Around line 128-133: The user-facing copy in AlertDialogDescription inside
wp-user.tsx uses "effect" instead of "affect"; update the string passed to the
t(...) call to read "Are you sure you want to remove this user? Removing a user
from the POS will not affect any data on the server." and, if your i18n system
uses source strings as keys, update the corresponding translation entries for
that key (or add a new key) so translations remain correct; reference the
AlertDialogDescription component and the t(...) invocation to locate and change
the text.

In `@packages/core/src/screens/main/pos/cart/add-customer.tsx`:
- Line 93: Replace the current pattern that attempts to translate arbitrary
error text (the t('{message}', {message: error.message || 'Error' }) call in
add-customer.tsx) with a stable user-facing translation key (e.g.,
t('cart.addCustomerError') or similar) and display that to users; preserve the
original error for debugging by including error.message in the cartLogger.error
context or a separate log entry (i.e., call cartLogger.error('Add customer
failed', { error }) or add error to the metadata) so you don’t pass raw
technical text into the translator.

In `@packages/core/src/screens/main/settings/theme.tsx`:
- Around line 41-71: themeOptions is calling t(...) when setting descriptionKey
which pre-translates the text; change each descriptionKey to a raw translation
key string (e.g. "Follow your device settings", "Clean and bright", etc.)
instead of t('...') so it matches how labelKey is stored, and ensure the
component that renders themes uses the translator (the same place labelKey is
translated — see where labelKey is looked up around the render logic at
functions/components referencing themeOptions) to translate descriptionKey at
render time (like labelKey at lines referenced).
🧹 Nitpick comments (10)
packages/core/src/screens/main/page-upgrade.tsx (1)

29-33: Remove leftover empty objects from translation calls.

The description translation calls have empty objects {} that appear to be leftovers from the _tags removal. These should be cleaned up for consistency with the title calls.

♻️ Proposed fix
 			case 'products':
 				return {
 					imageURL: 'https://wcpos.com/products-upgrade.png',
 					title: t('Upgrade to Pro'),
-					description: t(
-						'Adjust product prices and quantities by upgrading to WooCommerce POS Pro',
-						{
-						}
-					),
+					description: t('Adjust product prices and quantities by upgrading to WooCommerce POS Pro'),
 					demoURL: 'https://demo.wcpos.com/pos/products',
 				};
 			case 'orders':
 				return {
 					imageURL: 'https://wcpos.com/orders-upgrade.png',
 					title: t('Upgrade to Pro'),
-					description: t(
-						'Re-open and print receipts for older orders by upgrading to WooCommerce POS Pro',
-						{
-						}
-					),
+					description: t('Re-open and print receipts for older orders by upgrading to WooCommerce POS Pro'),
 					demoURL: 'https://demo.wcpos.com/pos/orders',
 				};
 			case 'customers':
 				return {
 					imageURL: 'https://wcpos.com/customers-upgrade.png',
 					title: t('Upgrade to Pro'),
-					description: t(
-						'Add new customers and edit existing customers by upgrading to WooCommerce POS Pro',
-						{
-						}
-					),
+					description: t('Add new customers and edit existing customers by upgrading to WooCommerce POS Pro'),
 					demoURL: 'https://demo.wcpos.com/pos/customers',
 				};
 			case 'reports':
 				return {
 					imageURL: 'https://wcpos.com/reports-upgrade.png',
 					title: t('Upgrade to Pro'),
-					description: t('Unlock End of Day Reports by upgrading to WooCommerce POS Pro', {
-					}),
+					description: t('Unlock End of Day Reports by upgrading to WooCommerce POS Pro'),
 					demoURL: 'https://demo.wcpos.com/pos/reports',
 				};

Also applies to: 40-44, 51-55, 62-63

packages/core/src/screens/main/pos/cart/buttons/edit-order-meta/form.tsx (1)

89-96: Consider cleaning up or updating commented-out code.

The commented-out code on lines 89 and 96 has been updated to remove _tags, but if this code is intended to be re-enabled in the future, note that i18next uses double curly braces {{variable}} for interpolation by default, not single braces {variable}. The current syntax t('Order #{number} saved', {number: ...}) and t('{message}', {message: ...}) would need adjustment.

If this code is no longer needed, consider removing it entirely to reduce noise.

packages/core/src/screens/auth/hooks/use-url-discovery.ts (1)

129-141: Consider preserving the specific error message.

The specific error message "Site does not seem to be a WordPress site" thrown at line 130 is replaced with a generic "Failed to discover WordPress API" in the catch block at line 138. This loses useful diagnostic information for the user.

♻️ Optional: Preserve specific error messages
 			if (!discoveredUrl) {
-				throw new Error(t('Site does not seem to be a WordPress site'));
+				const errorMessage = t('Site does not seem to be a WordPress site');
+				setError(errorMessage);
+				setStatus('error');
+				return null;
 			}

 			setWpApiUrl(discoveredUrl);
 			setStatus('success');
 			discoveryLogger.debug(`WordPress API URL discovered: ${discoveredUrl}`);
 			return discoveredUrl;
-		} catch {
-			const errorMessage = t('Failed to discover WordPress API');
+		} catch (err) {
+			const errorMessage = err instanceof Error ? err.message : t('Failed to discover WordPress API');
 			setError(errorMessage);
 			setStatus('error');
 			return null;
 		}
packages/core/src/screens/main/products/use-barcode.ts (1)

33-40: Consider using i18next's built-in pluralization.

The current approach uses two separate translation keys for singular/plural cases. i18next supports automatic pluralization based on count, which handles complex plural rules for different languages.

♻️ Optional refactor using i18next pluralization
 		if (results.length === 1) {
-			text2 = t('1 product found locally');
-		}
-
-		if (results.length === 0 || results.length > 1) {
-			text2 = t('{count} products found locally', { count: results.length });
+			text2 = t('product_found_locally', { count: 1 });
+		} else {
+			text2 = t('product_found_locally', { count: results.length });
 			isError = true;
 		}

This requires adding plural keys to the translation files:

{
  "product_found_locally_one": "{{count}} product found locally",
  "product_found_locally_other": "{{count}} products found locally"
}
packages/core/src/screens/main/pos/hooks/use-add-customer.ts (1)

68-68: Minor formatting inconsistency in object literal.

The object {customerName } has inconsistent spacing. Consider normalizing to { customerName } for consistency with standard formatting conventions.

✨ Suggested formatting fix
-			orderLogger.success(t('Customer assigned: {customerName}', {customerName }), {
+			orderLogger.success(t('Customer assigned: {customerName}', { customerName }), {
packages/core/src/screens/main/customers/cells/actions.tsx (1)

120-124: Remove empty options object from translation call.

The empty {} options object is unnecessary and appears to be a leftover from removing _tags: 'core'.

🧹 Suggested cleanup
 								{t(
 									'Are you sure you want to delete this user? Deleting a user is permanent, there is no Trash for WordPress users.',
-									{
-									}
 								)}
packages/core/package.json (1)

42-43: Consider upgrading i18next and react-i18next to newer major versions.

The specified versions (i18next@^24.0.0 and react-i18next@^15.0.0) are valid and compatible with each other. However, both packages have newer major releases available: i18next 25.8.0 and react-i18next 16.5.4. If there are no constraints preventing the upgrade, consider using the latest versions for better bug fixes, performance improvements, and security updates.

packages/core/src/screens/main/logs/filter-bar.tsx (1)

19-25: Consider translating the log level labels.

The items array contains hardcoded English labels ('Error', 'Warning', 'Info', 'Success', 'Audit') that are not wrapped in t() calls, unlike most other UI strings in this PR. If these should be localized, they would need translation wrappers.

Example if translation is desired
+const useItems = () => {
+  const t = useT();
+  return React.useMemo(() => [
+    { value: 'error', label: t('Error') },
+    { value: 'warn', label: t('Warning') },
+    { value: 'info', label: t('Info') },
+    { value: 'success', label: t('Success') },
+    { value: 'audit', label: t('Audit') },
+  ], [t]);
+};
packages/core/src/screens/main/products/edit/variation/form.tsx (2)

114-114: Verify the translation pattern for error messages.

The translation key '{message}' with interpolation {message: error.message || 'Error'} will just pass through the raw error message without actual translation. If error.message contains technical or English-only text, it won't be localized.

If the intent is to display the raw error message, consider removing the t() wrapper:

-mutationLogger.error(t('{message}', {message: error.message || 'Error' }), {
+mutationLogger.error(error.message || t('Error'), {

Alternatively, if error messages should be translated, use a more specific translation key with the error as context.


63-63: Consider avoiding translated strings in thrown errors.

Throwing an error with a translated message (throw new Error(t('Variation not found'))) may cause issues with error tracking systems that group errors by message. Additionally, developers debugging in different locales may see localized error messages.

Consider keeping error messages in English and translating only at the display layer, or using error codes:

-throw new Error(t('Variation not found'));
+throw new Error('Variation not found');

- Convert 'product found locally' strings to use i18next plural keys
  (product_found_locally_one/other) instead of conditional singular/plural
- Convert 'variation found for term' strings similarly
- Translation files should provide _one and _other suffixed keys for
  proper support of languages with multiple plural forms (Russian, Arabic, etc.)
- Fix prettier formatting issues caught by linter
- Update pnpm-lock.yaml with i18next dependencies
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 3, 2026

📊 Test Coverage Report

Package Statements Branches Functions Lines
@wcpos/core 🔴 36.5% 🔴 43.2% 🔴 42.0% 🔴 35.3%
@wcpos/components 🔴 44.9% 🟡 60.9% 🔴 24.2% 🔴 47.3%
@wcpos/database 🔴 33.2% 🔴 35.9% 🔴 40.3% 🔴 32.9%
@wcpos/hooks 🔴 45.4% 🔴 46.4% 🔴 44.8% 🔴 45.6%
@wcpos/utils 🔴 35.0% 🔴 0.0% 🔴 50.0% 🔴 33.3%
@wcpos/query 🟡 68.0% 🔴 52.6% 🟡 66.1% 🟡 68.0%
Average 🔴 43.8% 🔴 39.9% 🔴 44.5% 🔴 43.7%
Coverage Legend
  • 🟢 Good (≥80%)
  • 🟡 Moderate (60-79%)
  • 🔴 Needs improvement (<60%)
  • ⚪ No coverage data
--- 🤖 Updated by GitHub Actions • [View full report](https://github.com/wcpos/monorepo/actions/runs/21651464742)

Copy link
Copy Markdown
Contributor

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/screens/auth/hooks/use-api-discovery.ts (1)

116-123: ⚠️ Potential issue | 🟠 Major

Error re-throw logic will break for non-English locales.

The checks on lines 119-120 compare error.message against the English source strings ('Bad API response', 'WordPress API not found'). After i18next translates these, the comparison will fail for any non-English locale, causing the catch block to re-log and re-throw already-handled errors.

Consider using a custom error class or error code property instead of string matching:

🔧 Proposed fix using custom error class
+class ApiDiscoveryError extends Error {
+  constructor(message: string) {
+    super(message);
+    this.name = 'ApiDiscoveryError';
+  }
+}
+
 const fetchApiIndex = React.useCallback(
   async (wpApiUrl: string): Promise<WpJsonResponse> => {
     try {
       // ...
       if (!data || typeof data !== 'object') {
         // ...
-        throw new Error(t('Bad API response'));
+        throw new ApiDiscoveryError(t('Bad API response'));
       }
       // ...
       if (!namespaces || !Array.isArray(namespaces)) {
         // ...
-        throw new Error(t('WordPress API not found'));
+        throw new ApiDiscoveryError(t('WordPress API not found'));
       }
       // ...
     } catch (error) {
-      if (
-        error.message.includes('Bad API response') ||
-        error.message.includes('WordPress API not found')
-      ) {
+      if (error instanceof ApiDiscoveryError) {
         throw error;
       }
       // Handle network/connection errors
       // ...
     }
   },
   [http, t]
 );
🧹 Nitpick comments (2)
packages/core/src/screens/auth/hooks/use-api-discovery.ts (1)

212-214: Remove unnecessary empty object argument.

The empty object {} passed to t() serves no purpose and is inconsistent with all other t() calls in this file. This appears to be a remnant from the migration.

🧹 Proposed fix
 throw new Error(
-  t('WCPOS login URL is invalid. Please ensure WCPOS plugin is properly configured', {})
+  t('WCPOS login URL is invalid. Please ensure WCPOS plugin is properly configured')
 );
packages/core/src/screens/main/products/edit/variation/form.tsx (1)

114-122: Unnecessary translation wrapper around dynamic error message.

Using t('{message}', { message: error.message || 'Error' }) doesn't translate anything meaningful—it just passes through the interpolated value. The error.message content won't be translated since it's a dynamic runtime value.

Consider using the error message directly or providing a static translated fallback:

Suggested fix
-			mutationLogger.error(t('{message}', { message: error.message || 'Error' }), {
+			mutationLogger.error(error.message || t('Failed to save variation'), {

kilbot and others added 2 commits February 3, 2026 20:21
- Add wcposVersion to useMemo deps in TranslationProvider so the backend
  fetches from the correct CDN URL when the version changes
- Use ApiDiscoveryError class instead of string comparison for error
  re-throw logic (breaks in non-English locales)
- Store theme descriptionKey as raw strings, translate at render time
  (consistent with labelKey pattern)
- Fix "effect" → "affect" typo in user-facing copy
- Replace t('{message}', { message: error.message }) pattern with static
  translated messages (e.g. t('Failed to save product')) across 10 files
- Translate log level labels in filter-bar
- Preserve specific error messages in use-url-discovery catch block
- Clean up leftover empty {} from t() calls
Copy link
Copy Markdown
Contributor

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/screens/main/orders/edit/form.tsx (1)

205-213: ⚠️ Potential issue | 🔴 Critical

errorMessage is not defined in this scope — will cause ReferenceError.

The catch block at line 205 uses errorMessage (line 210) but never defines it. This will throw a ReferenceError at runtime when a customer fetch fails.

🐛 Proposed fix
 			} catch (error) {
+				const errorMessage = error instanceof Error ? error.message : String(error);
 				mutationLogger.error('Error fetching customer', {
 					context: {
 						errorCode: ERROR_CODES.RECORD_NOT_FOUND,
 						customerId,
 						error: errorMessage,
 					},
 				});
 			}
🤖 Fix all issues with AI agents
In `@packages/core/src/contexts/translations/index.tsx`:
- Around line 12-50: The RxDBBackend currently calls the global i18next
singleton in read(), so background updates don't reach the instance created by
TranslationProvider; modify init() to save the passed services object (e.g.,
this.services = _services) and in read() replace i18next.addResourceBundle(...)
with the instance-scoped call
this.services.resourceStore.addResourceBundle(language, namespace, data, true,
true) (or the appropriate addResourceBundle on the saved services), ensuring you
still check and update this.translationsState via
this.translationsState.set(language, ...) as before.

- Use instance resourceStore instead of global i18next singleton in both
  RxDBBackend and ElectronStoreBackend, so background CDN fetches update
  the correct i18n store
- Remove dead commented-out code and orphaned imports in edit-order-meta
  form
- Use named import { createInstance } instead of default i18next import
- Add locale to useMemo and useEffect dependency arrays
Copy link
Copy Markdown
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@packages/core/src/contexts/translations/index.tsx`:
- Around line 69-71: The i18next init options currently set fallbackLng: false
which disables any fallback language; change the init options object (the one
containing lng: locale, fallbackLng, ns: ['core']) to use fallbackLng: 'en' so
missing translations fall back to English, and keep the other keys (lng and ns)
unchanged; ensure this replacement is applied where the i18n initialization
occurs (the object with keys lng, fallbackLng, ns).
🧹 Nitpick comments (1)
packages/core/src/contexts/translations/index.tsx (1)

63-66: Avoid re-creating the i18n instance on every locale change.

changeLanguage already handles locale changes; rebuilding the instance can reset resource store and event bindings. Consider keeping the instance stable by removing locale from the memo deps.

♻️ Suggested refactor
-	}, [locale, translationsState, wcposVersion]);
+	}, [translationsState, wcposVersion]);

Also applies to: 86-86

Comment on lines +69 to +71
lng: locale,
fallbackLng: false,
ns: ['core'],
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

i18next fallbackLng false documentation behavior English fallback

💡 Result:

  • In i18next, fallbackLng defaults to "dev" (developer language). So if you don’t set fallbackLng, you may see fallback behavior that is not English. [1][2][3]
  • Setting fallbackLng: "en" (or an ordered list like ["fr","en"]) enables fallback to English (or your specified chain) when a key is missing in the active language. [1][3]
  • Setting fallbackLng: false turns the fallback language feature off: i18next will not load any fallback language resource files and will not look up missing keys in a fallback language. [1][2]
  • Note: even with fallbackLng: false, i18next can still do variant resolution (e.g., en-GBen) unless you also constrain loading/behavior via options like load. [3]

Sources: i18next init docs and configuration options, plus i18next fallback principles docs. [1][2][3]


🏁 Script executed:

cat -n packages/core/src/contexts/translations/index.tsx | head -80 | tail -30

Repository: wcpos/monorepo

Length of output: 1033


fallbackLng: false disables fallback entirely, preventing English fallback from working.

Setting fallbackLng: false turns off the fallback language feature in i18next — it will not load fallback language resources or look up missing keys in a fallback language. This conflicts with the PR objective of confirming English fallback works when translations are unavailable.

Configure it as fallbackLng: 'en' to enable proper English fallback:

🛠️ Suggested fix
-				fallbackLng: false,
+				fallbackLng: 'en',
📝 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
lng: locale,
fallbackLng: false,
ns: ['core'],
lng: locale,
fallbackLng: 'en',
ns: ['core'],
🤖 Prompt for AI Agents
In `@packages/core/src/contexts/translations/index.tsx` around lines 69 - 71, The
i18next init options currently set fallbackLng: false which disables any
fallback language; change the init options object (the one containing lng:
locale, fallbackLng, ns: ['core']) to use fallbackLng: 'en' so missing
translations fall back to English, and keep the other keys (lng and ns)
unchanged; ensure this replacement is applied where the i18n initialization
occurs (the object with keys lng, fallbackLng, ns).

- Add missing errorMessage declaration in handleCustomerChange catch block
  (was referencing variable from a different function scope)
- Fix orders E2E test: use rowgroup selector instead of filtering by status
  text that was never present in the row DOM (tooltip portals to body)
- Add comment clarifying why fallbackLng is false
@kilbot kilbot merged commit 35faa58 into main Feb 3, 2026
2 of 3 checks passed
@kilbot kilbot deleted the feature/i18next-migration-v2 branch February 3, 2026 23:12
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.

1 participant