Skip to content

Commit b266643

Browse files
araujoguiovflowd
andauthored
Set up i18n package (#6991)
* build: change package scopes * build: create packages workspace * feat: create initial i18n package * feat: create import locale fn * chore: some peaks * refactor: remove duplicated type * chore: set up lint * chore: dep fix * fix: crowdin config * fix: fix modules, eslint * fix: fixed lint, test and build process * chore: code review changes * chore: fix turbo.json * chore: fix package versions * fix: fix build due to shiki * chore: (unrelated) shiki js engine --------- Co-authored-by: Claudio Wunder <[email protected]>
1 parent a2cb6d8 commit b266643

35 files changed

+2732
-3962
lines changed

Diff for: .github/workflows/translations-pr.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ on:
1212
- 'apps/site/pages/**/*.mdx'
1313
- '!apps/site/pages/en/**/*.md'
1414
- '!apps/site/pages/en/**/*.mdx'
15-
- 'apps/site/i18n/locales/*.json'
16-
- '!apps/site/i18n/locales/en.json'
15+
- 'packages/i18n/locales/*.json'
16+
- '!packages/i18n/locales/en.json'
1717

1818
permissions:
1919
actions: read

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ tsconfig.tsbuildinfo
3333

3434
# Sentry Config File
3535
.sentryclirc
36+
37+
dist/

Diff for: .husky/pre-commit

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
#!/usr/bin/env sh
2-
. "$(dirname -- "$0")/_/husky.sh"
3-
41
# lint and format staged files
52
npx lint-staged
63

Diff for: apps/site/.storybook/preview.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import englishLocale from '@node-core/website-i18n/locales/en.json';
12
import { withThemeByDataAttribute } from '@storybook/addon-themes';
23
import type { Preview, ReactRenderer } from '@storybook/react';
34
import { NextIntlClientProvider } from 'next-intl';
45

56
import { STORYBOOK_MODES, STORYBOOK_SIZES } from '@/.storybook/constants';
6-
import englishLocale from '@/i18n/locales/en.json';
77
import { NotificationProvider } from '@/providers/notificationProvider';
88

99
import '../next.fonts';

Diff for: apps/site/components/Common/LanguageDropDown/index.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { LanguageIcon } from '@heroicons/react/24/outline';
2+
import type { LocaleConfig } from '@node-core/website-i18n/types';
23
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
34
import classNames from 'classnames';
45
import { useTranslations } from 'next-intl';
56
import type { FC } from 'react';
67

7-
import type { LocaleConfig } from '@/types';
8-
98
import styles from './index.module.css';
109

1110
type SimpleLocaleConfig = Pick<LocaleConfig, 'name' | 'code'>;

Diff for: apps/site/components/Downloads/Release/ReleaseCodeBox.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import type { FC } from 'react';
66

77
import CodeBox from '@/components/Common/CodeBox';
88
import { ReleaseContext } from '@/providers/releaseProvider';
9-
import { getShiki, highlightToHtml } from '@/util/getHighlighter';
9+
import { shikiPromise, highlightToHtml } from '@/util/getHighlighter';
1010
import { getNodeDownloadSnippet } from '@/util/getNodeDownloadSnippet';
1111

12-
// We cannot do top-level awaits on utilities or code that is imported by client-only components
13-
// hence we only declare a Promise and let it be fulfilled by the first call to the function
14-
const memoizedShiki = getShiki();
12+
const memoizedShiki = shikiPromise.then(highlightToHtml);
1513

1614
const ReleaseCodeBox: FC = () => {
1715
const { platform, os, release } = useContext(ReleaseContext);
@@ -25,9 +23,7 @@ const ReleaseCodeBox: FC = () => {
2523
// but usually we should recommend users to download "major" versions
2624
// since our Download Buttons get the latest minor of a major, it does make sense
2725
// to request installation of a major via a package manager
28-
memoizedShiki
29-
.then(shiki => highlightToHtml(shiki)(updatedCode, 'bash'))
30-
.then(setCode);
26+
memoizedShiki.then(shiki => shiki(updatedCode, 'bash')).then(setCode);
3127
// Only react when the specific release number changed
3228
// eslint-disable-next-line react-hooks/exhaustive-deps
3329
}, [release.versionWithPrefix, os, platform]);

Diff for: apps/site/eslint.config.js

+9-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { fixupPluginRules } from '@eslint/compat';
21
import { FlatCompat } from '@eslint/eslintrc';
32
import js from '@eslint/js';
43
import importX from 'eslint-plugin-import-x';
@@ -9,27 +8,16 @@ import storybook from 'eslint-plugin-storybook';
98
import tseslint from 'typescript-eslint';
109

1110
const compat = new FlatCompat();
12-
const pluginToPatch = '@next/next';
1311

14-
const compatConfig = compat
15-
.config({
16-
extends: [
17-
// https://github.com/vercel/next.js/discussions/49337
18-
'plugin:@next/eslint-plugin-next/core-web-vitals',
19-
20-
// https://github.com/facebook/react/issues/28313
21-
'plugin:react-hooks/recommended',
22-
],
23-
})
24-
.map(entry => {
25-
if (Object.hasOwn(entry.plugins, pluginToPatch)) {
26-
entry.plugins[pluginToPatch] = fixupPluginRules(
27-
entry.plugins[pluginToPatch]
28-
);
29-
}
12+
const compatConfig = compat.config({
13+
extends: [
14+
// https://github.com/vercel/next.js/discussions/49337
15+
'plugin:@next/eslint-plugin-next/core-web-vitals',
3016

31-
return entry;
32-
});
17+
// https://github.com/facebook/react/issues/28313
18+
'plugin:react-hooks/recommended',
19+
],
20+
});
3321

3422
export default tseslint.config(
3523
{
@@ -58,6 +46,7 @@ export default tseslint.config(
5846
'no-relative-import-paths': noRelativeImportPaths,
5947
},
6048
rules: {
49+
'@next/next/no-duplicate-head': 'off',
6150
'@typescript-eslint/array-type': ['error', { default: 'generic' }],
6251
'@typescript-eslint/consistent-type-imports': 'error',
6352
'@typescript-eslint/no-require-imports': 'off',

Diff for: apps/site/i18n.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { importLocale } from '@node-core/website-i18n';
12
import { getRequestConfig } from 'next-intl/server';
23

34
import { availableLocaleCodes } from '@/next.locales.mjs';
@@ -7,13 +8,15 @@ const loadLocaleDictionary = async (locale: string) => {
78
if (locale === 'en') {
89
// This enables HMR on the English Locale, so that instant refresh
910
// happens while we add/change texts on the source locale
10-
return import('./i18n/locales/en.json').then(f => f.default);
11+
return import('@node-core/website-i18n/locales/en.json').then(
12+
f => f.default
13+
);
1114
}
1215

1316
if (availableLocaleCodes.includes(locale)) {
1417
// Other languages don't really require HMR as they will never be development languages
1518
// so we can load them dynamically
16-
return import(`./i18n/locales/${locale}.json`).then(f => f.default);
19+
return importLocale(locale);
1720
}
1821

1922
throw new Error(`Unsupported locale: ${locale}`);

Diff for: apps/site/next-env.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
/// <reference types="next/navigation-types/compat/navigation" />
44

55
// NOTE: This file should not be edited
6-
// see https://nextjs.org/docs/basic-features/typescript for more information.
6+
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

Diff for: apps/site/next.locales.mjs

+13-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
'use strict';
22

3-
import localeConfig from './i18n/config.json' assert { type: 'json' };
3+
import {
4+
getAvailableLocales,
5+
getAvailableLocaleCodes,
6+
getDefaultLocale,
7+
getAvailableLocalesMap,
8+
getAllLocaleCodes,
9+
} from '@node-core/website-i18n';
410

511
// As set of available and enabled locales for the website
612
// This is used for allowing us to redirect the user to any
713
// of the available locales that we have enabled on the website
8-
const availableLocales = localeConfig.filter(locale => locale.enabled);
14+
const availableLocales = getAvailableLocales();
915

1016
// This gives an easy way of accessing all available locale codes
11-
const availableLocaleCodes = availableLocales.map(locale => locale.code);
17+
const availableLocaleCodes = getAvailableLocaleCodes();
1218

1319
// This provides the default locale information for the Next.js Application
1420
// This is marked by the unique `locale.default` property on the `en` locale
15-
/** @type {import('./types').LocaleConfig} */
16-
const defaultLocale = availableLocales.find(locale => locale.default);
21+
/** @type {import('@node-core/website-i18n/types').LocaleConfig} */
22+
const defaultLocale = getDefaultLocale();
1723

1824
// Creates a Map of available locales for easy access
19-
const availableLocalesMap = Object.fromEntries(
20-
localeConfig.map(locale => [locale.code, locale])
21-
);
25+
const availableLocalesMap = getAvailableLocalesMap();
2226

2327
// Creates all supported locales
24-
const allLocaleCodes = localeConfig.map(locale => locale.code);
28+
const allLocaleCodes = getAllLocaleCodes();
2529

2630
export {
2731
allLocaleCodes,

Diff for: apps/site/next.mdx.shiki.mjs

+3-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import classNames from 'classnames';
44
import { toString } from 'hast-util-to-string';
55
import { SKIP, visit } from 'unist-util-visit';
66

7-
import { getShiki, highlightToHast } from './util/getHighlighter';
7+
import { shikiPromise, highlightToHast } from './util/getHighlighter';
88

99
// This is what Remark will use as prefix within a <pre> className
1010
// to attribute the current language of the <pre> element
@@ -58,7 +58,7 @@ export default function rehypeShikiji() {
5858
// We do a top-level await, since the Unist-tree visitor
5959
// is synchronous, and it makes more sense to do a top-level
6060
// await, rather than an await inside the visitor function
61-
const memoizedShiki = await getShiki();
61+
const memoizedShiki = highlightToHast(await shikiPromise);
6262

6363
visit(tree, 'element', (_, index, parent) => {
6464
const languages = [];
@@ -174,10 +174,7 @@ export default function rehypeShikiji() {
174174
const languageId = codeLanguage.slice(languagePrefix.length);
175175

176176
// Parses the <pre> contents and returns a HAST tree with the highlighted code
177-
const { children } = highlightToHast(memoizedShiki)(
178-
preElementContents,
179-
languageId
180-
);
177+
const { children } = memoizedShiki(preElementContents, languageId);
181178

182179
// Adds the original language back to the <pre> element
183180
children[0].properties.class = classNames(

Diff for: apps/site/package.json

+47-50
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"type": "module",
33
"private": true,
4-
"name": "@nodejs/website",
4+
"name": "@node-core/website",
55
"description": "Nodejs.org Website",
66
"homepage": "https://nodejs.org",
77
"repository": {
@@ -38,96 +38,93 @@
3838
"dependencies": {
3939
"@heroicons/react": "~2.1.5",
4040
"@mdx-js/mdx": "^3.0.1",
41+
"@node-core/website-i18n": "*",
4142
"@nodevu/core": "~0.1.0",
4243
"@orama/highlight": "^0.1.6",
43-
"@oramacloud/client": "^1.3.2",
44-
"@radix-ui/react-accessible-icon": "^1.0.3",
45-
"@radix-ui/react-avatar": "^1.0.4",
46-
"@radix-ui/react-dialog": "^1.0.5",
47-
"@radix-ui/react-dropdown-menu": "^2.0.6",
48-
"@radix-ui/react-label": "^2.0.2",
49-
"@radix-ui/react-scroll-area": "^1.0.5",
50-
"@radix-ui/react-select": "^2.0.0",
51-
"@radix-ui/react-slot": "^1.0.2",
52-
"@radix-ui/react-tabs": "^1.0.4",
53-
"@radix-ui/react-toast": "^1.1.5",
44+
"@oramacloud/client": "^1.3.15",
45+
"@radix-ui/react-accessible-icon": "^1.1.0",
46+
"@radix-ui/react-avatar": "^1.1.0",
47+
"@radix-ui/react-dialog": "^1.1.1",
48+
"@radix-ui/react-dropdown-menu": "^2.1.1",
49+
"@radix-ui/react-label": "^2.1.0",
50+
"@radix-ui/react-scroll-area": "^1.1.0",
51+
"@radix-ui/react-select": "^2.1.1",
52+
"@radix-ui/react-slot": "^1.1.0",
53+
"@radix-ui/react-tabs": "^1.1.0",
54+
"@radix-ui/react-toast": "^1.2.1",
5455
"@savvywombat/tailwindcss-grid-areas": "~4.0.0",
55-
"@sentry/nextjs": "~8.14.0",
56+
"@sentry/nextjs": "~8.30.0",
5657
"@tailwindcss/container-queries": "~0.1.1",
57-
"@types/node": "20.16.3",
58+
"@types/node": "20.16.5",
5859
"@vcarl/remark-headings": "~0.1.0",
5960
"@vercel/analytics": "~1.3.1",
60-
"@vercel/speed-insights": "~1.0.10",
61-
"autoprefixer": "~10.4.18",
61+
"@vercel/speed-insights": "~1.0.12",
62+
"autoprefixer": "~10.4.20",
6263
"classnames": "~2.5.1",
6364
"cross-env": "7.0.3",
6465
"dedent": "1.5.3",
6566
"feed": "~4.2.2",
6667
"github-slugger": "~2.0.0",
67-
"glob": "~10.4.1",
68+
"glob": "~11.0.0",
6869
"gray-matter": "~4.0.3",
69-
"next": "~14.2.7",
70-
"next-intl": "~3.19.0",
70+
"next": "~14.2.11",
71+
"next-intl": "~3.19.1",
7172
"next-themes": "~0.3.0",
72-
"postcss": "~8.4.40",
73-
"postcss-calc": "~10.0.0",
73+
"postcss": "~8.4.45",
74+
"postcss-calc": "~10.0.2",
7475
"postcss-import": "~16.1.0",
75-
"postcss-mixins": "~10.0.1",
76+
"postcss-mixins": "~11.0.1",
7677
"postcss-simple-vars": "~7.0.1",
7778
"react": "^18.3.1",
7879
"react-dom": "^18.3.1",
7980
"rehype-autolink-headings": "~7.1.0",
8081
"rehype-slug": "~6.0.0",
8182
"remark-gfm": "~4.0.0",
8283
"remark-reading-time": "~2.0.1",
83-
"semver": "~7.6.0",
84-
"shiki": "~1.15.2",
85-
"tailwindcss": "^3.4.7",
86-
"typescript": "~5.5.3",
84+
"semver": "~7.6.3",
85+
"shiki": "~1.17.5",
86+
"tailwindcss": "^3.4.11",
8787
"unist-util-visit": "~5.0.0",
8888
"vfile": "~6.0.3",
8989
"vfile-matter": "~5.0.0"
9090
},
9191
"devDependencies": {
92-
"@eslint/compat": "^1.1.1",
93-
"@next/eslint-plugin-next": "^14.2.8",
94-
"@storybook/addon-controls": "~8.2.7",
95-
"@storybook/addon-interactions": "~8.2.7",
96-
"@storybook/addon-themes": "~8.2.7",
97-
"@storybook/addon-viewport": "~8.2.7",
98-
"@storybook/nextjs": "~8.2.7",
92+
"@eslint/compat": "~1.1.1",
93+
"@next/eslint-plugin-next": "~14.2.11",
94+
"@storybook/addon-controls": "~8.3.0",
95+
"@storybook/addon-interactions": "~8.3.0",
96+
"@storybook/addon-themes": "~8.3.0",
97+
"@storybook/addon-viewport": "~8.3.0",
98+
"@storybook/nextjs": "~8.3.0",
9999
"@testing-library/jest-dom": "~6.5.0",
100100
"@testing-library/react": "~16.0.1",
101101
"@testing-library/user-event": "~14.5.2",
102-
"@types/jest": "29.5.12",
102+
"@types/jest": "29.5.13",
103103
"@types/react": "^18.3.5",
104104
"@types/react-dom": "^18.3.0",
105105
"@types/semver": "~7.5.8",
106-
"eslint": "^9.10.0",
107-
"eslint-config-next": "~14.2.8",
108-
"eslint-config-prettier": "9.1.0",
109-
"eslint-import-resolver-typescript": "^3.6.3",
110-
"eslint-plugin-import-x": "^4.2.1",
111-
"eslint-plugin-mdx": "3.1.5",
112-
"eslint-plugin-no-relative-import-paths": "^1.5.3",
113-
"eslint-plugin-react": "^7.35.2",
114-
"eslint-plugin-react-hooks": "5.1.0-rc.0",
115-
"eslint-plugin-storybook": "0.9.0--canary.156.da7873a.0",
106+
"eslint": "~9.10.0",
107+
"eslint-config-next": "~14.2.11",
108+
"eslint-import-resolver-typescript": "~3.6.3",
109+
"eslint-plugin-import-x": "~4.2.1",
110+
"eslint-plugin-mdx": "~3.1.5",
111+
"eslint-plugin-no-relative-import-paths": "~1.5.5",
112+
"eslint-plugin-react": "~7.36.1",
113+
"eslint-plugin-react-hooks": "5.1.0-rc-4c58fce7-20240904",
114+
"eslint-plugin-storybook": "0.9.0--canary.156.26b630a.0",
116115
"handlebars": "4.7.8",
117116
"jest": "29.7.0",
118117
"jest-environment-jsdom": "29.7.0",
119118
"jest-junit": "16.0.0",
120119
"remark-frontmatter": "5.0.0",
121120
"remark-preset-lint-node": "5.1.2",
122-
"storybook": "~8.2.7",
121+
"storybook": "~8.3.0",
123122
"stylelint": "16.9.0",
124123
"stylelint-config-standard": "36.0.1",
125124
"stylelint-order": "6.0.4",
126-
"stylelint-selector-bem-pattern": "4.0.0",
127-
"typescript-eslint": "^8.4.0",
125+
"stylelint-selector-bem-pattern": "4.0.1",
126+
"typescript": "~5.5.4",
127+
"typescript-eslint": "~8.5.0",
128128
"user-agent-data-types": "0.4.2"
129-
},
130-
"overrides": {
131-
"eslint": "$eslint"
132129
}
133130
}

0 commit comments

Comments
 (0)