From 17fa8c7eb2d2c0eb48940ad6912dc2581830cf45 Mon Sep 17 00:00:00 2001 From: Stefan Probst Date: Fri, 14 Aug 2026 20:33:16 +0200 Subject: [PATCH 1/5] feat: add turbopack support to locale plugin --- .../optimize-locales-plugin/LocalesLoader.js | 37 +++++++++++++ .../LocalesPlugin.d.ts | 14 ++++- .../optimize-locales-plugin/LocalesPlugin.js | 35 ++++++++++++- .../dev/optimize-locales-plugin/README.md | 28 +++++++++- .../test/LocalesPlugin.test.js | 52 +++++++++++++++++++ .../s2-docs/pages/react-aria/frameworks.mdx | 30 +++++++++++ 6 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 packages/dev/optimize-locales-plugin/LocalesLoader.js diff --git a/packages/dev/optimize-locales-plugin/LocalesLoader.js b/packages/dev/optimize-locales-plugin/LocalesLoader.js new file mode 100644 index 00000000000..4c8ce04e67b --- /dev/null +++ b/packages/dev/optimize-locales-plugin/LocalesLoader.js @@ -0,0 +1,37 @@ +/* + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +const path = require('path'); + +module.exports = function localesLoader(source) { + let {locales} = this.getOptions(); + let includedLocales = locales.map(locale => new Intl.Locale(locale)); + let match = path.basename(this.resourcePath).match(/[a-z]{2}-[A-Z]{2}/); + if (match) { + let locale = new Intl.Locale(match[0]); + if (!includedLocales.some(includedLocale => localeMatches(locale, includedLocale))) { + return 'export default undefined;'; + } + } + + if (path.extname(this.resourcePath) === '.json') { + return `export default ${source.toString()};`; + } + + return source; +}; + +function localeMatches(localeToMatch, includedLocale) { + return ( + localeToMatch.language === includedLocale.language && + (!includedLocale.region || localeToMatch.region === includedLocale.region) + ); +} diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts b/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts index f844c570b20..1ca070b5b1a 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts @@ -4,5 +4,17 @@ type Options = { locales: readonly string[]; }; -declare const plugin: UnpluginInstance; +type TurbopackConfig = { + rules: Record< + string, + { + loaders: [{loader: string; options: {locales: string[]}}]; + as: '*.js'; + } + >; +}; + +declare const plugin: UnpluginInstance & { + turbopack(options: Options): TurbopackConfig; +}; export = plugin; diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.js b/packages/dev/optimize-locales-plugin/LocalesPlugin.js index 49b0557e435..2e836fdbe52 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.js +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.js @@ -12,7 +12,18 @@ const {createUnplugin} = require('unplugin'); const path = require('path'); -module.exports = createUnplugin(({locales}) => { +const REACT_ARIA_PACKAGES = [ + '@react-stately', + '@react-aria', + '@react-spectrum', + '@adobe/react-spectrum', + 'react-stately', + 'react-aria', + 'react-aria-components' +]; +const LOCALE_EXTENSIONS = ['json', 'mjs', 'js', 'cjs']; + +let plugin = createUnplugin(({locales}) => { locales = locales.map(l => new Intl.Locale(l)); return { name: 'locales-plugin', @@ -42,6 +53,28 @@ module.exports = createUnplugin(({locales}) => { }; }); +plugin.turbopack = ({locales}) => { + let loader = path.join(__dirname, 'LocalesLoader.js'); + let rules = {}; + for (let packageName of REACT_ARIA_PACKAGES) { + for (let extension of LOCALE_EXTENSIONS) { + rules[`**/${packageName}/**/??-??.${extension}`] = { + loaders: [ + { + loader, + options: {locales} + } + ], + as: '*.js' + }; + } + } + + return {rules}; +}; + +module.exports = plugin; + function localeMatches(localeToMatch, includedLocale) { return ( localeToMatch.language === includedLocale.language && diff --git a/packages/dev/optimize-locales-plugin/README.md b/packages/dev/optimize-locales-plugin/README.md index d089af52ec2..7206c6ef025 100644 --- a/packages/dev/optimize-locales-plugin/README.md +++ b/packages/dev/optimize-locales-plugin/README.md @@ -1,6 +1,6 @@ # @react-aria/optimize-locales-plugin -A build plugin to optimize React Aria to only include translated strings for locales that your app supports. It currently supports Vite, Rollup, Webpack, and esbuild via [unplugin](https://github.com/unjs/unplugin). For Parcel, please use `@react-aria/parcel-resolver-optimize-locales`. +A build plugin to optimize React Aria to only include translated strings for locales that your app supports. It currently supports Vite, Rollup, Webpack, and esbuild via [unplugin](https://github.com/unjs/unplugin), as well as Turbopack via a webpack loader. For Parcel, please use `@react-aria/parcel-resolver-optimize-locales`. ## Configuration @@ -40,6 +40,32 @@ module.exports = { }; ``` +When using Turbopack, spread the plugin's rules into the existing `turbopack.rules` configuration: + +```ts +// next.config.ts +import type {NextConfig} from 'next'; +import optimizeLocales from '@react-aria/optimize-locales-plugin'; + +const localeOptimization = optimizeLocales.turbopack({ + locales: ['en-US', 'fr-FR'] +}); + +const config: NextConfig = { + turbopack: { + rules: { + '*.css': { + loaders: ['@tailwindcss/turbopack'], + as: '*.css' + }, + ...localeOptimization.rules + } + } +}; + +export default config; +``` + ### Vite ```js diff --git a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js index 5493361ef74..bf6f643d103 100644 --- a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js +++ b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js @@ -11,8 +11,10 @@ */ const path = require('path'); const LocalesPlugin = require('../LocalesPlugin'); +const localesLoader = require('../LocalesLoader'); const EMPTY_JS = path.join(path.dirname(require.resolve('../LocalesPlugin')), 'empty.js'); +const LOADER = path.join(path.dirname(require.resolve('../LocalesPlugin')), 'LocalesLoader.js'); function createPlugin(locales = ['en-US']) { return LocalesPlugin.raw({locales}, {framework: 'rollup'}); @@ -82,4 +84,54 @@ describe('@react-aria/optimize-locales-plugin', () => { const resolved = plugin.resolveId('./fr-FR.json', windowsImporter, {}); expect(resolved).toBe(EMPTY_JS); }); + + describe('Turbopack', () => { + test('returns scoped loader rules', () => { + let config = LocalesPlugin.turbopack({locales: ['en-US', 'fr']}); + + expect(config.rules['**/@react-aria/**/??-??.json']).toEqual({ + loaders: [ + { + loader: LOADER, + options: {locales: ['en-US', 'fr']} + } + ], + as: '*.js' + }); + expect(config.rules['**/@react-aria/**/??-??.mjs']).toBeDefined(); + expect(config.rules['**/react-aria-components/**/??-??.json']).toBeDefined(); + expect(Object.keys(config.rules)).toHaveLength(28); + }); + + test('loader replaces an excluded locale with undefined', () => { + let result = callLoader('fr-FR', ['en-US']); + expect(result).toBe('export default undefined;'); + }); + + test('loader preserves an included locale', () => { + let result = callLoader('fr-FR', ['en-US', 'fr-FR']); + expect(result).toBe('export default {"message":"Bonjour"};'); + }); + + test('loader preserves regional locales included by language', () => { + let result = callLoader('fr-CA', ['en-US', 'fr']); + expect(result).toBe('export default {"message":"Bonjour"};'); + }); + + test('loader preserves included compiled locale modules', () => { + let source = 'export default {"message":"Bonjour"};'; + let result = callLoader('fr-FR', ['fr'], 'mjs', source); + expect(result.toString()).toBe(source); + }); + }); }); + +function callLoader(locale, locales, extension = 'json', source = '{"message":"Bonjour"}') { + return localesLoader.call( + { + resourcePath: `/repo/node_modules/@react-aria/button/intl/${locale}.${extension}`, + getOptions: () => ({locales}) + }, + Buffer.from(source) + ); +} diff --git a/packages/dev/s2-docs/pages/react-aria/frameworks.mdx b/packages/dev/s2-docs/pages/react-aria/frameworks.mdx index 04c1288126c..faf8222151f 100644 --- a/packages/dev/s2-docs/pages/react-aria/frameworks.mdx +++ b/packages/dev/s2-docs/pages/react-aria/frameworks.mdx @@ -73,6 +73,36 @@ export const description = 'How to integrate with your framework.'; } ``` + + By default, React Aria includes localized strings for 30+ languages. To optimize the JavaScript bundle to include only your supported languages, install our bundler plugin. + + + Edit `next.config.ts` to add the plugin's rules to your existing Turbopack configuration: + + ```ts + // next.config.ts + import type {NextConfig} from 'next'; + import optimizeLocales from '@react-aria/optimize-locales-plugin'; + + const localeOptimization = optimizeLocales.turbopack({ + locales: ['en-US', 'fr-FR'] + }); + + const config: NextConfig = { + turbopack: { + rules: { + '*.css': { + loaders: ['@tailwindcss/turbopack'], + as: '*.css' + }, + ...localeOptimization.rules + } + } + }; + + export default config; + ``` + If you are using a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP) with a nonce, add a `` tag to your document head, setting the `content` attribute to the generated nonce value. React Aria automatically reads the nonce from this tag. From bae2a35b23401cc0574f8f14ba64ae108630b1d6 Mon Sep 17 00:00:00 2001 From: Stefan Probst Date: Sun, 16 Aug 2026 14:21:16 +0200 Subject: [PATCH 2/5] feat: accept config array with loader conditions --- .../LocalesPlugin.d.ts | 33 +++++++++++++----- .../optimize-locales-plugin/LocalesPlugin.js | 34 +++++++++++++------ .../test/LocalesPlugin.test.js | 30 ++++++++++++++++ 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts b/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts index 1ca070b5b1a..76f8a1bc2c2 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts @@ -4,17 +4,34 @@ type Options = { locales: readonly string[]; }; +type TurbopackCondition = + | 'browser' + | 'foreign' + | 'development' + | 'production' + | 'node' + | 'edge-light' + | {not: TurbopackCondition} + | {all: TurbopackCondition[]} + | {any: TurbopackCondition[]} + | {path: string | RegExp; content?: RegExp} + | {path?: string | RegExp; content: RegExp}; + +type TurbopackOptions = Options & { + condition?: TurbopackCondition; +}; + +type TurbopackRule = { + condition?: TurbopackCondition; + loaders: [{loader: string; options: {locales: readonly string[]}}]; + as: '*.js'; +}; + type TurbopackConfig = { - rules: Record< - string, - { - loaders: [{loader: string; options: {locales: string[]}}]; - as: '*.js'; - } - >; + rules: Record; }; declare const plugin: UnpluginInstance & { - turbopack(options: Options): TurbopackConfig; + turbopack(options: Options | readonly TurbopackOptions[]): TurbopackConfig; }; export = plugin; diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.js b/packages/dev/optimize-locales-plugin/LocalesPlugin.js index 2e836fdbe52..393d051d3c3 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.js +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.js @@ -53,20 +53,34 @@ let plugin = createUnplugin(({locales}) => { }; }); -plugin.turbopack = ({locales}) => { +plugin.turbopack = options => { let loader = path.join(__dirname, 'LocalesLoader.js'); + let hasConditions = Array.isArray(options); + let configurations = hasConditions ? options : [options]; let rules = {}; for (let packageName of REACT_ARIA_PACKAGES) { for (let extension of LOCALE_EXTENSIONS) { - rules[`**/${packageName}/**/??-??.${extension}`] = { - loaders: [ - { - loader, - options: {locales} - } - ], - as: '*.js' - }; + let packageRules = configurations.map(({locales, condition}) => { + let rule = { + loaders: [ + { + loader, + options: {locales} + } + ], + as: '*.js' + }; + + if (condition !== undefined) { + rule.condition = condition; + } + + return rule; + }); + + rules[`**/${packageName}/**/??-??.${extension}`] = hasConditions + ? packageRules + : packageRules[0]; } } diff --git a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js index bf6f643d103..d34511ec9e8 100644 --- a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js +++ b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js @@ -103,6 +103,36 @@ describe('@react-aria/optimize-locales-plugin', () => { expect(Object.keys(config.rules)).toHaveLength(28); }); + test('supports different locales in browser and server module graphs', () => { + let config = LocalesPlugin.turbopack([ + {locales: [], condition: 'browser'}, + {locales: ['en-US', 'fr'], condition: {not: 'browser'}} + ]); + + expect(config.rules['**/@react-aria/**/??-??.json']).toEqual([ + { + condition: 'browser', + loaders: [ + { + loader: LOADER, + options: {locales: []} + } + ], + as: '*.js' + }, + { + condition: {not: 'browser'}, + loaders: [ + { + loader: LOADER, + options: {locales: ['en-US', 'fr']} + } + ], + as: '*.js' + } + ]); + }); + test('loader replaces an excluded locale with undefined', () => { let result = callLoader('fr-FR', ['en-US']); expect(result).toBe('export default undefined;'); From bb964f283e3b93af2c97f7b05baddc9fe8ebf57b Mon Sep 17 00:00:00 2001 From: Stefan Probst Date: Sun, 16 Aug 2026 14:28:12 +0200 Subject: [PATCH 3/5] fix: handle empty config array --- packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts | 4 +++- packages/dev/optimize-locales-plugin/LocalesPlugin.js | 4 ++++ .../dev/optimize-locales-plugin/test/LocalesPlugin.test.js | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts b/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts index 76f8a1bc2c2..733429f4d86 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.d.ts @@ -21,6 +21,8 @@ type TurbopackOptions = Options & { condition?: TurbopackCondition; }; +type TurbopackOptionsList = readonly [TurbopackOptions, ...TurbopackOptions[]]; + type TurbopackRule = { condition?: TurbopackCondition; loaders: [{loader: string; options: {locales: readonly string[]}}]; @@ -32,6 +34,6 @@ type TurbopackConfig = { }; declare const plugin: UnpluginInstance & { - turbopack(options: Options | readonly TurbopackOptions[]): TurbopackConfig; + turbopack(options: Options | TurbopackOptionsList): TurbopackConfig; }; export = plugin; diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.js b/packages/dev/optimize-locales-plugin/LocalesPlugin.js index 393d051d3c3..48ef63645c7 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.js +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.js @@ -56,6 +56,10 @@ let plugin = createUnplugin(({locales}) => { plugin.turbopack = options => { let loader = path.join(__dirname, 'LocalesLoader.js'); let hasConditions = Array.isArray(options); + if (hasConditions && options.length === 0) { + throw new TypeError('Expected at least one Turbopack locale configuration.'); + } + let configurations = hasConditions ? options : [options]; let rules = {}; for (let packageName of REACT_ARIA_PACKAGES) { diff --git a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js index d34511ec9e8..279d9f2eb4a 100644 --- a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js +++ b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js @@ -133,6 +133,12 @@ describe('@react-aria/optimize-locales-plugin', () => { ]); }); + test('requires at least one conditional configuration', () => { + expect(() => LocalesPlugin.turbopack([])).toThrow( + 'Expected at least one Turbopack locale configuration.' + ); + }); + test('loader replaces an excluded locale with undefined', () => { let result = callLoader('fr-FR', ['en-US']); expect(result).toBe('export default undefined;'); From 0d7386269ab6e63937a27efeb1f735c1abaedd47 Mon Sep 17 00:00:00 2001 From: Stefan Probst Date: Mon, 17 Aug 2026 11:13:44 +0200 Subject: [PATCH 4/5] fix: add nextjs version requirements --- .../optimize-locales-plugin/LocalesPlugin.js | 45 +++++++++---------- .../dev/optimize-locales-plugin/README.md | 25 ++++++++++- .../test/LocalesPlugin.test.js | 12 ++--- .../s2-docs/pages/react-aria/frameworks.mdx | 2 +- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.js b/packages/dev/optimize-locales-plugin/LocalesPlugin.js index 48ef63645c7..3198034c41e 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.js +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.js @@ -23,6 +23,8 @@ const REACT_ARIA_PACKAGES = [ ]; const LOCALE_EXTENSIONS = ['json', 'mjs', 'js', 'cjs']; +const LOCALES_GLOB = `**/{${REACT_ARIA_PACKAGES.join(',')}}/**/[a-z][a-z]-[A-Z][A-Z].{${LOCALE_EXTENSIONS.join(',')}}`; + let plugin = createUnplugin(({locales}) => { locales = locales.map(l => new Intl.Locale(l)); return { @@ -61,34 +63,29 @@ plugin.turbopack = options => { } let configurations = hasConditions ? options : [options]; - let rules = {}; - for (let packageName of REACT_ARIA_PACKAGES) { - for (let extension of LOCALE_EXTENSIONS) { - let packageRules = configurations.map(({locales, condition}) => { - let rule = { - loaders: [ - { - loader, - options: {locales} - } - ], - as: '*.js' - }; - - if (condition !== undefined) { - rule.condition = condition; + let localeRules = configurations.map(({locales, condition}) => { + let rule = { + loaders: [ + { + loader, + options: {locales} } + ], + as: '*.js' + }; - return rule; - }); - - rules[`**/${packageName}/**/??-??.${extension}`] = hasConditions - ? packageRules - : packageRules[0]; + if (condition !== undefined) { + rule.condition = condition; } - } - return {rules}; + return rule; + }); + + return { + rules: { + [LOCALES_GLOB]: hasConditions ? localeRules : localeRules[0] + } + }; }; module.exports = plugin; diff --git a/packages/dev/optimize-locales-plugin/README.md b/packages/dev/optimize-locales-plugin/README.md index 7206c6ef025..9d006032152 100644 --- a/packages/dev/optimize-locales-plugin/README.md +++ b/packages/dev/optimize-locales-plugin/README.md @@ -40,7 +40,9 @@ module.exports = { }; ``` -When using Turbopack, spread the plugin's rules into the existing `turbopack.rules` configuration: +When using Turbopack, spread the plugin's rules into the existing `turbopack.rules` configuration. +This requires Next.js 15.4 or newer, because the rule matches files using glob syntax that earlier +versions of Turbopack do not implement. ```ts // next.config.ts @@ -66,6 +68,27 @@ const config: NextConfig = { export default config; ``` +The object form above includes the configured locales in every module graph. This is useful when +the application relies on the locale strings bundled with React Aria components. + +On Next.js 16 and newer, where Turbopack supports loader conditions, an array can be provided to +configure different locales for individual module graphs. For example, when using `LocalizedStringProvider`, keep the supported +locales on the server and exclude them from the browser bundle because the provider injects the +current locale's strings into the initial HTML: + +```ts +const localeOptimization = optimizeLocales.turbopack([ + { + locales: [], + condition: 'browser' + }, + { + locales: ['en-US', 'fr-FR'], + condition: {not: 'browser'} + } +]); +``` + ### Vite ```js diff --git a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js index 279d9f2eb4a..a4b4198cd27 100644 --- a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js +++ b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js @@ -15,6 +15,8 @@ const localesLoader = require('../LocalesLoader'); const EMPTY_JS = path.join(path.dirname(require.resolve('../LocalesPlugin')), 'empty.js'); const LOADER = path.join(path.dirname(require.resolve('../LocalesPlugin')), 'LocalesLoader.js'); +const LOCALES_GLOB = + '**/{@react-stately,@react-aria,@react-spectrum,@adobe/react-spectrum,react-stately,react-aria,react-aria-components}/**/[a-z][a-z]-[A-Z][A-Z].{json,mjs,js,cjs}'; function createPlugin(locales = ['en-US']) { return LocalesPlugin.raw({locales}, {framework: 'rollup'}); @@ -86,10 +88,11 @@ describe('@react-aria/optimize-locales-plugin', () => { }); describe('Turbopack', () => { - test('returns scoped loader rules', () => { + test('returns a single scoped loader rule', () => { let config = LocalesPlugin.turbopack({locales: ['en-US', 'fr']}); - expect(config.rules['**/@react-aria/**/??-??.json']).toEqual({ + expect(Object.keys(config.rules)).toEqual([LOCALES_GLOB]); + expect(config.rules[LOCALES_GLOB]).toEqual({ loaders: [ { loader: LOADER, @@ -98,9 +101,6 @@ describe('@react-aria/optimize-locales-plugin', () => { ], as: '*.js' }); - expect(config.rules['**/@react-aria/**/??-??.mjs']).toBeDefined(); - expect(config.rules['**/react-aria-components/**/??-??.json']).toBeDefined(); - expect(Object.keys(config.rules)).toHaveLength(28); }); test('supports different locales in browser and server module graphs', () => { @@ -109,7 +109,7 @@ describe('@react-aria/optimize-locales-plugin', () => { {locales: ['en-US', 'fr'], condition: {not: 'browser'}} ]); - expect(config.rules['**/@react-aria/**/??-??.json']).toEqual([ + expect(config.rules[LOCALES_GLOB]).toEqual([ { condition: 'browser', loaders: [ diff --git a/packages/dev/s2-docs/pages/react-aria/frameworks.mdx b/packages/dev/s2-docs/pages/react-aria/frameworks.mdx index faf8222151f..d67e1f0b7ce 100644 --- a/packages/dev/s2-docs/pages/react-aria/frameworks.mdx +++ b/packages/dev/s2-docs/pages/react-aria/frameworks.mdx @@ -77,7 +77,7 @@ export const description = 'How to integrate with your framework.'; By default, React Aria includes localized strings for 30+ languages. To optimize the JavaScript bundle to include only your supported languages, install our bundler plugin. - Edit `next.config.ts` to add the plugin's rules to your existing Turbopack configuration: + Edit `next.config.ts` to add the plugin's rules to your existing Turbopack configuration. This requires Next.js 15.4 or newer. ```ts // next.config.ts From 48299baf62d648b0f58f75aec8dcf3aa00c0941b Mon Sep 17 00:00:00 2001 From: Stefan Probst Date: Mon, 17 Aug 2026 11:28:17 +0200 Subject: [PATCH 5/5] fix: avoid character classes in matcher glob --- packages/dev/optimize-locales-plugin/LocalesPlugin.js | 2 +- packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev/optimize-locales-plugin/LocalesPlugin.js b/packages/dev/optimize-locales-plugin/LocalesPlugin.js index 3198034c41e..ade81ed2828 100644 --- a/packages/dev/optimize-locales-plugin/LocalesPlugin.js +++ b/packages/dev/optimize-locales-plugin/LocalesPlugin.js @@ -23,7 +23,7 @@ const REACT_ARIA_PACKAGES = [ ]; const LOCALE_EXTENSIONS = ['json', 'mjs', 'js', 'cjs']; -const LOCALES_GLOB = `**/{${REACT_ARIA_PACKAGES.join(',')}}/**/[a-z][a-z]-[A-Z][A-Z].{${LOCALE_EXTENSIONS.join(',')}}`; +const LOCALES_GLOB = `**/{${REACT_ARIA_PACKAGES.join(',')}}/**/??-??.{${LOCALE_EXTENSIONS.join(',')}}`; let plugin = createUnplugin(({locales}) => { locales = locales.map(l => new Intl.Locale(l)); diff --git a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js index a4b4198cd27..9d56c214094 100644 --- a/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js +++ b/packages/dev/optimize-locales-plugin/test/LocalesPlugin.test.js @@ -16,7 +16,7 @@ const localesLoader = require('../LocalesLoader'); const EMPTY_JS = path.join(path.dirname(require.resolve('../LocalesPlugin')), 'empty.js'); const LOADER = path.join(path.dirname(require.resolve('../LocalesPlugin')), 'LocalesLoader.js'); const LOCALES_GLOB = - '**/{@react-stately,@react-aria,@react-spectrum,@adobe/react-spectrum,react-stately,react-aria,react-aria-components}/**/[a-z][a-z]-[A-Z][A-Z].{json,mjs,js,cjs}'; + '**/{@react-stately,@react-aria,@react-spectrum,@adobe/react-spectrum,react-stately,react-aria,react-aria-components}/**/??-??.{json,mjs,js,cjs}'; function createPlugin(locales = ['en-US']) { return LocalesPlugin.raw({locales}, {framework: 'rollup'});