Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8935e3f
Add i18n and 10n with lingui and babel
goanpeca May 1, 2025
75ac56e
Adapt strings for localization
goanpeca May 2, 2025
1c52623
Update language switcher
goanpeca May 2, 2025
b676777
Add local storage and fix language switcher
goanpeca May 3, 2025
e1d27cd
Add more strings, fix language swithcer routing and add more translat…
goanpeca May 4, 2025
774fd7e
Add additional strings
goanpeca May 4, 2025
eefd47c
remove uneeded router
goanpeca May 4, 2025
603c379
Add unique key to menuitems
goanpeca May 4, 2025
f551364
Remove files from version control
goanpeca May 7, 2025
612d162
Remoe other file and update gititnore and package.json
goanpeca May 7, 2025
345d147
Remove comment
goanpeca May 8, 2025
fc175ec
Fix translations in blog
melissawm Nov 19, 2025
fd76845
Try to resolve dependency issues in npm
melissawm Nov 22, 2025
4d411f7
Apply suggestions from Copilot code review
melissawm Nov 22, 2025
101fd2b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 22, 2025
61b5e6a
Remove unused Trans
melissawm Nov 22, 2025
64bb1da
Add information about translations to README.md
melissawm Jan 6, 2026
81a868b
Trying to fix blog display
melissawm Jan 6, 2026
8c33da3
Add link
melissawm Jan 6, 2026
b6a5aea
Update yarn.lock
melissawm Jan 6, 2026
f6a284e
Add compiled messages for babel
melissawm Jan 6, 2026
0a8121a
Apply code review suggestion
melissawm Feb 9, 2026
dc7ff64
Update dependencies
melissawm Feb 9, 2026
28ae5ce
Revert changes to yarn.lock
melissawm Feb 9, 2026
50d5606
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 9, 2026
5b37dac
Merge branch 'main' into add-i18n
melissawm Feb 9, 2026
f77da54
Resolve eslint log error
melissawm Feb 9, 2026
0fb8d27
Set default locale for blog posts
melissawm Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"presets": [
"next/babel"
],
"plugins": ["@lingui/babel-plugin-lingui-macro"]
}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,12 @@ yarn-error.log*

.yarn
.vscode/

# Lingui
src/locales/**/*.mo
src/locales/**/*.js

# RSS
atom.xml
rss.json
rss.xml
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ You can start editing the page by modifying `pages/index.js`. The page auto-upda
/>
</a>

