From da1c6110144a1e97606d867b6a1bdd397361f651 Mon Sep 17 00:00:00 2001 From: Wes Copeland Date: Tue, 1 Oct 2024 19:47:30 -0400 Subject: [PATCH] chore: remove internal lib --- package-lock.json | 15 ++ package.json | 1 + resources/js/app.tsx | 2 +- .../RecentPostAggregateLinks.tsx | 3 +- .../RecentPostsCards/RecentPostsCards.tsx | 2 +- .../RecentPostsTable/RecentPostsTable.tsx | 2 +- .../SimplePaginator/SimplePaginator.tsx | 2 +- .../ForumBreadcrumbs/ForumBreadcrumbs.tsx | 2 +- .../RecentPostsMainRoot.tsx | 2 +- .../@types/php-array-reader.d.ts | 8 - resources/js/lib/laravel-react-i18n/README.md | 1 - .../js/lib/laravel-react-i18n/context.ts | 14 -- .../contrib/get-plural-index.ts | 172 -------------- resources/js/lib/laravel-react-i18n/hook.ts | 8 - resources/js/lib/laravel-react-i18n/index.ts | 4 - .../laravel-react-i18n/interfaces/context.ts | 14 -- .../interfaces/default-options.ts | 11 - .../interfaces/i18n-provider-props.ts | 11 - .../interfaces/locale-file.ts | 8 - .../interfaces/options-provider.ts | 8 - .../laravel-react-i18n/interfaces/options.ts | 8 - .../interfaces/replacements.ts | 6 - .../lib/laravel-react-i18n/plugin/helper.ts | 14 -- .../lib/laravel-react-i18n/plugin/key-type.ts | 34 --- .../lib/laravel-react-i18n/plugin/locale.ts | 46 ---- .../lib/laravel-react-i18n/plugin/parser.ts | 77 ------ .../js/lib/laravel-react-i18n/provider.ts | 222 ------------------ .../laravel-react-i18n/utils/pluralization.ts | 83 ------- .../laravel-react-i18n/utils/recognizer.ts | 72 ------ .../lib/laravel-react-i18n/utils/replacer.ts | 27 --- .../lib/laravel-react-i18n/utils/resolver.ts | 37 --- resources/js/lib/laravel-react-i18n/vite.ts | 138 ----------- resources/js/ssr.tsx | 2 +- resources/js/test/setup.tsx | 2 +- vite.config.ts | 3 +- 35 files changed, 26 insertions(+), 1035 deletions(-) delete mode 100644 resources/js/lib/laravel-react-i18n/@types/php-array-reader.d.ts delete mode 100644 resources/js/lib/laravel-react-i18n/README.md delete mode 100644 resources/js/lib/laravel-react-i18n/context.ts delete mode 100644 resources/js/lib/laravel-react-i18n/contrib/get-plural-index.ts delete mode 100644 resources/js/lib/laravel-react-i18n/hook.ts delete mode 100644 resources/js/lib/laravel-react-i18n/index.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/context.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/default-options.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/i18n-provider-props.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/locale-file.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/options-provider.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/options.ts delete mode 100644 resources/js/lib/laravel-react-i18n/interfaces/replacements.ts delete mode 100644 resources/js/lib/laravel-react-i18n/plugin/helper.ts delete mode 100644 resources/js/lib/laravel-react-i18n/plugin/key-type.ts delete mode 100644 resources/js/lib/laravel-react-i18n/plugin/locale.ts delete mode 100644 resources/js/lib/laravel-react-i18n/plugin/parser.ts delete mode 100644 resources/js/lib/laravel-react-i18n/provider.ts delete mode 100644 resources/js/lib/laravel-react-i18n/utils/pluralization.ts delete mode 100644 resources/js/lib/laravel-react-i18n/utils/recognizer.ts delete mode 100644 resources/js/lib/laravel-react-i18n/utils/replacer.ts delete mode 100644 resources/js/lib/laravel-react-i18n/utils/resolver.ts delete mode 100644 resources/js/lib/laravel-react-i18n/vite.ts diff --git a/package-lock.json b/package-lock.json index 623e4f70e..49f999a5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "cmdk": "^1.0.0", "dayjs": "^1.11.11", "js-cookie": "^3.0.5", + "laravel-react-i18n": "^2.0.5", "linkify-html": "^4.1.3", "php-array-reader": "^2.1.2", "react": "^18.3.1", @@ -8688,6 +8689,20 @@ "node": ">=10" } }, + "node_modules/laravel-react-i18n": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/laravel-react-i18n/-/laravel-react-i18n-2.0.5.tgz", + "integrity": "sha512-o6dHjKJlY1gZZAJba4TVGJ/QHOCIeGoDWl8M+NczW+M4IJ4JhroaxkkiphGH/GMEWL3zJbOtDM3tk0GcXl1ydQ==", + "license": "MIT", + "dependencies": { + "php-array-reader": "^2.1.2", + "react": "^18.2.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=9.0.0" + } + }, "node_modules/laravel-vite-plugin": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.4.tgz", diff --git a/package.json b/package.json index f35ede16a..a795ac76f 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "cmdk": "^1.0.0", "dayjs": "^1.11.11", "js-cookie": "^3.0.5", + "laravel-react-i18n": "^2.0.5", "linkify-html": "^4.1.3", "php-array-reader": "^2.1.2", "react": "^18.3.1", diff --git a/resources/js/app.tsx b/resources/js/app.tsx index 5e84eaccc..76f1c3c15 100644 --- a/resources/js/app.tsx +++ b/resources/js/app.tsx @@ -1,10 +1,10 @@ import { createInertiaApp } from '@inertiajs/react'; +import { LaravelReactI18nProvider } from 'laravel-react-i18n'; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; import { createRoot, hydrateRoot } from 'react-dom/client'; import { AppProviders } from './common/components/AppProviders'; import type { AppGlobalProps } from './common/models'; -import { LaravelReactI18nProvider } from './lib/laravel-react-i18n'; const appName = import.meta.env.APP_NAME || 'RetroAchievements'; diff --git a/resources/js/common/components/RecentPostAggregateLinks/RecentPostAggregateLinks.tsx b/resources/js/common/components/RecentPostAggregateLinks/RecentPostAggregateLinks.tsx index af582f76b..74ade3266 100644 --- a/resources/js/common/components/RecentPostAggregateLinks/RecentPostAggregateLinks.tsx +++ b/resources/js/common/components/RecentPostAggregateLinks/RecentPostAggregateLinks.tsx @@ -1,7 +1,6 @@ +import { useLaravelReactI18n } from 'laravel-react-i18n'; import type { FC } from 'react'; -import { useLaravelReactI18n } from '@/lib/laravel-react-i18n'; - interface RecentPostAggregateLinksProps { topic: App.Data.ForumTopic; } diff --git a/resources/js/common/components/RecentPostsCards/RecentPostsCards.tsx b/resources/js/common/components/RecentPostsCards/RecentPostsCards.tsx index a01b8d61d..6f9d7823f 100644 --- a/resources/js/common/components/RecentPostsCards/RecentPostsCards.tsx +++ b/resources/js/common/components/RecentPostsCards/RecentPostsCards.tsx @@ -1,8 +1,8 @@ +import { useLaravelReactI18n } from 'laravel-react-i18n'; import type { FC } from 'react'; import { UserAvatar } from '@/common/components/UserAvatar'; import { usePageProps } from '@/common/hooks/usePageProps'; -import { useLaravelReactI18n } from '@/lib/laravel-react-i18n'; import { PostTimestamp } from '../PostTimestamp'; import { RecentPostAggregateLinks } from '../RecentPostAggregateLinks'; diff --git a/resources/js/common/components/RecentPostsTable/RecentPostsTable.tsx b/resources/js/common/components/RecentPostsTable/RecentPostsTable.tsx index 066e10a54..98d0004e3 100644 --- a/resources/js/common/components/RecentPostsTable/RecentPostsTable.tsx +++ b/resources/js/common/components/RecentPostsTable/RecentPostsTable.tsx @@ -1,8 +1,8 @@ +import { useLaravelReactI18n } from 'laravel-react-i18n'; import type { FC } from 'react'; import { UserAvatar } from '@/common/components/UserAvatar'; import { usePageProps } from '@/common/hooks/usePageProps'; -import { useLaravelReactI18n } from '@/lib/laravel-react-i18n'; import { PostTimestamp } from '../PostTimestamp'; import { RecentPostAggregateLinks } from '../RecentPostAggregateLinks'; diff --git a/resources/js/common/components/SimplePaginator/SimplePaginator.tsx b/resources/js/common/components/SimplePaginator/SimplePaginator.tsx index 7ad2d378c..6cba89225 100644 --- a/resources/js/common/components/SimplePaginator/SimplePaginator.tsx +++ b/resources/js/common/components/SimplePaginator/SimplePaginator.tsx @@ -1,3 +1,4 @@ +import { useLaravelReactI18n } from 'laravel-react-i18n'; import type { FC } from 'react'; import { @@ -7,7 +8,6 @@ import { BasePaginationNext, BasePaginationPrevious, } from '@/common/components/+vendor/BasePagination'; -import { useLaravelReactI18n } from '@/lib/laravel-react-i18n'; interface SimplePaginatorProps { paginatedData: App.Data.PaginatedData; diff --git a/resources/js/features/forums/components/ForumBreadcrumbs/ForumBreadcrumbs.tsx b/resources/js/features/forums/components/ForumBreadcrumbs/ForumBreadcrumbs.tsx index 5000fc9d0..36d89df01 100644 --- a/resources/js/features/forums/components/ForumBreadcrumbs/ForumBreadcrumbs.tsx +++ b/resources/js/features/forums/components/ForumBreadcrumbs/ForumBreadcrumbs.tsx @@ -1,3 +1,4 @@ +import { useLaravelReactI18n } from 'laravel-react-i18n'; import type { FC } from 'react'; import { @@ -8,7 +9,6 @@ import { BaseBreadcrumbPage, BaseBreadcrumbSeparator, } from '@/common/components/+vendor/BaseBreadcrumb'; -import { useLaravelReactI18n } from '@/lib/laravel-react-i18n'; // TODO support ForumCategory and Forum interface ForumBreadcrumbsProps { diff --git a/resources/js/features/forums/components/RecentPostsMainRoot/RecentPostsMainRoot.tsx b/resources/js/features/forums/components/RecentPostsMainRoot/RecentPostsMainRoot.tsx index 0e417c53e..07037fcec 100644 --- a/resources/js/features/forums/components/RecentPostsMainRoot/RecentPostsMainRoot.tsx +++ b/resources/js/features/forums/components/RecentPostsMainRoot/RecentPostsMainRoot.tsx @@ -1,10 +1,10 @@ +import { useLaravelReactI18n } from 'laravel-react-i18n'; import type { FC } from 'react'; import { RecentPostsCards } from '@/common/components/RecentPostsCards'; import { RecentPostsTable } from '@/common/components/RecentPostsTable'; import { SimplePaginator } from '@/common/components/SimplePaginator'; import { usePageProps } from '@/common/hooks/usePageProps'; -import { useLaravelReactI18n } from '@/lib/laravel-react-i18n'; import { ForumBreadcrumbs } from '../ForumBreadcrumbs'; diff --git a/resources/js/lib/laravel-react-i18n/@types/php-array-reader.d.ts b/resources/js/lib/laravel-react-i18n/@types/php-array-reader.d.ts deleted file mode 100644 index a9859beeb..000000000 --- a/resources/js/lib/laravel-react-i18n/@types/php-array-reader.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Declaration patch for module `php-array-reader` - * - * (fromString) - */ -declare module 'php-array-reader' { - export const fromString: (phpString: string) => object; -} diff --git a/resources/js/lib/laravel-react-i18n/README.md b/resources/js/lib/laravel-react-i18n/README.md deleted file mode 100644 index 84b43c3ef..000000000 --- a/resources/js/lib/laravel-react-i18n/README.md +++ /dev/null @@ -1 +0,0 @@ -i18n - https://github.com/EugeneMeles/laravel-react-i18n, forked and modified to support React 18 with server-side rendering \ No newline at end of file diff --git a/resources/js/lib/laravel-react-i18n/context.ts b/resources/js/lib/laravel-react-i18n/context.ts deleted file mode 100644 index f0956498a..000000000 --- a/resources/js/lib/laravel-react-i18n/context.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createContext } from 'react'; - -import type ContextInterface from './interfaces/context'; - -export const Context = createContext({ - t: (key) => '', - tChoice: (key) => '', - currentLocale: () => '', - getLocales: () => [''], - isLocale: (locale) => true, - loading: true, - // eslint-disable-next-line @typescript-eslint/no-empty-function - setLocale: (locale) => {}, -}); diff --git a/resources/js/lib/laravel-react-i18n/contrib/get-plural-index.ts b/resources/js/lib/laravel-react-i18n/contrib/get-plural-index.ts deleted file mode 100644 index e7589746b..000000000 --- a/resources/js/lib/laravel-react-i18n/contrib/get-plural-index.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* eslint-disable */ - -/** - * Get the index to use for pluralization. - * The plural rules are derived from code of the Zend Framework. - * - * @category Zend - * @package Zend_Locale - * @public https://github.com/zendframework/zf1/blob/master/library/Zend/Translate/Plural.php - * @copyright 2005-2015 Zend Technologies USA Inc. http://www.zend.com - * @license http://framework.zend.com/license New BSD License - * - * @param {String} locale - * @param {Number} number - * @return {Number} - */ -export function getPluralIndex(locale: string, number: number) { - locale = locale.replace('-', '_'); - - if (locale === 'pt_BR') { - // temporary set a locale for brazilian - locale = 'xbr'; - } - - if (locale.length > 3) { - locale = locale.substring(0, locale.lastIndexOf('_')); - } - - switch (locale) { - case 'az': - case 'bo': - case 'dz': - case 'id': - case 'ja': - case 'jv': - case 'ka': - case 'km': - case 'kn': - case 'ko': - case 'ms': - case 'th': - case 'tr': - case 'vi': - case 'zh': - return 0; - case 'af': - case 'bn': - case 'bg': - case 'ca': - case 'da': - case 'de': - case 'el': - case 'en': - case 'eo': - case 'es': - case 'et': - case 'eu': - case 'fa': - case 'fi': - case 'fo': - case 'fur': - case 'fy': - case 'gl': - case 'gu': - case 'ha': - case 'he': - case 'hu': - case 'is': - case 'it': - case 'ku': - case 'lb': - case 'ml': - case 'mn': - case 'mr': - case 'nah': - case 'nb': - case 'ne': - case 'nl': - case 'nn': - case 'no': - case 'om': - case 'or': - case 'pa': - case 'pap': - case 'ps': - case 'pt': - case 'so': - case 'sq': - case 'sv': - case 'sw': - case 'ta': - case 'te': - case 'tk': - case 'ur': - case 'zu': - return number === 1 ? 0 : 1; - case 'am': - case 'bh': - case 'fil': - case 'fr': - case 'gun': - case 'hi': - case 'ln': - case 'mg': - case 'nso': - case 'xbr': - case 'ti': - case 'wa': - return number === 0 || number === 1 ? 0 : 1; - case 'be': - case 'bs': - case 'hr': - case 'ru': - case 'sr': - case 'uk': - return number % 10 === 1 && number % 100 !== 11 - ? 0 - : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) - ? 1 - : 2; - case 'cs': - case 'sk': - return number === 1 ? 0 : number >= 2 && number <= 4 ? 1 : 2; - case 'ga': - return number === 1 ? 0 : number === 2 ? 1 : 2; - case 'lt': - return number % 10 === 1 && number % 100 !== 11 - ? 0 - : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) - ? 1 - : 2; - case 'sl': - return number % 100 === 1 ? 0 : number % 100 === 2 ? 1 : number % 100 === 3 || number % 100 === 4 ? 2 : 3; - case 'mk': - return number % 10 === 1 ? 0 : 1; - case 'mt': - return number === 1 - ? 0 - : number === 0 || (number % 100 > 1 && number % 100 < 11) - ? 1 - : number % 100 > 10 && number % 100 < 20 - ? 2 - : 3; - case 'lv': - return number === 0 ? 0 : number % 10 === 1 && number % 100 !== 11 ? 1 : 2; - case 'pl': - return number === 1 - ? 0 - : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) - ? 1 - : 2; - case 'cy': - return number === 1 ? 0 : number === 2 ? 1 : number === 8 || number === 11 ? 2 : 3; - case 'ro': - return number === 1 ? 0 : number === 0 || (number % 100 > 0 && number % 100 < 20) ? 1 : 2; - case 'ar': - return number === 0 - ? 0 - : number === 1 - ? 1 - : number === 2 - ? 2 - : number >= 3 && number <= 10 - ? 3 - : number >= 11 && number <= 99 - ? 4 - : 5; - default: - return 0; - } - } - \ No newline at end of file diff --git a/resources/js/lib/laravel-react-i18n/hook.ts b/resources/js/lib/laravel-react-i18n/hook.ts deleted file mode 100644 index 848149f73..000000000 --- a/resources/js/lib/laravel-react-i18n/hook.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useContext } from 'react'; - -import { Context } from './context'; -import type ContextInterface from './interfaces/context'; - -export default function useLaravelReactI18n() { - return useContext>(Context); -} diff --git a/resources/js/lib/laravel-react-i18n/index.ts b/resources/js/lib/laravel-react-i18n/index.ts deleted file mode 100644 index 6339c00ed..000000000 --- a/resources/js/lib/laravel-react-i18n/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import useLaravelReactI18n from './hook'; -import LaravelReactI18nProvider from './provider'; - -export { LaravelReactI18nProvider, useLaravelReactI18n }; diff --git a/resources/js/lib/laravel-react-i18n/interfaces/context.ts b/resources/js/lib/laravel-react-i18n/interfaces/context.ts deleted file mode 100644 index d80b41d70..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/context.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type ReplacementsInterface from './replacements'; - -/** - * - */ -export default interface ContextInterface { - currentLocale: () => string; - getLocales: () => string[]; - isLocale: (locale: string) => boolean; - loading: boolean; - setLocale: (locale: string) => void; - t: (key: T, replacements?: ReplacementsInterface) => string; - tChoice: (key: T, number: number, replacements?: ReplacementsInterface) => string; -} diff --git a/resources/js/lib/laravel-react-i18n/interfaces/default-options.ts b/resources/js/lib/laravel-react-i18n/interfaces/default-options.ts deleted file mode 100644 index d94a3786e..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/default-options.ts +++ /dev/null @@ -1,11 +0,0 @@ -import LocaleFileInterface from './locale-file'; - -/** - * The Interface that is responsible for the default options. - */ -export default interface DefaultOptionsInterface { - fallbackLocale: string; - locale: string; - prevLocale: string; - files: Record | Record Promise>; -} diff --git a/resources/js/lib/laravel-react-i18n/interfaces/i18n-provider-props.ts b/resources/js/lib/laravel-react-i18n/interfaces/i18n-provider-props.ts deleted file mode 100644 index 5cd811a60..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/i18n-provider-props.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ReactNode } from 'react'; - -import type OptionsInterface from './options'; - -/** - * - */ -export default interface I18nProviderProps extends OptionsInterface { - children: ReactNode; - ssr?: boolean; -} diff --git a/resources/js/lib/laravel-react-i18n/interfaces/locale-file.ts b/resources/js/lib/laravel-react-i18n/interfaces/locale-file.ts deleted file mode 100644 index 5d44f338f..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/locale-file.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * - */ -export default interface LocaleFileInterface { - default: { - [key: string]: string; - }; -} diff --git a/resources/js/lib/laravel-react-i18n/interfaces/options-provider.ts b/resources/js/lib/laravel-react-i18n/interfaces/options-provider.ts deleted file mode 100644 index 7ace17adc..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/options-provider.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type OptionsInterface from './options'; - -/** - * The Interface that is responsible for the OptionsProvider provided. - */ -export default interface OptionsProviderInterface extends OptionsInterface { - prevLocale?: string; -} diff --git a/resources/js/lib/laravel-react-i18n/interfaces/options.ts b/resources/js/lib/laravel-react-i18n/interfaces/options.ts deleted file mode 100644 index 9ba5b4c23..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/options.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * The Interface that is responsible for the Options provided. - */ -export default interface OptionsInterface { - fallbackLocale?: string; - locale?: string; - files: Record | Record Promise>; -} diff --git a/resources/js/lib/laravel-react-i18n/interfaces/replacements.ts b/resources/js/lib/laravel-react-i18n/interfaces/replacements.ts deleted file mode 100644 index 8df853664..000000000 --- a/resources/js/lib/laravel-react-i18n/interfaces/replacements.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * - */ -export default interface ReplacementsInterface { - [key: string]: string | number; -} diff --git a/resources/js/lib/laravel-react-i18n/plugin/helper.ts b/resources/js/lib/laravel-react-i18n/plugin/helper.ts deleted file mode 100644 index 6256840e0..000000000 --- a/resources/js/lib/laravel-react-i18n/plugin/helper.ts +++ /dev/null @@ -1,14 +0,0 @@ -import path from 'path'; - -/** - * - * @param rawDirname - */ -export function dirnameSanitize(rawDirname: string): string { - return ( - rawDirname - .replace(/\/+/g, path.sep) - .replace(/\\+/g, path.sep) - .replace(/[\\/]$/, '') + path.sep - ); -} diff --git a/resources/js/lib/laravel-react-i18n/plugin/key-type.ts b/resources/js/lib/laravel-react-i18n/plugin/key-type.ts deleted file mode 100644 index bd17d2738..000000000 --- a/resources/js/lib/laravel-react-i18n/plugin/key-type.ts +++ /dev/null @@ -1,34 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -/** - * - * @param dirname - * @param basename - */ -export function convertToKeyType(dirname: string, basename: string): string { - const result = fs.readFileSync(`${dirname + path.sep + basename}.json`, 'utf8'); - const obj = Object.entries(JSON.parse(result)); - - let str = ''; - for (const [index, [key]] of obj.entries()) { - // Escaping a key - const escKey = key.replace(/[!@#$%^&*()+=\-[\]\\';,/{}|":<>?~_]/g, '\\$&'); - - str = obj.length === 1 || obj.length - 1 === index ? `${str}'${escKey}'` : `${str}'${escKey}'|`; - } - - return str; -} - -/** - * - * @param keys - * @param dirname - */ -export function saveKeyTypeToFile(keys: string, dirname = 'resources/js') { - const sanitizeDirname = dirname.replace(/[\\/]$/, '') + path.sep; - const data = `export type I18nKeyType = ${keys};`.replace(/[\r\n]+/g, ''); - - fs.writeFileSync(`${sanitizeDirname}LaravelReactI18n.types.ts`, data); -} diff --git a/resources/js/lib/laravel-react-i18n/plugin/locale.ts b/resources/js/lib/laravel-react-i18n/plugin/locale.ts deleted file mode 100644 index c7945227f..000000000 --- a/resources/js/lib/laravel-react-i18n/plugin/locale.ts +++ /dev/null @@ -1,46 +0,0 @@ -import fs from 'fs'; - -import { dirnameSanitize } from './helper'; - -export default { - /** - * - * @param dirname - */ - getJsonLocale: (dirname: string): string[] => { - dirname = dirnameSanitize(dirname); - - if (!fs.existsSync(dirname)) { - // console.error(`No such directory: '${dirname}'`); - return []; - } - - return fs - .readdirSync(dirname) - .filter((basename) => fs.statSync(dirname + basename).isFile()) - .filter((basename) => !/^php_/.test(basename)) - .map((basename) => basename.replace('.json', '')) - .sort(); - }, - /** - * - * @param dirname - */ - getPhpLocale: (dirname: string): string[] => { - dirname = dirnameSanitize(dirname); - - if (!fs.existsSync(dirname)) { - // console.error(`No such directory: '${dirname}'`); - return []; - } - - return fs - .readdirSync(dirname) - .filter((basename) => fs.statSync(dirname + basename).isDirectory()) - .filter( - (folder) => - fs.readdirSync(dirname + folder).filter((basename) => /\.php$/.test(basename)).length > 0, - ) - .sort(); - }, -}; diff --git a/resources/js/lib/laravel-react-i18n/plugin/parser.ts b/resources/js/lib/laravel-react-i18n/plugin/parser.ts deleted file mode 100644 index 9686e3684..000000000 --- a/resources/js/lib/laravel-react-i18n/plugin/parser.ts +++ /dev/null @@ -1,77 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { fromString } from 'php-array-reader'; - -import { dirnameSanitize } from './helper'; - -/** - * - * Parse array of PHP file. - * - * @param dirname - */ -export default function parser(dirname: string): { path: string; basename: string }[] { - dirname = dirnameSanitize(dirname); - - if (!fs.existsSync(dirname)) { - // console.error(`No such directory: '${dirname}'`); - return []; - } - - return fs - .readdirSync(dirname) - .filter((basename) => fs.statSync(dirname + basename).isDirectory()) - .sort() - .map((locale) => ({ - locale, - trans: convertToDottedKey(getObjectTranslation(dirname + locale)), - })) - .filter(({ trans }) => Object.keys(trans).length > 0) - .map(({ locale, trans }) => { - const basename = `php_${locale}.json`; - fs.writeFileSync(dirname + basename, JSON.stringify(trans)); - - return { basename, path: dirname + basename }; - }); -} - -/** - * - * @param source - * @param target - * @param keys - */ -function convertToDottedKey( - source: object, - target: { [kes: string]: string } = {}, - keys: string[] = [], -): { [kes: string]: string } { - for (const [key, value] of Object.entries(source)) { - if (Object.prototype.toString.call(value) === '[object Object]') { - convertToDottedKey(value, target, keys.concat(key)); - continue; - } - - target[keys.concat(key).join('.')] = value; - } - - return target; -} - -/** - * - * @param dirname - */ -function getObjectTranslation(dirname: string): { [kes: string]: string } { - return fs - .readdirSync(dirname) - .map((basename) => { - const absoluteFile = dirname + path.sep + basename; - const key = basename.replace(/\.\w+$/, ''); - - return fs.statSync(absoluteFile).isDirectory() - ? { key, val: getObjectTranslation(absoluteFile) } - : { key, val: fromString(fs.readFileSync(absoluteFile).toString()) }; - }) - .reduce((obj, { key, val }) => ({ ...obj, [key]: val }), {}); -} diff --git a/resources/js/lib/laravel-react-i18n/provider.ts b/resources/js/lib/laravel-react-i18n/provider.ts deleted file mode 100644 index bfac16651..000000000 --- a/resources/js/lib/laravel-react-i18n/provider.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -import { createElement, useEffect, useState } from 'react'; - -import { Context } from './context'; -import type DefaultOptionsInterface from './interfaces/default-options'; -import type I18nProviderProps from './interfaces/i18n-provider-props'; -import type ReplacementsInterface from './interfaces/replacements'; -import pluralization from './utils/pluralization'; -import recognizer from './utils/recognizer'; -import replacer from './utils/replacer'; -import resolver from './utils/resolver'; - -/** - * - */ -const isServer = typeof window === 'undefined'; - -/** - * Map object for translations. - */ -const translation = new Map(); - -/** - * Get document lang meta from HTML. - */ -const documentLang = - typeof document !== 'undefined' - ? document?.documentElement?.lang?.replace('-', '_') || 'en' - : 'en'; - -/** - * The default options. - */ -const defaultOptions: DefaultOptionsInterface = { - locale: documentLang, - fallbackLocale: documentLang, - prevLocale: documentLang, - files: {}, -}; - -/** - * Laravel React I18n Provider: - */ -export default function LaravelReactI18nProvider({ - children, - ssr, - ...currentOptions -}: I18nProviderProps) { - const [isFirstRender, setIsFirstRender] = useState(true); - const [loading, setLoading] = useState(!isServer); - const [options, setOptions] = useState({ - ...defaultOptions, - ...currentOptions, - }); - const { getLocales, isLocale } = recognizer(options.files); - - // Determine if files are eagerly loaded. - const filesAreEagerlyLoaded = Object.values(options.files).every( - (value) => typeof value === 'object' && value !== null, - ); - - const { locale, fallbackLocale } = options; - - if (!translation.get(locale)) { - if (filesAreEagerlyLoaded) { - fetchLocaleSync(locale); - } else if (isServer) { - fetchLocaleServer(locale); - } - } - - if (locale !== fallbackLocale && !translation.get(fallbackLocale)) { - if (filesAreEagerlyLoaded) { - fetchLocaleSync(fallbackLocale); - } else if (isServer) { - fetchLocaleServer(fallbackLocale); - } - } - - useEffect(() => { - if (!filesAreEagerlyLoaded) { - if (!translation.get(locale)) fetchLocaleClient(locale); - if (locale !== fallbackLocale && !translation.get(fallbackLocale)) - fetchLocaleClient(fallbackLocale); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [options.locale]); - - function fetchLocaleSync(locale: string): void { - const responses = resolver(options.files, locale); - - for (const response of responses) { - translation.set(locale, { - ...(translation.get(locale) || {}), - ...response.default, - }); - } - } - - /** - * Initialise translations for server. - */ - if (isServer) { - const { locale, fallbackLocale } = options; - - if (!translation.get(locale)) fetchLocaleServer(locale); - if (locale !== fallbackLocale && !translation.get(fallbackLocale)) - fetchLocaleServer(fallbackLocale); - } - - /** - * Fetching locale for client side. - */ - function fetchLocaleClient(locale: string): void { - const promises = resolver(options.files, locale); - - setLoading(true); - Promise.all(promises) - .then((responses) => { - for (const response of responses) { - translation.set(locale, { - ...(translation.get(locale) || {}), - ...response.default, - }); - } - }) - .then(() => { - if (isFirstRender) setIsFirstRender(false); - setLoading(false); - }); - } - - /** - * Fetching locale for server side. - */ - function fetchLocaleServer(locale: string): void { - const responses = resolver(options.files, locale); - - for (const response of responses) { - translation.set(locale, { - ...(translation.get(locale) || {}), - ...response.default, - }); - } - } - - /** - * Get the translation for the given key. - */ - function t(key: string, replacements: ReplacementsInterface = {}): string { - const { locale, fallbackLocale, prevLocale } = options; - - let message = translation.get(fallbackLocale)?.[key] - ? translation.get(fallbackLocale)[key] - : key; - - if (isLocale(locale)) { - if (translation.get(locale)?.[key]) { - message = translation.get(locale)[key]; - } else if (translation.get(prevLocale)?.[key]) { - message = translation.get(prevLocale)[key]; - } else if (translation.get(fallbackLocale)?.[key]) { - message = translation.get(fallbackLocale)[key]; - } - } - - return replacer(message, replacements); - } - - /** - * Translates the given message based on a count. - */ - function tChoice(key: string, number: number, replacements: ReplacementsInterface = {}): string { - const message = t(key, replacements); - const locale = isLocale(options.locale) ? options.locale : options.fallbackLocale; - - return replacer(pluralization(message, number, locale), { - ...replacements, - count: number.toString(), - }); - } - - /** - * Set locale. - */ - function setLocale(locale: string) { - if (!isServer) { - // When setting the HTML lang attribute, hyphen must be use instead of underscore. - document.documentElement.setAttribute('lang', locale.replace('_', '-')); - } - - setOptions((prevState) => ({ - ...options, - locale, - prevLocale: prevState.locale, - })); - } - - /** - * Current locale. - */ - function currentLocale(): string { - return options.locale || options.fallbackLocale; - } - - return createElement( - Context.Provider, - { - value: { - t, - tChoice, - loading, - isLocale, - getLocales, - currentLocale, - setLocale, - }, - }, - children, - ); -} diff --git a/resources/js/lib/laravel-react-i18n/utils/pluralization.ts b/resources/js/lib/laravel-react-i18n/utils/pluralization.ts deleted file mode 100644 index 118bd1b5e..000000000 --- a/resources/js/lib/laravel-react-i18n/utils/pluralization.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { getPluralIndex } from '../contrib/get-plural-index'; - -/** - * Select a proper translation string based on the given number. - * - * @param message - * @param number - * @param locale - */ -export default function pluralization(message: string, number: number, locale: string): string { - let segments = message.split('|'); - const extracted = extract(segments, number); - - if (extracted !== null) { - return extracted.trim(); - } - - segments = stripConditions(segments); - const pluralIndex = getPluralIndex(locale, number); - - if (segments.length === 1 || !segments[pluralIndex]) { - return segments[0]; - } - - return segments[pluralIndex]; -} - -/** - * Extract a translation string using inline conditions. - * - * @param segments - * @param number - */ -function extract(segments: string[], number: number): string | null { - let result: string | null = null; - - for (const segment of segments) { - if (result !== null) continue; - result = extractFromString(segment, number); - } - - return result; -} - -/** - * Get the translation string if the condition matches. - * - * @param part - * @param number - */ -function extractFromString(part: string, number: number): string | null { - const matches = part.match(/^[{[]([^[\]{}]*)[}\]](.*)/s) || []; - - if (!matches?.[1] && !matches?.[2]) { - return null; - } - - const condition = matches[1]; - const value = matches[2]; - - if (condition.includes(',')) { - const [from, to] = condition.split(','); - - if ( - (to === '*' && number >= parseFloat(from)) || - (from === '*' && number <= parseFloat(to)) || - (number >= parseFloat(from) && number <= parseFloat(to)) - ) { - return value; - } - } - - return parseFloat(condition) === number ? value : null; -} - -/** - * Strip the inline conditions from each segment, just leaving the text. - * - * @param segments - */ -function stripConditions(segments: string[]): string[] { - return segments.map((part) => part.replace(/^[{[]([^[\]{}]*)[}\]]/, '')); -} diff --git a/resources/js/lib/laravel-react-i18n/utils/recognizer.ts b/resources/js/lib/laravel-react-i18n/utils/recognizer.ts deleted file mode 100644 index cfc7e9767..000000000 --- a/resources/js/lib/laravel-react-i18n/utils/recognizer.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * - * @param files - */ -export default function recognizer( - files: Record | Record Promise>, -) { - const jsonLocales: string[] = []; - const phpLocales: string[] = []; - const jsonFileLocales: Record = {}; - const phpFileLocales: Record = {}; - - Object.keys(files).map((file) => { - const match = file.match(/(.*)\/(.*).json$/) || []; - - if (match?.[0] && match?.[2]) { - if (match[2].match(/php_/)) { - const locale = match[2].replace('php_', ''); - phpLocales.push(locale); - phpLocales.sort(); - phpFileLocales[locale] = match[0]; - } else { - const locale = match[2]; - jsonLocales.push(locale); - jsonLocales.sort(); - jsonFileLocales[locale] = match[0]; - } - } - }); - - const locales = [...jsonLocales, ...phpLocales] - .filter((locale, index, array) => array.indexOf(locale) === index) - .sort(); - - return { - /** - * - * @param locale - */ - isLocale: (locale: string): boolean => locales.includes(locale), - /** - * - */ - getLocales: () => locales, - /** - * - * @param locale - */ - isJsonLocale: (locale: string): boolean => jsonLocales.includes(locale), - /** - * - */ - getJsonLocales: () => jsonLocales, - /** - * - * @param locale - */ - isPhpLocale: (locale: string): boolean => phpLocales.includes(locale), - /** - * - */ - getPhpLocales: () => phpLocales, - /** - * - */ - getJsonFile: (locale: string): string => jsonFileLocales?.[locale], - /** - * - */ - getPhpFile: (locale: string): string => phpFileLocales?.[locale], - }; -} diff --git a/resources/js/lib/laravel-react-i18n/utils/replacer.ts b/resources/js/lib/laravel-react-i18n/utils/replacer.ts deleted file mode 100644 index 1de8aa48b..000000000 --- a/resources/js/lib/laravel-react-i18n/utils/replacer.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type ReplacementsInterface from '../interfaces/replacements'; - -/** - * Make the place-holder replacements on a line. - * - * @param message - * @param replacements - */ -export default function replacer(message: string, replacements?: ReplacementsInterface): string { - for (const [key, value] of Object.entries(replacements || [])) { - message = message - .replaceAll(`:${key}`, value.toString()) - .replaceAll(`:${key.toUpperCase()}`, value.toString().toUpperCase()) - .replaceAll(`:${capitalize(key)}`, capitalize(value.toString())); - } - - return message; -} - -/** - * Capitalizing string. - * - * @param str - */ -function capitalize(str: string) { - return str.charAt(0).toUpperCase() + str.slice(1); -} diff --git a/resources/js/lib/laravel-react-i18n/utils/resolver.ts b/resources/js/lib/laravel-react-i18n/utils/resolver.ts deleted file mode 100644 index 044fb0146..000000000 --- a/resources/js/lib/laravel-react-i18n/utils/resolver.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type LocaleFileInterface from '../interfaces/locale-file'; -import recognizer from './recognizer'; - -/** - * Resolver the language file. - * - * @param files - * @param locale - */ -export default function resolver( - files: Record | Record Promise>, - locale: string, -): LocaleFileInterface[] { - const { isJsonLocale, isPhpLocale, getJsonFile, getPhpFile } = recognizer(files); - - const jsonLocale = isJsonLocale(locale) ? files[getJsonFile(locale)] : undefined; - const phpLocale = isPhpLocale(locale) ? files[getPhpFile(locale)] : undefined; - - const getType = (obj: string) => Object.prototype.toString.call(obj); - - if ( - ['[object Promise]', '[object Module]'].includes(getType(jsonLocale)) || - ['[object Promise]', '[object Module]'].includes(getType(phpLocale)) - ) { - return [jsonLocale ? jsonLocale : { default: {} }, phpLocale ? phpLocale : { default: {} }]; - } - - if (getType(jsonLocale) === '[object Object]' || getType(phpLocale) === '[object Object]') { - return [{ default: jsonLocale || {} }, { default: phpLocale || {} }]; - } - - if (getType(jsonLocale) === '[object Function]' || getType(phpLocale) === '[object Function]') { - return [jsonLocale ? jsonLocale() : { default: {} }, phpLocale ? phpLocale() : { default: {} }]; - } - - return [{ default: {} }, { default: {} }]; -} diff --git a/resources/js/lib/laravel-react-i18n/vite.ts b/resources/js/lib/laravel-react-i18n/vite.ts deleted file mode 100644 index 3ed64308b..000000000 --- a/resources/js/lib/laravel-react-i18n/vite.ts +++ /dev/null @@ -1,138 +0,0 @@ -import fs from 'fs'; -import { createLogger } from 'vite'; - -import { convertToKeyType, saveKeyTypeToFile } from './plugin/key-type'; -import locale from './plugin/locale'; -import parser from './plugin/parser'; - -interface ConfigInterface { - langDirname?: string; - typeDestinationPath?: string; - typeTranslationKeys?: boolean; -} - -/** - * - */ -export default function i18n(config?: ConfigInterface) { - const langDirname = config?.langDirname ? config.langDirname : 'lang'; - - const logger = createLogger('info', { prefix: '[laravel-react-i18n]' }); - - let isPhpLocale = false; - let files: { path: string; basename: string }[] = []; - let exitHandlersBound = false; - let jsonLocales: string[] = []; - let phpLocales: string[] = []; - - function clean() { - for (const file of files) fs.existsSync(file.path) && fs.unlinkSync(file.path); - files = []; - } - - function pushKeys(keys: string[], locales: string[]) { - if ( - typeof process.env.VITE_LARAVEL_REACT_I18N_LOCALE !== 'undefined' && - locales.includes(process.env.VITE_LARAVEL_REACT_I18N_LOCALE) - ) { - const fileName = isPhpLocale - ? `php_${process.env.VITE_LARAVEL_REACT_I18N_LOCALE}` - : process.env.VITE_LARAVEL_REACT_I18N_LOCALE; - keys.push(convertToKeyType(langDirname, fileName)); - } - - if ( - typeof process.env.VITE_LARAVEL_REACT_I18N_FALLBACK_LOCALE !== 'undefined' && - locales.includes(process.env.VITE_LARAVEL_REACT_I18N_FALLBACK_LOCALE) && - process.env.VITE_LARAVEL_REACT_I18N_LOCALE !== - process.env.VITE_LARAVEL_REACT_I18N_FALLBACK_LOCALE - ) { - const fileName = isPhpLocale - ? `php_${process.env.VITE_LARAVEL_REACT_I18N_FALLBACK_LOCALE}` - : process.env.VITE_LARAVEL_REACT_I18N_FALLBACK_LOCALE; - keys.push(convertToKeyType(langDirname, fileName)); - } - } - - return { - name: 'i18n', - enforce: 'post', - config() { - const keys: string[] = []; - - // Check language directory is exists. - if (!fs.existsSync(langDirname)) { - const msg = [ - 'Language directory is not exist, maybe you did not publish the language files with `php artisan lang:publish`.', - 'For more information please visit: https://laravel.com/docs/10.x/localization#publishing-the-language-files', - ]; - - msg.map((str) => logger.error(str, { timestamp: true })); - - return; - } - - // JSON-file locales. - jsonLocales = locale.getJsonLocale(langDirname); - - if (config?.typeTranslationKeys) { - pushKeys(keys, jsonLocales); - } - - // PHP-file locales. - phpLocales = locale.getPhpLocale(langDirname); - - if (phpLocales.length > 0) { - files = parser(langDirname); - isPhpLocale = true; - - if (config?.typeTranslationKeys) { - pushKeys(keys, phpLocales); - } - } else { - const msg = [ - 'Language directory not contain php translations files.', - 'For more information please visit: https://laravel.com/docs/10.x/localization#introduction', - ]; - - msg.map((str) => logger.info(str, { timestamp: true })); - } - - if (config?.typeTranslationKeys) { - saveKeyTypeToFile(keys.join('|'), config?.typeDestinationPath); - } - }, - buildEnd: clean, - handleHotUpdate(ctx: any) { - const keys: string[] = []; - - if (config?.typeTranslationKeys) { - pushKeys(keys, jsonLocales); - } - - if (isPhpLocale) { - if (/lang\/.*\.php$/.test(ctx.file)) { - files = parser(langDirname); - } - - if (config?.typeTranslationKeys) { - pushKeys(keys, phpLocales); - } - } - - if (config?.typeTranslationKeys) { - saveKeyTypeToFile(keys.join('|'), config?.typeDestinationPath); - } - }, - configureServer() { - if (exitHandlersBound) return; - - process.on('exit', clean); - process.on('SIGINT', process.exit); - process.on('SIGTERM', process.exit); - process.on('SIGHUP', process.exit); - - exitHandlersBound = true; - }, - }; -} diff --git a/resources/js/ssr.tsx b/resources/js/ssr.tsx index edf7e7467..82e935188 100644 --- a/resources/js/ssr.tsx +++ b/resources/js/ssr.tsx @@ -3,6 +3,7 @@ import { createInertiaApp } from '@inertiajs/react'; import createServer from '@inertiajs/react/server'; +import { LaravelReactI18nProvider } from 'laravel-react-i18n'; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; import ReactDOMServer from 'react-dom/server'; import type { RouteName, RouteParams } from 'ziggy-js'; @@ -10,7 +11,6 @@ import type { RouteName, RouteParams } from 'ziggy-js'; import { route } from '../../vendor/tightenco/ziggy'; import { AppProviders } from './common/components/AppProviders'; import type { AppGlobalProps } from './common/models'; -import { LaravelReactI18nProvider } from './lib/laravel-react-i18n'; const appName = import.meta.env.APP_NAME ?? 'RetroAchievements'; const inertiaDaemonPort = import.meta.env.VITE_INERTIA_SSR_PORT ?? 13714; diff --git a/resources/js/test/setup.tsx b/resources/js/test/setup.tsx index 370f8da80..36b1d6648 100644 --- a/resources/js/test/setup.tsx +++ b/resources/js/test/setup.tsx @@ -20,7 +20,7 @@ vi.mock('@inertiajs/react', () => ({ * output no DOM. Rather than trying to hack laravel-react-i18n to fix this, * it's better to just mock the functions we need to test basic UI functionality. */ -vi.mock('@/lib/laravel-react-i18n', () => ({ +vi.mock('laravel-react-i18n', () => ({ __esModule: true, LaravelReactI18nProvider: ({ children }: any) => <>{children}, diff --git a/vite.config.ts b/vite.config.ts index 45dc11055..f4297043c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,14 +2,13 @@ import react from '@vitejs/plugin-react'; import { existsSync, readFileSync } from 'fs'; +import i18n from 'laravel-react-i18n/vite'; import laravel from 'laravel-vite-plugin'; import { homedir } from 'os'; import { resolve } from 'path'; import type { PluginOption } from 'vite'; import { defineConfig, loadEnv } from 'vite'; -import i18n from './resources/js/lib/laravel-react-i18n/vite'; - export default defineConfig(({ mode, isSsrBuild }) => { const env = loadEnv(mode, process.cwd(), '');