diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..63c841270 --- /dev/null +++ b/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + "next/babel" + ], + "plugins": ["@lingui/babel-plugin-lingui-macro"] +} diff --git a/.gitignore b/.gitignore index f65d00b8c..47ff8eb05 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,11 @@ package-lock.json # production /build +# generated RSS/Atom feeds +public/atom.xml +public/rss.json +public/rss.xml + # misc .DS_Store *.pem @@ -42,3 +47,12 @@ yarn-error.log* .yarn .vscode/ + +# Lingui +src/locales/**/*.mo +src/locales/**/*.js + +# RSS +atom.xml +rss.json +rss.xml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50661092c..f03dbf2a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace @@ -18,17 +18,7 @@ repos: hooks: - id: prettier exclude: \.html$ - - files: "\\.(\ - css|less|scss\ - |graphql|gql\ - |html\ - |js|jsx\ - |json\ - |ts|tsx\ - |vue\ - |yaml|yml\ - )$" + files: "\\.(css|less|scss|graphql|gql|html|js|jsx|json|ts|tsx|vue|yaml|yml)$" - repo: https://github.com/pre-commit/mirrors-prettier rev: 'v4.0.0-alpha.8' hooks: @@ -36,9 +26,6 @@ repos: name: prettier-markdown entry: prettier --write --parser mdx - files: "\\.(\ - |md|markdown|mdown|mkdn\ - |mdx\ - )$" + files: "\\.(md|markdown|mdown|mkdn|mdx)$" # exclude files ending with .html exclude: \.html$ diff --git a/README.md b/README.md index 8b784312e..32e8e2e9a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # xarray landing page -![Vercel Deployment](https://img.shields.io/github/deployments/xarray-contrib/xarray.dev/Production?label=vercel&logo=vercel&style=for-the-badge) +[![Netlify Status](https://api.netlify.com/api/v1/badges/4f940719-54bd-4ff7-95e0-0088dfb3c10f/deploy-status)](https://app.netlify.com/projects/xarraydev/deploys) Landing Page for xarray project. @@ -44,8 +44,60 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. - + + Deploys by Netlify + + +## Translations + +Translations are managed through Crowdin at https://scientific-python.crowdin.com. The xarray project is part of the [Scientific Python](https://scientific-python.org/) organization on Crowdin, which also includes projects such as NumPy, SciPy, and others. + +When creating new content or editing existing content, only the english version of the content should be modified directly in this repository. Once this new content is merged into the main branch of the xarray.dev website, the Crowdin integration will automatically pick up the changes and notify translators that new translations are needed. Once the translation is completed, a pull request will be automatically created in this repository to add the new translated content. + +For more details on how the integration works, see https://scientific-python-translations.github.io/docs/. For more details on how to translate the website, see https://scientific-python-translations.github.io/translate/. + +## Authoring blog post tips + +1. To create a new blog post a good place to start is copying a subfolder under `src/posts/`, so, for example https://xarray.dev/blog/flox is written here https://github.com/xarray-contrib/xarray.dev/blob/e04905f5ea039eb2eb848c0b4945beee323900e4/src/posts/flox/index.md + +### Static assets + +Once you have `src/posts/newpost/index.md` start writing! If you want to include figures or other static assets, they go into a matching `public/posts/newpost` folder. But! reference an images without the `public` part of the path like this: + +```html

- +