## 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
Expand Down
17 changes: 17 additions & 0 deletions lingui.config.js
Original file line number Diff line number Diff line change
@@ -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/'],
},
],
}
17 changes: 17 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,21 @@ export default withMDX({
images: {
domains: ["raw.githubusercontent.com", "numpy.org", "dask.org", "chainer.org", ],
},
i18n: {
// These are all the locales you want to support in
// your application
locales: ["en", "es", "pt"],
defaultLocale: "en",
},
async rewrites() {
return {
beforeFiles: [
// Blog posts are not localized, so we always redirect back to the root blog path
{
source: '/:locale(es|pt)/blog/:id',
destination: '/blog/:id',
},
],
}
},
})
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@
"swr": "^2.2.5"
},
"devDependencies": {
"@lingui/babel-plugin-lingui-macro": "^5.9.0",
"@lingui/loader": "^5.9.0",
"@lingui/react": "^5.9.0",
"@types/react": "^18.3.11",
"eslint": "^9.39.1",
"eslint-config-next": "16.0.6",
"eslint-config-next": "^14.2.30",
"playwright": "^1.57.0",
"typescript": ">=5.9.2",
"webpack": "^5.101.0"
Expand Down
8 changes: 5 additions & 3 deletions src/components/array-libraries.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -41,12 +42,13 @@ const Library = ({ name, description, repo, url, logo }) => {
}

export const ArrayLibraries = () => {
const { t } = useLingui()
let data = getLibraries()
const libraries = React.useMemo(() => data, [])
return (
<Box my={8}>
<Text fontSize={'lg'}>
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.`}
</Text>

<SimpleGrid
Expand Down
8 changes: 5 additions & 3 deletions src/components/dashboard/issue-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Box as='section' id='issue-tracker'>
<Container maxW='container.lg'>
<Heading as='h2' size='xl' textAlign={'center'}>
Xarray Issue Tracker
{t`Xarray Issue Tracker`}
</Heading>
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={{ base: 5, lg: 8 }}>
{' '}
<TimeseriesAggStatsCard
title={'Median time a pull request is open'}
title={t`Median time a pull request is open`}
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+%27pull%27%0D%0A++and+state+%3D+%27closed%27%0D%0Aorder+by%0D%0A++id'
}
icon={<GiDuration size={'3em'} />}
/>
<TimeseriesAggStatsCard
title={'Median time an issue is open'}
title={t`Median time an issue is open`}
icon={<GiDuration size={'3em'} />}
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'
Expand Down
19 changes: 10 additions & 9 deletions src/components/dashboard/project-metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ 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 = () => {
const { data, error } = useSWR(
'https://raw.githubusercontent.com/andersy005/xarray-datasette/a73704d803350a2ec059bec1b4cce601cd9efdd9/data/docs-monthly-views.json',
fetcher,
)

if (error) return <div>failed to load data</div>
const { t } = useLingui()
if (error) return <div>{t`failed to load data`}</div>
if (!data)
return (
<Spinner
Expand All @@ -36,17 +37,17 @@ export const ProjectMetrics = () => {
<Container maxW='container.lg'>
{' '}
<Heading as='h2' size='xl' textAlign={'center'}>
Xarray Project Metrics
{t`Xarray Project Metrics`}
</Heading>
<SimpleGrid columns={{ base: 1, md: 3 }} spacing={{ base: 5, lg: 8 }}>
<StatisticsCard
title={'Core Maintainers'}
title={t`Core Maintainers`}
stat={'15'}
icon={<BsPerson size={'3em'} />}
link={'https://docs.xarray.dev/en/stable/team.html'}
/>
<DatasetteStatsCard
title={'Contributors'}
title={t`Contributors`}
query={
'https://xarray-datasette.fly.dev/github/_analyze_tables_/contributors,user_id.json?_shape=array'
}
Expand All @@ -55,7 +56,7 @@ export const ProjectMetrics = () => {
/>

<DatasetteStatsCard
title={'Stargazers'}
title={t`Stargazers`}
icon={<GoStar size={'3em'} />}
query={
'https://xarray-datasette.fly.dev/github/_analyze_tables_/stars,user.json?_shape=array'
Expand All @@ -64,20 +65,20 @@ export const ProjectMetrics = () => {
/>

<StatisticsCard
title={'Dependent Packages/Repos'}
title={t`Dependent Packages/Repos`}
stat={21275}
icon={<GoPackage size={'3em'} />}
link={'https://github.com/pydata/xarray/network/dependents'}
/>

<StatisticsCard
title={`${month}/${year} Docs Visitors`}
title={t`Docs Visitors` + ` - ${month}/${year}`}
stat={monthlyViews.users}
icon={<GoBook size={'3em'} />}
/>

<DatasetteStatsCard
title={'Releases'}
title={t`Releases`}
query={
'https://xarray-datasette.fly.dev/github/_analyze_tables_/releases,id.json?_shape=array'
}
Expand Down
10 changes: 5 additions & 5 deletions src/components/dashboard/timeline-plot-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Tabs,
Text,
} from '@chakra-ui/react'
import { useLingui } from '@lingui/react/macro'
import * as d3 from 'd3'
import useSWR from 'swr'

Expand All @@ -18,8 +19,8 @@ export const TimelinePlotContainer = () => {
'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 <div>failed to load data</div>
const { t } = useLingui()
if (error) return <div>{t`failed to load data`}</div>
if (!data)
return (
<Spinner
Expand All @@ -37,16 +38,15 @@ export const TimelinePlotContainer = () => {
return (
<Box my={8}>
<Text fontSize={'md'} align={'center'}>
This is a timeline of how many open issues and pull requests Xarray has
on Github over time from {new Date(start).toLocaleDateString()} to{' '}
{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()}.
</Text>
<br />
<br />
<Tabs align='center' variant='enclosed' isFitted colorScheme='teal'>
<TabList>
<Tab _selected={{ color: 'white', bg: 'teal.500' }}>
Pull Requests
{t`Pull Requests`}
</Tab>
<Tab _selected={{ color: 'white', bg: 'teal.500' }}>Issues</Tab>
</TabList>
Expand Down
11 changes: 7 additions & 4 deletions src/components/dashboard/timeseries-agg-stats-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Text>failed to load</Text>
if (error) return <Text>{t`failed to load`}</Text>
if (!data)
return (
<Spinner
Expand Down Expand Up @@ -43,16 +45,17 @@ export const TimeseriesAggStatsCard = ({ query, title, icon }) => {

const change = {
type: diffPercentage < 0 ? 'increase' : 'decrease',
value: `${d3.format('.2f')(Math.abs(diffPercentage))}% since last month`,
value:
`${d3.format('.2f')(Math.abs(diffPercentage))}% ` + t`since last month`,
}
return (
<StatisticsCard
title={title}
icon={icon}
stat={
result <= 2
? `${d3.format('.1f')(result * 24)} hours`
: `${d3.format('.1f')(result)} days`
? `${d3.format('.1f')(result * 24)} ` + t`hours`
: `${d3.format('.1f')(result)} ` + t`days`
}
diff={change}
/>
Expand Down
16 changes: 8 additions & 8 deletions src/components/donate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Box id={'donate'} as='section'>
<Container maxW='container.lg' centerContent>
<Heading as='h1' size='2xl'>
Donate
{t`Donate`}
</Heading>
<SimpleGrid
columns={{ base: 1, md: 2 }}
Expand All @@ -33,7 +35,7 @@ export const Donate = () => {
position={'relative'}
>
<Text fontSize={'lg'}>
Xarray is a Sponsored Project of NumFOCUS, a{' '}
{t`Xarray is a NumFOCUS Sponsored Project, a `}
<Text
as={Link}
href={'https://en.wikipedia.org/wiki/501(c)(3)_organization'}
Expand All @@ -42,9 +44,9 @@ export const Donate = () => {
>
501(c)(3) nonprofit charity
</Text>{' '}
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 `}
<Text
as={Link}
useExternalIcon
Expand All @@ -53,11 +55,9 @@ export const Donate = () => {
>
numfocus.org
</Text>{' '}
for more information.
<br />
<br />
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.`}
</Text>
<Button
as={Link}
Expand All @@ -68,7 +68,7 @@ export const Donate = () => {
rightIcon={<BiDonateHeart />}
href={'https://numfocus.org/donate-to-xarray'}
>
Donate
{t`Donate`}
</Button>
</Stack>

Expand Down
Loading