From dd0379f506dcfb9b7acd350d7212d4d0d1ce964f Mon Sep 17 00:00:00 2001 From: bulunote <33898013+bulunote@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:01:47 +0800 Subject: [PATCH 1/2] fix(i18n): include locale assets in package builds --- packages/i18n/package.json | 1 + .../scripts/__tests__/locale-assets.test.ts | 33 +++++++++++++++++++ packages/i18n/src/core/instance.ts | 7 ++-- packages/i18n/tsdown.config.ts | 1 + 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 packages/i18n/scripts/__tests__/locale-assets.test.ts diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 32c05b72c1f..58e1420ea53 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -18,6 +18,7 @@ "generate:types": "tsx scripts/generate-types.ts", "sync:check": "tsx scripts/sync-check.ts", "check:sync": "tsx scripts/sync-check.ts --ci", + "test": "pnpm run build && tsx --test scripts/__tests__/*.test.ts", "check:lint": "oxlint --max-warnings=9 .", "check:types": "pnpm run generate:types && tsc --noEmit", "check:format": "oxfmt --check .", diff --git a/packages/i18n/scripts/__tests__/locale-assets.test.ts b/packages/i18n/scripts/__tests__/locale-assets.test.ts new file mode 100644 index 00000000000..425f2889241 --- /dev/null +++ b/packages/i18n/scripts/__tests__/locale-assets.test.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import test from "node:test"; + +const PACKAGE_ROOT = path.resolve(import.meta.dirname, "../.."); + +const listJsonAssets = (root: string): string[] => + fs + .readdirSync(root, { recursive: true, withFileTypes: true }) + .filter((entry) => entry.isFile() && entry.name.endsWith(".json")) + .map((entry) => path.relative(root, path.join(entry.parentPath, entry.name)).replaceAll("\\", "/")) + .toSorted(); + +test("the built package includes every locale asset", () => { + const sourceLocaleRoot = path.join(PACKAGE_ROOT, "src/locales"); + const builtLocaleRoot = path.join(PACKAGE_ROOT, "dist/locales"); + + assert.equal(fs.existsSync(builtLocaleRoot), true, "expected dist/locales to be created by the package build"); + assert.deepEqual(listJsonAssets(builtLocaleRoot), listJsonAssets(sourceLocaleRoot)); +}); + +test("the built entry resolves dynamic locale imports inside dist", () => { + const builtEntry = fs.readFileSync(path.join(PACKAGE_ROOT, "dist/index.js"), "utf8"); + + assert.match(builtEntry, /import\(`\.\/locales\/\$\{[^}]+\}\/\$\{[^}]+\}\.json`\)/); +}); diff --git a/packages/i18n/src/core/instance.ts b/packages/i18n/src/core/instance.ts index 58e425e1224..7c02794d3e4 100644 --- a/packages/i18n/src/core/instance.ts +++ b/packages/i18n/src/core/instance.ts @@ -4,7 +4,7 @@ * See the LICENSE file for details. */ -import i18n from "i18next"; +import { createInstance } from "i18next"; import { initReactI18next } from "react-i18next"; import ICU from "i18next-icu"; import resourcesToBackend from "i18next-resources-to-backend"; @@ -13,12 +13,13 @@ import { NAMESPACES, DEFAULT_NAMESPACE } from "../constants/namespaces"; import type { i18n as I18nInstance } from "i18next"; -export const i18nInstance: I18nInstance = i18n.createInstance(); +export const i18nInstance: I18nInstance = createInstance(); i18nInstance .use(ICU) .use(initReactI18next) - .use(resourcesToBackend((language: string, namespace: string) => import(`../locales/${language}/${namespace}.json`))); + // Locale assets are copied beside the bundled dist entry by tsdown. + .use(resourcesToBackend((language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`))); const initialLng = typeof window !== "undefined" ? localStorage.getItem(LANGUAGE_STORAGE_KEY) || FALLBACK_LANGUAGE : FALLBACK_LANGUAGE; diff --git a/packages/i18n/tsdown.config.ts b/packages/i18n/tsdown.config.ts index 92959eaf716..2883546ca47 100644 --- a/packages/i18n/tsdown.config.ts +++ b/packages/i18n/tsdown.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ entry: ["src/index.ts"], format: ["esm"], dts: true, + copy: ["src/locales"], platform: "neutral", exports: true, }); From 02dd9d845405731d10dde35e39f76f28ab165a48 Mon Sep 17 00:00:00 2001 From: bulunote <33898013+bulunote@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:28:39 +0800 Subject: [PATCH 2/2] fix(i18n): type-check scripts with ES2023 --- packages/i18n/scripts/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/i18n/scripts/tsconfig.json b/packages/i18n/scripts/tsconfig.json index 34d295eed80..2b058322324 100644 --- a/packages/i18n/scripts/tsconfig.json +++ b/packages/i18n/scripts/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "target": "ES2022", + "lib": ["ES2023"], "module": "Node16", "moduleResolution": "Node16", "esModuleInterop": true,