- +``` + +### Xarray HTML reprs + +To include an html repr, you must save it first: + +```python +with open('da-repr.html', 'w') as f: + f.write(da._repr_html_()) +``` + +Then put it into the post's static assets folder `public/posts/newpost/da-repr.html`. And finally in `src/posts/newpost/index.md` you can include it with this syntax: + +``` + +``` + +### Toggling visibilty of sections (markdown comments) + +While authoring, you might want to toggle specific sections on and off during rendering. You can do that with this syntax: + +``` +{/* This is a comment that won't be rendered! */} +``` + +### Landing page banner + +If you'd like to add a link to the latest blog post on the landing page banner, edit this section here: + +https://github.com/xarray-contrib/xarray.dev/blob/e04905f5ea039eb2eb848c0b4945beee323900e4/src/components/layout.js#L18 diff --git a/lingui.config.js b/lingui.config.js new file mode 100644 index 000000000..02840dd6f --- /dev/null +++ b/lingui.config.js @@ -0,0 +1,17 @@ +const nextConfig = require('./next.config') + +/** @type {import('@lingui/conf').LinguiConfig} */ +module.exports = { + locales: nextConfig.i18n.locales, + pseudoLocale: 'pseudo', + sourceLocale: nextConfig.i18n.defaultLocale, + fallbackLocales: { + default: 'en', + }, + catalogs: [ + { + path: 'src/locales/{locale}/messages', + include: ['src/'], + }, + ], +} diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 000000000..209622093 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,46 @@ +[build] + publish = ".next" + command = "npm run build" + +[build.environment] + NODE_VERSION = "20" + +# Redirect rules for Next.js +[[redirects]] + from = "/_next/static/*" + to = "/static/:splat" + status = 200 + +# Handle client-side routing +[[redirects]] + from = "/*" + to = "/404.html" + status = 404 + +# Environment variables for different deploy contexts +[context.production.environment] + NEXT_PUBLIC_SITE_URL = "https://xarray.dev" + +[context.deploy-preview.environment] + NEXT_PUBLIC_SITE_URL = "$DEPLOY_PRIME_URL" + +[context.branch-deploy.environment] + NEXT_PUBLIC_SITE_URL = "$DEPLOY_PRIME_URL" + +# Headers for security +[[headers]] + for = "/*" + [headers.values] + X-Content-Type-Options = "nosniff" + Referrer-Policy = "strict-origin-when-cross-origin" + +# Cache static assets +[[headers]] + for = "/static/*" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" + +[[headers]] + for = "/_next/static/*" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" diff --git a/next.config.mjs b/next.config.mjs index 1fd06d66a..07e796ddb 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,5 +1,6 @@ import nextMDX from "@next/mdx" import rehypeSlug from "rehype-slug" +import { i18nConfig } from "./src/config/i18n.mjs" const withMDX = nextMDX({ extension: /\.mdx?$/, @@ -18,4 +19,6 @@ export default withMDX({ images: { domains: ["raw.githubusercontent.com", "numpy.org", "dask.org", "chainer.org", ], }, + // Available locales for the application + i18n: i18nConfig, }) diff --git a/package.json b/package.json index a92b25208..cfea24bc3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "compile": "lingui compile" }, "repository": { "type": "git", @@ -22,8 +23,11 @@ "@chakra-ui/system": "^2.6.2", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@fontsource-variable/inter": "^5.1.0", + "@fontsource-variable/inter": "^5.2.5", "@giscus/react": "^3.0.0", + "@lingui/babel-plugin-lingui-macro": "^5.9.0", + "@lingui/loader": "^5.9.0", + "@lingui/react": "^5.9.0", "@mdx-js/loader": "^3.0.1", "@mdx-js/react": "^3.0.1", "@next/mdx": "^14.2.14", @@ -31,11 +35,12 @@ "d3": "^7.9.0", "date-fns": "^3.0.0", "date-fns-tz": "^3.2.0", - "feed": "^4.2.2", + "feed": "^5.1.0", "framer-motion": "^11.9.0", + "glob": "^11.0.3", "gray-matter": "^4.0.3", "isomorphic-dompurify": "^2.16.0", - "next": "^14.2.15", + "next": "^14.2.30", "next-mdx-remote": "^5.0.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", @@ -47,10 +52,10 @@ }, "devDependencies": { "@types/react": "^18.3.11", - "eslint": "^9.12.0", - "eslint-config-next": "14.2.14", - "playwright": "^1.47.2", - "typescript": ">=5.7.2", - "webpack": "^5.96.1" + "eslint": "^9.39.1", + "eslint-config-next": "16.0.6", + "playwright": "^1.57.0", + "typescript": ">=5.9.2", + "webpack": "^5.101.0" } } diff --git a/public/posts/dask-detrending/indexing-data-selection.png b/public/posts/dask-detrending/indexing-data-selection.png index eb6e716f8..dba64ba04 100644 Binary files a/public/posts/dask-detrending/indexing-data-selection.png and b/public/posts/dask-detrending/indexing-data-selection.png differ diff --git a/public/posts/dask-detrending/input-array.png b/public/posts/dask-detrending/input-array.png index d5db73f40..985ac6b3c 100644 Binary files a/public/posts/dask-detrending/input-array.png and b/public/posts/dask-detrending/input-array.png differ diff --git a/public/posts/dask-detrending/output-array-new.png b/public/posts/dask-detrending/output-array-new.png index a3a95da17..2de2a180b 100644 Binary files a/public/posts/dask-detrending/output-array-new.png and b/public/posts/dask-detrending/output-array-new.png differ diff --git a/public/posts/dask-detrending/output-array-old.png b/public/posts/dask-detrending/output-array-old.png index f7efec383..0fa0057b6 100644 Binary files a/public/posts/dask-detrending/output-array-old.png and b/public/posts/dask-detrending/output-array-old.png differ diff --git a/public/posts/flexible-indexing/da-pandas-repr.html b/public/posts/flexible-indexing/da-pandas-repr.html new file mode 100644 index 000000000..6ef906031 --- /dev/null +++ b/public/posts/flexible-indexing/da-pandas-repr.html @@ -0,0 +1,447 @@ +
+ + + + + + + + + + + + + + +
<xarray.DataArray 'M' (X: 6)> Size: 48B
+array([10., 20., 30., 40., 50., 60.])
+Coordinates:
+  * X        (X) int64 48B 1 2 4 8 16 32
\ No newline at end of file diff --git a/public/posts/flexible-indexing/da-rasterix-repr.html b/public/posts/flexible-indexing/da-rasterix-repr.html new file mode 100644 index 000000000..232306f65 --- /dev/null +++ b/public/posts/flexible-indexing/da-rasterix-repr.html @@ -0,0 +1,457 @@ +
+ + + + + + + + + + + + + + +
<xarray.DataArray 'band_data' (y: 626401, x: 1296001)> Size: 3TB
+[811816322401 values with dtype=float32]
+Coordinates:
+    band         int64 8B 1
+    spatial_ref  int64 8B ...
+  * x            (x) float64 10MB -180.0 -180.0 -180.0 ... 180.0 180.0 180.0
+  * y            (y) float64 5MB 84.0 84.0 84.0 84.0 ... -90.0 -90.0 -90.0 -90.0
+Indexes:
+  ┌ x        RasterIndex (crs=None)
+  └ y
+Attributes:
+    AREA_OR_POINT:  Point
\ No newline at end of file diff --git a/public/posts/flexible-indexing/ds-range-repr.html b/public/posts/flexible-indexing/ds-range-repr.html new file mode 100644 index 000000000..aec8e205e --- /dev/null +++ b/public/posts/flexible-indexing/ds-range-repr.html @@ -0,0 +1,451 @@ +
+ + + + + + + + + + + + + + +
<xarray.Dataset> Size: 8MB
+Dimensions:  (x: 1000000)
+Coordinates:
+  * x        (x) float64 8MB 0.0 0.1 0.2 0.3 0.4 ... 1e+05 1e+05 1e+05 1e+05
+Data variables:
+    *empty*
+Indexes:
+    x        RangeIndex (start=0, stop=1e+05, step=0.1)
\ No newline at end of file diff --git a/public/posts/flexible-indexing/ds-range-slice-repr.html b/public/posts/flexible-indexing/ds-range-slice-repr.html new file mode 100644 index 000000000..af6ff6d72 --- /dev/null +++ b/public/posts/flexible-indexing/ds-range-slice-repr.html @@ -0,0 +1,449 @@ +
+ + + + + + + + + + + + + + +
<xarray.DataArray 'x' (x: 490)> Size: 4kB
+[490 values with dtype=float64]
+Coordinates:
+  * x        (x) float64 4kB 1e-06 1.1e-06 1.2e-06 ... 4.98e-05 4.99e-05
+Indexes:
+    x        RangeIndex (start=1e-06, stop=5e-05, step=1e-07)
diff --git a/public/posts/flexible-indexing/summary-slide.png b/public/posts/flexible-indexing/summary-slide.png new file mode 100644 index 000000000..b85c081d4 Binary files /dev/null and b/public/posts/flexible-indexing/summary-slide.png differ diff --git a/public/posts/flexible-indexing/xvec-repr.html b/public/posts/flexible-indexing/xvec-repr.html new file mode 100644 index 000000000..355b5d734 --- /dev/null +++ b/public/posts/flexible-indexing/xvec-repr.html @@ -0,0 +1,498 @@ +
+ + + + + + + + + + + + + + +
<xarray.Dataset> Size: 173kB
+Dimensions:       (county: 3085, year: 4)
+Coordinates:
+  * county        (county) geometry 25kB POLYGON ((-95.34258270263672 48.5467...
+  * year          (year) int64 32B 1960 1970 1980 1990
+Data variables:
+    population    (county, year) int32 49kB 4304 3987 3764 ... 43766 55800 65077
+    unemployment  (county, year) float64 99kB 7.9 9.0 5.903 ... 7.018 5.489
+Indexes:
+    county   GeometryIndex (crs=EPSG:4326)
\ No newline at end of file diff --git a/public/posts/flexible-indexing/xvecfig.png b/public/posts/flexible-indexing/xvecfig.png new file mode 100644 index 000000000..c12651f6d Binary files /dev/null and b/public/posts/flexible-indexing/xvecfig.png differ diff --git a/public/posts/gpu-pipline/all_in_all_improvement.png b/public/posts/gpu-pipline/all_in_all_improvement.png new file mode 100644 index 000000000..a9c38c25a Binary files /dev/null and b/public/posts/gpu-pipline/all_in_all_improvement.png differ diff --git a/public/posts/gpu-pipline/baseline.png b/public/posts/gpu-pipline/baseline.png new file mode 100644 index 000000000..24f9f2281 Binary files /dev/null and b/public/posts/gpu-pipline/baseline.png differ diff --git a/public/posts/gpu-pipline/flowchart_2.png b/public/posts/gpu-pipline/flowchart_2.png new file mode 100644 index 000000000..f06acac77 Binary files /dev/null and b/public/posts/gpu-pipline/flowchart_2.png differ diff --git a/public/posts/gpu-pipline/flowchart_3.png b/public/posts/gpu-pipline/flowchart_3.png new file mode 100644 index 000000000..e882ed937 Binary files /dev/null and b/public/posts/gpu-pipline/flowchart_3.png differ diff --git a/public/posts/gpu-pipline/profiling_screenshot1.png b/public/posts/gpu-pipline/profiling_screenshot1.png new file mode 100644 index 000000000..f6456dc5f Binary files /dev/null and b/public/posts/gpu-pipline/profiling_screenshot1.png differ diff --git a/public/posts/gpu-pipline/profiling_screenshot2.png b/public/posts/gpu-pipline/profiling_screenshot2.png new file mode 100644 index 000000000..4fec34829 Binary files /dev/null and b/public/posts/gpu-pipline/profiling_screenshot2.png differ diff --git a/public/posts/gpu-pipline/profiling_screenshot_dali.png b/public/posts/gpu-pipline/profiling_screenshot_dali.png new file mode 100644 index 000000000..d242019c7 Binary files /dev/null and b/public/posts/gpu-pipline/profiling_screenshot_dali.png differ diff --git a/public/posts/gpu-pipline/scaling_chunking_performance_plot.png b/public/posts/gpu-pipline/scaling_chunking_performance_plot.png new file mode 100644 index 000000000..28971c445 Binary files /dev/null and b/public/posts/gpu-pipline/scaling_chunking_performance_plot.png differ diff --git a/public/posts/gpu-pipline/zstd_benchmark.png b/public/posts/gpu-pipline/zstd_benchmark.png new file mode 100644 index 000000000..ce5aaf38b Binary files /dev/null and b/public/posts/gpu-pipline/zstd_benchmark.png differ diff --git a/public/posts/introducing-pint-xarray/squared_wind.png b/public/posts/introducing-pint-xarray/squared_wind.png index 7628b6218..69d198aac 100644 Binary files a/public/posts/introducing-pint-xarray/squared_wind.png and b/public/posts/introducing-pint-xarray/squared_wind.png differ diff --git a/public/posts/introducing-xradar/odim_polar_scan.png b/public/posts/introducing-xradar/odim_polar_scan.png index b04c96097..7a196a492 100644 Binary files a/public/posts/introducing-xradar/odim_polar_scan.png and b/public/posts/introducing-xradar/odim_polar_scan.png differ diff --git a/public/posts/season-grouping/four-month-seasons.png b/public/posts/season-grouping/four-month-seasons.png new file mode 100644 index 000000000..9cf89848f Binary files /dev/null and b/public/posts/season-grouping/four-month-seasons.png differ diff --git a/public/posts/tutorial/tutorial-before-after.png b/public/posts/tutorial/tutorial-before-after.png index 601f9d906..0d48644d6 100644 Binary files a/public/posts/tutorial/tutorial-before-after.png and b/public/posts/tutorial/tutorial-before-after.png differ diff --git a/public/posts/xarray-biology/dataarray-repr.html b/public/posts/xarray-biology/dataarray-repr.html new file mode 100644 index 000000000..cc6354776 --- /dev/null +++ b/public/posts/xarray-biology/dataarray-repr.html @@ -0,0 +1,524 @@ +
+ + + + + + + + + + + + + + +
<xarray.DataArray (FOV: 2, time: 4, channel: 3, Z: 5, Y: 512, X: 512)> Size: 63MB
+dask.array<array, shape=(2, 4, 3, 5, 512, 512), dtype=uint16, chunksize=(2, 4, 3, 5, 512, 512), chunktype=numpy.ndarray>
+Coordinates:
+    FOV_x    (FOV) int64 16B 0 301
+    FOV_y    (FOV) int64 16B 0 235
+  * X        (X) float64 4kB 0.0 0.1957 0.3914 0.5871 ... 99.41 99.61 99.8 100.0
+  * Y        (Y) float64 4kB 0.0 0.1957 0.3914 0.5871 ... 99.41 99.61 99.8 100.0
+  * Z        (Z) float64 40B -2.5 -1.25 0.0 1.25 2.5
+  * channel  (channel) <U5 60B 'BF' 'GFP' 'dsred'
+  * time     (time) timedelta64[ns] 32B 00:00:00 00:15:00 00:31:00 00:35:00
+Dimensions without coordinates: FOV
+Attributes:
+    exposure:       {'BF': 10, 'GFP': 50, 'dsred': 100}
+    Date Acquired:  2025-04-26
\ No newline at end of file diff --git a/public/posts/xarray-biology/dataset-repr.html b/public/posts/xarray-biology/dataset-repr.html new file mode 100644 index 000000000..6921d9f7a --- /dev/null +++ b/public/posts/xarray-biology/dataset-repr.html @@ -0,0 +1,618 @@ +
+ + + + + + + + + + + + + + +
<xarray.Dataset> Size: 67MB
+Dimensions:      (FOV: 2, X: 512, Y: 512, Z: 5, channel: 3, time: 4)
+Coordinates:
+    FOV_x        (FOV) int64 16B 0 301
+    FOV_y        (FOV) int64 16B 0 235
+  * X            (X) float64 4kB 0.0 0.1957 0.3914 0.5871 ... 99.61 99.8 100.0
+  * Y            (Y) float64 4kB 0.0 0.1957 0.3914 0.5871 ... 99.61 99.8 100.0
+  * Z            (Z) float64 40B -2.5 -1.25 0.0 1.25 2.5
+  * channel      (channel) <U5 60B 'BF' 'GFP' 'dsred'
+  * time         (time) timedelta64[ns] 32B 00:00:00 00:15:00 00:31:00 00:35:00
+Dimensions without coordinates: FOV
+Data variables:
+    images       (FOV, time, channel, Z, Y, X) uint16 63MB dask.array<chunksize=(2, 4, 3, 5, 512, 512), meta=np.ndarray>
+    cell labels  (FOV, time, Y, X) uint16 4MB dask.array<chunksize=(2, 4, 512, 512), meta=np.ndarray>
\ No newline at end of file diff --git a/public/posts/xarray-biology/datatree-repr.html b/public/posts/xarray-biology/datatree-repr.html new file mode 100644 index 000000000..62ed80285 --- /dev/null +++ b/public/posts/xarray-biology/datatree-repr.html @@ -0,0 +1,1701 @@ +
+ + + + + + + + + + + + + + +
<xarray.DatasetView> Size: 0B
+Dimensions:  ()
+Data variables:
+    *empty*
\ No newline at end of file diff --git a/public/posts/xarray-for-neurophysiology/image2.png b/public/posts/xarray-for-neurophysiology/image2.png index f64192d4f..9200dc830 100644 Binary files a/public/posts/xarray-for-neurophysiology/image2.png and b/public/posts/xarray-for-neurophysiology/image2.png differ diff --git a/public/posts/xarray-for-neurophysiology/image3.png b/public/posts/xarray-for-neurophysiology/image3.png index cb18ebe0d..6e21fc6e0 100644 Binary files a/public/posts/xarray-for-neurophysiology/image3.png and b/public/posts/xarray-for-neurophysiology/image3.png differ diff --git a/public/posts/xarray-for-neurophysiology/image4.png b/public/posts/xarray-for-neurophysiology/image4.png index 727230630..1856318a7 100644 Binary files a/public/posts/xarray-for-neurophysiology/image4.png and b/public/posts/xarray-for-neurophysiology/image4.png differ diff --git a/public/posts/xarray-for-neurophysiology/image5.png b/public/posts/xarray-for-neurophysiology/image5.png index 356f24810..4f714ae9b 100644 Binary files a/public/posts/xarray-for-neurophysiology/image5.png and b/public/posts/xarray-for-neurophysiology/image5.png differ diff --git a/public/posts/xarray-napari-plan/dataarray-repr.html b/public/posts/xarray-napari-plan/dataarray-repr.html new file mode 100644 index 000000000..cc6354776 --- /dev/null +++ b/public/posts/xarray-napari-plan/dataarray-repr.html @@ -0,0 +1,524 @@ +
+ + + + + + + + + + + + + + +
<xarray.DataArray (FOV: 2, time: 4, channel: 3, Z: 5, Y: 512, X: 512)> Size: 63MB
+dask.array<array, shape=(2, 4, 3, 5, 512, 512), dtype=uint16, chunksize=(2, 4, 3, 5, 512, 512), chunktype=numpy.ndarray>
+Coordinates:
+    FOV_x    (FOV) int64 16B 0 301
+    FOV_y    (FOV) int64 16B 0 235
+  * X        (X) float64 4kB 0.0 0.1957 0.3914 0.5871 ... 99.41 99.61 99.8 100.0
+  * Y        (Y) float64 4kB 0.0 0.1957 0.3914 0.5871 ... 99.41 99.61 99.8 100.0
+  * Z        (Z) float64 40B -2.5 -1.25 0.0 1.25 2.5
+  * channel  (channel) <U5 60B 'BF' 'GFP' 'dsred'
+  * time     (time) timedelta64[ns] 32B 00:00:00 00:15:00 00:31:00 00:35:00
+Dimensions without coordinates: FOV
+Attributes:
+    exposure:       {'BF': 10, 'GFP': 50, 'dsred': 100}
+    Date Acquired:  2025-04-26
\ No newline at end of file diff --git a/public/posts/xarray-napari-plan/scipy-napari-xarray-sprint.png b/public/posts/xarray-napari-plan/scipy-napari-xarray-sprint.png new file mode 100644 index 000000000..db94d0b85 Binary files /dev/null and b/public/posts/xarray-napari-plan/scipy-napari-xarray-sprint.png differ diff --git a/public/projects/xmip.png b/public/projects/xmip.png index 5d4c47441..81541eac9 100644 Binary files a/public/projects/xmip.png and b/public/projects/xmip.png differ diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 4cd3aeaab..000000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/components/array-libraries.js b/src/components/array-libraries.js index 7451543b8..b11d60cd4 100644 --- a/src/components/array-libraries.js +++ b/src/components/array-libraries.js @@ -3,9 +3,10 @@ import React from 'react' import { IoIosGlobe, IoLogoGithub } from 'react-icons/io' import { Image } from '@/components/mdx' -import { Libraries as data } from '@/data/array-libraries' +import { getLibraries } from '@/data/array-libraries' import { SocialLink } from '@/components/social-link' +import { useLingui } from '@lingui/react/macro' const Library = ({ name, description, repo, url, logo }) => { return ( @@ -41,12 +42,12 @@ const Library = ({ name, description, repo, url, logo }) => { } export const ArrayLibraries = () => { - const libraries = React.useMemo(() => data, []) + const { t } = useLingui() + const libraries = getLibraries() return ( - Xarray supports multiple array backends, allowing users to choose array - types that work best for their application. + {t`Xarray supports multiple array backends, allowing users to choose array types that work best for their application.`} { } = useDisclosure({ defaultIsOpen: true }) return isVisible ? ( - - - - - {title} - {description} - {children} - - - + + {' '} + {/* This Box provides top padding to clear the header */} + + {' '} + {/* Constrains the width of the banner */} + + + + {' '} + {/* Container for text content, allows vertical flow */} + {title} + {description && ( + {description} + )} + + {' '} + {/* Wrapper for children, e.g., links */} + {children} + + + + + ) : ( <> diff --git a/src/components/dashboard/issue-tracker.js b/src/components/dashboard/issue-tracker.js index c0eb49067..5f84c3bdf 100644 --- a/src/components/dashboard/issue-tracker.js +++ b/src/components/dashboard/issue-tracker.js @@ -4,25 +4,27 @@ import { GiDuration } from 'react-icons/gi' import { TimelinePlotContainer } from '@/components/dashboard/timeline-plot-container' import { TimeseriesAggStatsCard } from '@/components/dashboard/timeseries-agg-stats-card' import { Heading } from '@/components/mdx' +import { useLingui } from '@lingui/react/macro' export const IssueTracker = () => { + const { t } = useLingui() return ( - Xarray Issue Tracker + {t`Xarray Issue Tracker`} {' '} } /> } query={ 'https://xarray-datasette.fly.dev/github.json?_shape=array&&sql=select%0D%0A++id%2C%0D%0A++number%2C%0D%0A++state%2C%0D%0A++created_at%2C%0D%0A++closed_at%2C%0D%0A++julianday%28closed_at%29+-+julianday%28created_at%29+as+age_in_days%0D%0Afrom%0D%0A++issues+as+data%0D%0Awhere%0D%0A++type+%3D+%27issue%27%0D%0A++and+state+%3D+%27closed%27%0D%0Aorder+by%0D%0A++id' diff --git a/src/components/dashboard/project-metrics.js b/src/components/dashboard/project-metrics.js index c1ba57ced..b87123f86 100644 --- a/src/components/dashboard/project-metrics.js +++ b/src/components/dashboard/project-metrics.js @@ -5,6 +5,7 @@ import { fetcher } from '@/lib/data-fetching' import { Box, Container, SimpleGrid, Spinner } from '@chakra-ui/react' import { BsPeople, BsPerson } from 'react-icons/bs' import { GoBook, GoPackage, GoStar, GoTag } from 'react-icons/go' +import { useLingui } from '@lingui/react/macro' import useSWR from 'swr' export const ProjectMetrics = () => { @@ -12,8 +13,8 @@ export const ProjectMetrics = () => { 'https://raw.githubusercontent.com/andersy005/xarray-datasette/a73704d803350a2ec059bec1b4cce601cd9efdd9/data/docs-monthly-views.json', fetcher, ) - - if (error) return
failed to load data
+ const { t } = useLingui() + if (error) return
{t`failed to load data`}
if (!data) return ( { {' '} - Xarray Project Metrics + {t`Xarray Project Metrics`} } link={'https://docs.xarray.dev/en/stable/team.html'} /> { /> } query={ 'https://xarray-datasette.fly.dev/github/_analyze_tables_/stars,user.json?_shape=array' @@ -64,20 +65,20 @@ export const ProjectMetrics = () => { /> } link={'https://github.com/pydata/xarray/network/dependents'} /> } /> { 'https://pydata-datasette.fly.dev/open_pulls_and_issues.json?_shape=array&&sql=select%0D%0A++time%2C%0D%0A++open_issues%2C%0D%0A++open_pull_requests%0D%0Afrom%0D%0A++open_pulls_and_issues%0D%0Awhere%0D%0A++project+%3D+%27pydata%2Fxarray%27%0D%0Aorder+by%0D%0A++time', fetcher, ) - - if (error) return
failed to load data
+ const { t } = useLingui() + if (error) return
{t`failed to load data`}
if (!data) return ( { return ( - This is a timeline of how many open issues and pull requests Xarray has - on Github over time from {new Date(start).toLocaleDateString()} to{' '} - {new Date(end).toLocaleDateString()}. + {t`This is a timeline of how many open issues and pull requests Xarray has on Github over time from ${new Date(start).toLocaleDateString()} to ${new Date(end).toLocaleDateString()}.`}

- Pull Requests + {t`Pull Requests`} - Issues + {t`Issues`} diff --git a/src/components/dashboard/timeseries-agg-stats-card.js b/src/components/dashboard/timeseries-agg-stats-card.js index e87a78e6b..3e48aa226 100644 --- a/src/components/dashboard/timeseries-agg-stats-card.js +++ b/src/components/dashboard/timeseries-agg-stats-card.js @@ -4,10 +4,12 @@ import { Spinner, Text } from '@chakra-ui/react' import * as d3 from 'd3' import { isWithinInterval, lastDayOfMonth, startOfMonth } from 'date-fns' import useSWR from 'swr' +import { useLingui } from '@lingui/react/macro' export const TimeseriesAggStatsCard = ({ query, title, icon }) => { + const { t } = useLingui() let { data, error } = useSWR(query, fetcher) - if (error) return failed to load + if (error) return {t`failed to load`} if (!data) return ( { const change = { type: diffPercentage < 0 ? 'increase' : 'decrease', - value: `${d3.format('.2f')(Math.abs(diffPercentage))}% since last month`, + value: t`${d3.format('.2f')(Math.abs(diffPercentage))}% since last month`, } return ( { icon={icon} stat={ result <= 2 - ? `${d3.format('.1f')(result * 24)} hours` - : `${d3.format('.1f')(result)} days` + ? t`${d3.format('.1f')(result * 24)} hours` + : t`${d3.format('.1f')(result)} days` } diff={change} /> diff --git a/src/components/donate.js b/src/components/donate.js index 15b6ea116..3875e58ef 100644 --- a/src/components/donate.js +++ b/src/components/donate.js @@ -10,13 +10,15 @@ import { import { Heading, Image, Link } from '@/components/mdx' import { BiDonateHeart } from 'react-icons/bi' +import { useLingui } from '@lingui/react/macro' export const Donate = () => { + const { t } = useLingui() return ( - Donate + {t`Donate`} { position={'relative'} > - Xarray is a Sponsored Project of NumFOCUS, a{' '} + {t`Xarray is a NumFOCUS Sponsored Project, a `} - 501(c)(3) nonprofit charity + {t`501(c)(3) nonprofit charity`} {' '} - in the United States. NumFOCUS provides Xarray with fiscal, legal, + {t`in the United States. NumFOCUS provides Xarray with fiscal, legal, and administrative support to help ensure the health and - sustainability of the project. Visit{' '} + sustainability of the project. For more information, visit `} { > numfocus.org {' '} - for more information.

- If you like Xarray and want to support our mission, please - consider making a donation to support our efforts. + {t`If you like Xarray and want to support our mission, please consider making a donation to support our efforts.`}
diff --git a/src/components/ecosystem.js b/src/components/ecosystem.js index 7f91c8190..1e51cee89 100644 --- a/src/components/ecosystem.js +++ b/src/components/ecosystem.js @@ -16,18 +16,21 @@ import { Heading, Link } from '@/components/mdx' import { ScientificDomains } from '@/components/scientific-domains' import { IoLogoGithub } from 'react-icons/io5' +import { useLingui } from '@lingui/react/macro' + import useSWR from 'swr' const fetcher = (...args) => fetch(...args).then((res) => res.json()) const GitHubStats = () => { + const { t } = useLingui() const { data, error } = useSWR( 'https://xarray-datasette.fly.dev/github/_analyze_tables_/stars,user.json?_shape=array', fetcher, ) - if (error) return
failed to load
- if (!data) return
loading...
+ if (error) return
{t`failed to load`}
+ if (!data) return
{t`loading...`}
return ( @@ -42,23 +45,22 @@ const GitHubStats = () => { {data[0].total_rows.toLocaleString(undefined, { minimumFractionDigits: 0, })}{' '} - Stars + {t`Stars`} ) } export const Ecosystem = () => { + const { t } = useLingui() return ( - Ecosystem + {t`Ecosystem`} - Xarray is part of the larger scientific Python ecosystem. It is built - on top of NumPy, Pandas, and Dask and supports a wide range of domain - specific scientific applications. + {t`Xarray is part of the larger scientific Python ecosystem. It is built on top of NumPy, Pandas, and Dask and supports a wide range of domain specific scientific applications.`} { > - Scientific Domains + {t`Scientific Domains`} - Array Libraries + {t`Array Libraries`} diff --git a/src/components/features.js b/src/components/features.js index ae5aaf65b..daa5acf40 100644 --- a/src/components/features.js +++ b/src/components/features.js @@ -10,22 +10,21 @@ import { import React from 'react' import { Heading } from '@/components/mdx' -import { Features as data } from '@/data/features' +import { getFeatures } from '@/data/features' import { CheckIcon } from '@chakra-ui/icons' +import { useLingui } from '@lingui/react/macro' export const Features = () => { - const features = React.useMemo(() => data, []) + const { t } = useLingui() + const features = getFeatures() return ( - Key Features & Capabilities + {t`Key Features & Capabilities`} - Xarray provides data models for working with labeled arrays and - datasets. Its toolkit includes a broad set of domain-agnostic - functions for advanced analytics and visualization with these data - structures. + {t`Xarray provides data models for working with labeled arrays and datasets. Its toolkit includes a broad set of domain-agnostic functions for advanced analytics and visualization with these data structures.`} diff --git a/src/components/footer.js b/src/components/footer.js index 5fb7f0be7..80585ce6e 100644 --- a/src/components/footer.js +++ b/src/components/footer.js @@ -14,9 +14,10 @@ import { getRootURL } from '@/lib/seo-utils' import { GitSHA } from '@/components/git-sha' import { Image, Link } from '@/components/mdx' -import { VercelCallout } from '@/components/vercel' -import { footerItems } from '@/data/footer-items' +import { NetlifyCallout } from '@/components/netlify' +import { getFooterItems } from '@/data/footer-items' import { FaGithub, FaRss, FaTwitter, FaYoutube } from 'react-icons/fa' +import { useLingui } from '@lingui/react/macro' const SocialButton = ({ children, label, href }) => { return ( @@ -51,6 +52,9 @@ const ListHeader = ({ children }) => { } export const Footer = () => { + let { t } = useLingui() + let footerItems = getFooterItems() + const currentYear = new Date().getFullYear() return ( { - © {new Date().getFullYear()}, Xarray core developers. Apache 2.0 - Licensed. + {t`© ${currentYear}, Xarray core developers. Apache 2.0 Licensed.`} @@ -123,7 +126,7 @@ export const Footer = () => { })} - Resources + {t`Resources`} {footerItems.resources.map((item) => { return ( { })} - Community + {t`Community`} {footerItems.community.map((item) => { return ( { - + diff --git a/src/components/git-sha.js b/src/components/git-sha.js index c905e3ffd..19cdd51ba 100644 --- a/src/components/git-sha.js +++ b/src/components/git-sha.js @@ -2,9 +2,17 @@ import { Link } from '@/components/mdx' import { Flex, Text } from '@chakra-ui/react' import { IoGitBranchOutline } from 'react-icons/io5' -const sha = process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA || '' -const owner = process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER || '' -const slug = process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG || '' +const sha = process.env.HEAD || '' +const repositoryUrl = process.env.REPOSITORY_URL || '' + +// Extract owner and repo from REPOSITORY_URL (format: https://github.com/owner/repo) +const getRepoInfo = (url) => { + if (!url) return { owner: '', slug: '' } + const match = url.match(/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/) + return match ? { owner: match[1], slug: match[2] } : { owner: '', slug: '' } +} + +const { owner, slug } = getRepoInfo(repositoryUrl) export const GitSHA = () => { if (!sha || !owner || !slug) { diff --git a/src/components/header.js b/src/components/header.js index 37ebfab46..bbbc36207 100644 --- a/src/components/header.js +++ b/src/components/header.js @@ -1,7 +1,7 @@ import { DesktopNav } from '@/components/desktop-nav' import { Link } from '@/components/mdx' import { MobileNav } from '@/components/mobile-nav' -import { menuItems } from '@/data/menu-items' +import { getMenuItems } from '@/data/menu-items' import { CloseIcon, HamburgerIcon } from '@chakra-ui/icons' import { Box, @@ -15,12 +15,16 @@ import { useDisclosure, } from '@chakra-ui/react' import React from 'react' +import { useRouter } from 'next/router' -export const Header = () => { - const navItems = React.useMemo(() => menuItems, []) +import { LanguageSwitcher } from './language-switcher' +export const Header = () => { + let navItems = getMenuItems() const { isOpen, onToggle } = useDisclosure() const { colorMode, toggleColorMode } = useColorMode() + const router = useRouter() + const isBlogPage = router.asPath.startsWith('/blog') return ( @@ -41,7 +45,7 @@ export const Header = () => { ), }} > - + { navItems={navItems} display={{ base: 'none', md: 'flex' }} /> + + {!isBlogPage && } - + ) } diff --git a/src/components/hero-banner.js b/src/components/hero-banner.js index 5c96b9c86..a4343fd6a 100644 --- a/src/components/hero-banner.js +++ b/src/components/hero-banner.js @@ -2,7 +2,10 @@ import { Box, Button, Container, Heading, Stack, Text } from '@chakra-ui/react' import { Image, Link } from '@/components/mdx' +import { useLingui } from '@lingui/react/macro' + export const HeroBanner = () => { + const { t } = useLingui() return ( @@ -35,20 +38,14 @@ export const HeroBanner = () => {
- N-D labeled arrays and datasets in Python + {t`N-D labeled arrays and datasets in Python`} - Xarray is an open source project and Python - package that introduces labels in the form of dimensions, - coordinates, and attributes on top of raw NumPy-like arrays, which - allows for more intuitive, more concise, and less error-prone user - experience. + {t`Xarray is an open source project and Python package that introduces labels in the form of dimensions, coordinates, and attributes on top of raw NumPy-like arrays, which allows for more intuitive, more concise, and less error-prone user experience.`}

- Xarray includes a large and growing library of domain-agnostic - functions for advanced analytics and visualization with these data - structures. + {t`Xarray includes a large and growing library of domain-agnostic functions for advanced analytics and visualization with these data structures.`}
@@ -70,7 +67,7 @@ export const HeroBanner = () => { colorScheme={'blue'} href='https://docs.xarray.dev/en/stable/getting-started-guide/quick-overview.html' > - Get Started + {t`Get Started`} diff --git a/src/components/language-switcher.js b/src/components/language-switcher.js new file mode 100644 index 000000000..cd70bf289 --- /dev/null +++ b/src/components/language-switcher.js @@ -0,0 +1,73 @@ +'use client' +import { useLingui } from '@lingui/react/macro' +import { i18n } from '@lingui/core' +import { useRouter, usePathname } from 'next/navigation' +import { useEffect } from 'react' +import { + Menu, + MenuButton, + MenuList, + MenuItem, + Link, + Button, +} from '@chakra-ui/react' + +function getLocales() { + const { t } = useLingui() + return [ + { locale: 'en', label: t`English` }, + { locale: 'es', label: t`Spanish` }, + { locale: 'pt', label: t`Portuguese` }, + ] +} + +function getLocaleLabel(locale) { + const locales = getLocales() + const localeObject = locales.find((object) => object.locale === locale) + return localeObject ? localeObject.label : locale +} + +export const LanguageSwitcher = () => { + const router = useRouter() + const pathname = usePathname() + + async function changeLocale() { + const localeString = i18n.locale + localStorage.setItem('locale', localeString) + const catalog = await import(`../locales/${localeString}/messages.js`) + i18n.load(localeString, catalog.messages) + i18n.activate(localeString) + } + + useEffect(() => { + const storedLocale = localStorage.getItem('locale') + if (storedLocale && storedLocale !== router.locale) { + changeLocale() + } + }, []) + return ( + + + {getLocaleLabel(i18n.locale)} + + + {getLocales().map((object) => ( + + + {object.label} + + + ))} + + + ) +} diff --git a/src/components/layout.js b/src/components/layout.js index aecd295d0..6f578773a 100644 --- a/src/components/layout.js +++ b/src/components/layout.js @@ -4,6 +4,8 @@ import { Header } from '@/components/header' import { Link } from '@/components/mdx' import { Box, Flex } from '@chakra-ui/react' import Head from 'next/head' +import { useLingui } from '@lingui/react/macro' +import { useRouter } from 'next/router' export const Layout = ({ title, @@ -13,18 +15,38 @@ export const Layout = ({ url = 'https://xarray.dev', enableBanner = false, }) => { - const bannerTitle = 'Check out the new blog post on DataTree!' - const bannerDescription = '' - const bannerChildren = ( - - Xarray x NASA: xarray.DataTree for hierarchical data structures + /** + * This macro hook is needed to get `t` which + * is bound to i18n from React.Context + */ + const { t } = useLingui() + const router = useRouter() + + const bannerTitle = t`Check out the latest blog post:` + // The first link will be the main description for the banner + const bannerDescription = ( + + {' '} + {/* Ensure it stands out a bit */} + Xarray ❤️ napari: A plan for seamless integration ) + // The second link will be passed as children, styled to be smaller + // const bannerChildren = ( + // + // {' '} + // {/* Add your second link here, smaller font */} + // SciPy 2025 Click here for info about an Xarray for Bio Sprint! + // + //) // Determine the base URL based on the environment - const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL - ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` - : 'http://localhost:3000' + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL + ? process.env.NEXT_PUBLIC_SITE_URL + : process.env.URL || 'http://localhost:3000' // Construct the full card URL const fullCardUrl = card.startsWith('http') ? card : `${baseUrl}${card}` @@ -47,7 +69,7 @@ export const Layout = ({ rel='icon' type='image/png' sizes='96x96' - href='/Xarray-assets/Icon/Xarray_Icon_final.svg' + href='/Xarray-assets/Icon/Xarray_Icon_Final.svg' /> @@ -64,12 +86,11 @@ export const Layout = ({
{enableBanner && ( - {bannerChildren} + {/* {bannerChildren} */} )} {children} -