️ feat: refactor sorting parameters in post listing API and components

- Renamed `orderedBy` to `orderBy` and `reverse` to `desc` in ListPostsParams interface and related functions.
- Updated all usages of the sorting parameters in the post listing logic to reflect the new naming convention.

feat: add user-related API functions

- Implemented `getLoginUser` and `getUserById` functions in the user API to fetch user details.
- Enhanced user model to include `language` property.

feat: integrate next-intl for internationalization

- Added `next-intl` plugin to Next.js configuration for improved localization support.
- Removed previous i18n implementation and replaced it with a new structure using JSON files for translations.
- Created locale files for English, Japanese, and Chinese with basic translations.
- Implemented a request configuration to handle user locales and messages dynamically.

fix: clean up unused imports and code

- Removed unused i18n utility functions and language settings from device context.
- Cleaned up commented-out code in blog card component and sidebar.

chore: update dependencies

- Added `deepmerge` for merging locale messages.
- Updated package.json and pnpm-lock.yaml to reflect new dependencies.
This commit is contained in:
2025-07-26 09:48:23 +08:00
parent 9984f665d4
commit e659de23fb
46 changed files with 660 additions and 331 deletions

183
web/pnpm-lock.yaml generated
View File

@ -29,30 +29,30 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
deepmerge:
specifier: ^4.3.1
version: 4.3.1
field-conv:
specifier: ^1.0.9
version: 1.0.9
framer-motion:
specifier: ^12.23.9
version: 12.23.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
i18next:
specifier: ^25.3.2
version: 25.3.2(typescript@5.8.3)
lucide-react:
specifier: ^0.525.0
version: 0.525.0(react@19.1.0)
next:
specifier: 15.4.1
version: 15.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next-intl:
specifier: ^4.3.4
version: 4.3.4(next@15.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
react:
specifier: 19.1.0
version: 19.1.0
react-dom:
specifier: 19.1.0
version: 19.1.0(react@19.1.0)
react-i18next:
specifier: ^15.6.1
version: 15.6.1(i18next@25.3.2(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
react-icons:
specifier: ^5.5.0
version: 5.5.0(react@19.1.0)
@ -101,10 +101,6 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@babel/runtime@7.27.6':
resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==}
engines: {node: '>=6.9.0'}
'@emnapi/core@1.4.4':
resolution: {integrity: sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==}
@ -152,6 +148,24 @@ packages:
resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@formatjs/ecma402-abstract@2.3.4':
resolution: {integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==}
'@formatjs/fast-memoize@2.2.7':
resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==}
'@formatjs/icu-messageformat-parser@2.11.2':
resolution: {integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==}
'@formatjs/icu-skeleton-parser@1.8.14':
resolution: {integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==}
'@formatjs/intl-localematcher@0.5.10':
resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==}
'@formatjs/intl-localematcher@0.6.1':
resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==}
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'}
@ -631,6 +645,9 @@ packages:
'@rushstack/eslint-patch@1.12.0':
resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==}
'@schummar/icu-type-parser@1.21.5':
resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==}
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@ -1097,9 +1114,16 @@ packages:
supports-color:
optional: true
decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@ -1449,17 +1473,6 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
html-parse-stringify@3.0.1:
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
i18next@25.3.2:
resolution: {integrity: sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==}
peerDependencies:
typescript: ^5
peerDependenciesMeta:
typescript:
optional: true
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@ -1480,6 +1493,9 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
intl-messageformat@10.7.16:
resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==}
is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
@ -1786,6 +1802,20 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
negotiator@1.0.0:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
next-intl@4.3.4:
resolution: {integrity: sha512-VWLIDlGbnL/o4LnveJTJD1NOYN8lh3ZAGTWw2krhfgg53as3VsS4jzUVnArJdqvwtlpU/2BIDbWTZ7V4o1jFEw==}
peerDependencies:
next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
typescript: ^5.0.0
peerDependenciesMeta:
typescript:
optional: true
next@15.4.1:
resolution: {integrity: sha512-eNKB1q8C7o9zXF8+jgJs2CzSLIU3T6bQtX6DcTnCq1sIR1CJ0GlSyRs1BubQi3/JgCnr9Vr+rS5mOMI38FFyQw==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
@ -1915,22 +1945,6 @@ packages:
peerDependencies:
react: ^19.1.0
react-i18next@15.6.1:
resolution: {integrity: sha512-uGrzSsOUUe2sDBG/+FJq2J1MM+Y4368/QW8OLEKSFvnDflHBbZhSd1u3UkW0Z06rMhZmnB/AQrhCpYfE5/5XNg==}
peerDependencies:
i18next: '>= 23.2.3'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
typescript: ^5
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
typescript:
optional: true
react-icons@5.5.0:
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
peerDependencies:
@ -2222,6 +2236,11 @@ packages:
'@types/react':
optional: true
use-intl@4.3.4:
resolution: {integrity: sha512-sHfiU0QeJ1rirNWRxvCyvlSh9+NczcOzRnPyMeo2rtHXhVnBsvMRjE+UG4eh3lRhCxrvcqei/I0lBxsc59on1w==}
peerDependencies:
react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
use-sidecar@1.1.3:
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
engines: {node: '>=10'}
@ -2232,10 +2251,6 @@ packages:
'@types/react':
optional: true
void-elements@3.1.0:
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
engines: {node: '>=0.10.0'}
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@ -2278,8 +2293,6 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.12
'@jridgewell/trace-mapping': 0.3.29
'@babel/runtime@7.27.6': {}
'@emnapi/core@1.4.4':
dependencies:
'@emnapi/wasi-threads': 1.0.3
@ -2340,6 +2353,36 @@ snapshots:
'@eslint/core': 0.15.1
levn: 0.4.1
'@formatjs/ecma402-abstract@2.3.4':
dependencies:
'@formatjs/fast-memoize': 2.2.7
'@formatjs/intl-localematcher': 0.6.1
decimal.js: 10.6.0
tslib: 2.8.1
'@formatjs/fast-memoize@2.2.7':
dependencies:
tslib: 2.8.1
'@formatjs/icu-messageformat-parser@2.11.2':
dependencies:
'@formatjs/ecma402-abstract': 2.3.4
'@formatjs/icu-skeleton-parser': 1.8.14
tslib: 2.8.1
'@formatjs/icu-skeleton-parser@1.8.14':
dependencies:
'@formatjs/ecma402-abstract': 2.3.4
tslib: 2.8.1
'@formatjs/intl-localematcher@0.5.10':
dependencies:
tslib: 2.8.1
'@formatjs/intl-localematcher@0.6.1':
dependencies:
tslib: 2.8.1
'@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.6':
@ -2719,6 +2762,8 @@ snapshots:
'@rushstack/eslint-patch@1.12.0': {}
'@schummar/icu-type-parser@1.21.5': {}
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
@ -3199,8 +3244,12 @@ snapshots:
dependencies:
ms: 2.1.3
decimal.js@10.6.0: {}
deep-is@0.1.4: {}
deepmerge@4.3.1: {}
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
@ -3699,16 +3748,6 @@ snapshots:
dependencies:
function-bind: 1.1.2
html-parse-stringify@3.0.1:
dependencies:
void-elements: 3.1.0
i18next@25.3.2(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.27.6
optionalDependencies:
typescript: 5.8.3
ignore@5.3.2: {}
ignore@7.0.5: {}
@ -3726,6 +3765,13 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
intl-messageformat@10.7.16:
dependencies:
'@formatjs/ecma402-abstract': 2.3.4
'@formatjs/fast-memoize': 2.2.7
'@formatjs/icu-messageformat-parser': 2.11.2
tslib: 2.8.1
is-array-buffer@3.0.5:
dependencies:
call-bind: 1.0.8
@ -4003,6 +4049,18 @@ snapshots:
natural-compare@1.4.0: {}
negotiator@1.0.0: {}
next-intl@4.3.4(next@15.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3):
dependencies:
'@formatjs/intl-localematcher': 0.5.10
negotiator: 1.0.0
next: 15.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
use-intl: 4.3.4(react@19.1.0)
optionalDependencies:
typescript: 5.8.3
next@15.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@next/env': 15.4.1
@ -4140,16 +4198,6 @@ snapshots:
react: 19.1.0
scheduler: 0.26.0
react-i18next@15.6.1(i18next@25.3.2(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.27.6
html-parse-stringify: 3.0.1
i18next: 25.3.2(typescript@5.8.3)
react: 19.1.0
optionalDependencies:
react-dom: 19.1.0(react@19.1.0)
typescript: 5.8.3
react-icons@5.5.0(react@19.1.0):
dependencies:
react: 19.1.0
@ -4541,6 +4589,13 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.8
use-intl@4.3.4(react@19.1.0):
dependencies:
'@formatjs/fast-memoize': 2.2.7
'@schummar/icu-type-parser': 1.21.5
intl-messageformat: 10.7.16
react: 19.1.0
use-sidecar@1.1.3(@types/react@19.1.8)(react@19.1.0):
dependencies:
detect-node-es: 1.1.0
@ -4549,8 +4604,6 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.8
void-elements@3.1.0: {}
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0