Next.js 实用小技巧

i18n 国际化

参考项目: https://github.com/vercel/next.js/tree/canary/examples/with-i18n-rosetta

首先,是本地 i18n目录下存储翻译文件,可以使用 js/ts/json/yaml 等格式。

这里以最复杂的 yaml 为例。

// i18n/index.ts
import './index.d';
import zh from './zh.yml';
import en from './en.yml';

export default {
  zh,
  en
};

添加声明文件。可以引入,或者直接加入到 tsconfig 中(为了避免意外,就没有这么做)。

// i18n/index.d.ts
declare module '*';

添加 js-yaml-loadernext.config.js 中:

module.exports = {
  // 配置语言
  i18n: {
    locales: ['en', 'zh'],
    defaultLocale: 'zh',
    localeDetection: true
  },
  webpack: (config, { dev, isServer }) => {
    // 注入 js-yaml-loader
    config.module.rules.push({
      test: /\.ya?ml$/,
      use: 'js-yaml-loader'
    });

    return config;
  }
};

I18nContext

// lib/i18n.tsx
import { useRouter } from 'next/router';
import { createContext, useState, useRef, ReactNode } from 'react';
import rosetta from 'rosetta';
import localeMessages from '../../i18n';

interface iI18nContext {
  activeLocale: Languages;
  // eslint-disable-next-line no-unused-vars
  t: (key: string, ...args: any[]) => string;
}

type Languages = keyof typeof localeMessages;

const i18n = rosetta();
i18n.locale('en');
export const I18nContext = createContext<iI18nContext>({} as iI18nContext);

export default function I18n({ children }: { children: ReactNode }) {
  const { locale, defaultLocale } = useRouter();
  const activeLocaleRef = useRef<Languages>((locale as Languages) || (defaultLocale as Languages) || 'en');
  const [, setTick] = useState(0);
  const firstRender = useRef(true);

  const i18nWrapper = {
    activeLocale: activeLocaleRef.current,
    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
    t: (key: string, ...args: any[]) => i18n.t(key, ...args),
    locale: (l: Languages) => {
      i18n.locale(l);

      i18n.set(l, localeMessages[l]);
      // force rerender to update view
      setTick((tick) => tick + 1);
    }
  };

  // for initial SSR render
  if (firstRender.current === true) {
    firstRender.current = false;
    i18nWrapper.locale(activeLocaleRef.current);
  }

  return <I18nContext.Provider value={i18nWrapper}>{children}</I18nContext.Provider>;
}

UseI18n Hook

// hooks/use-i18n.ts
import { useContext } from 'react';
import { I18nContext } from '../lib/i18n';

export default function useI18n() {
  const i18n = useContext(I18nContext);
  return i18n;
}

_app 改造

注入 I18nContext:

import '../styles/globals.css';
import type { AppProps } from 'next/app';
import Layout from '../components/layout';
import I18n from '../lib/i18n';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Layout>
      <I18n>
        <Component {...pageProps} />
      </I18n>
    </Layout>
  );
}

export default MyApp;

到这里,就基本完成了。参考本示例项目: https://github.com/willin/nextjs-blog-poc/tree/7e8943897de8665c1b02ee1ad369ea8102fe3bfe

生成站点 Sitemap

需要安装 globby

yarn add --dev globby

创建一个脚本,我的文件名为: scripts/generate-sitemap.mjs

import { writeFileSync } from 'fs';
import { globby } from 'globby';
import prettier from 'prettier';

const DOMAIN = process.env.DOMAIN || 'willin.wang';

async function generate() {
  const prettierConfig = await prettier.resolveConfig('./.prettierrc.js');
  const pages = await globby([
    'src/pages/*.tsx',
    'data/**/*.mdx',
    '!data/*.mdx',
    '!src/pages/_*.tsx',
    '!src/pages/api',
    '!src/pages/404.tsx'
  ]);

  const sitemap = `
    
    
        ${pages
          .map((page) => {
            const path = page.replace('src/pages', '').replace('data', '').replace('.tsx', '').replace('.mdx', '');
            const route = path === '/index' ? '' : path;
            return `
              
                  ${`https://${DOMAIN}${route}`}
              
            `;
          })
          .join('')}
    
    `;

  const formatted = prettier.format(sitemap, {
    ...prettierConfig,
    parser: 'html'
  });

  // eslint-disable-next-line no-sync
  writeFileSync('public/sitemap.xml', formatted);
}

generate();

注意一下对应的目录。 我的源码是放置于 src/pages 如果不是,就批量替换回 pages,如果不是用 Typescript,可以改文件后缀为 .jsx,可以使用正则。还有文件,我是放在 data目录下,均为 .mdx文件。

生成 RSS Feed

需要安装 rss

yarn add --dev rss

获取全部文章,然后生成 RSS Feed:

import { writeFileSync } from 'fs';
import RSS from 'rss';
import { allBlogs } from '.contentlayer/data';

const DOMAIN = process.env.DOMAIN || 'willin.wang';

async function generate() {
  const feed = new RSS({
    title: 'Lee Robinson',
    site_url: `https://${DOMAIN}`,
    feed_url: `https://${DOMAIN}/feed.xml`
  });

  allBlogs.map((post) => {
    feed.item({
      title: post.title,
      url: `https://${DOMAIN}/blog/${post.slug}`,
      date: post.publishedAt,
      description: post.summary
    });
  });

  writeFileSync('./public/feed.xml', feed.xml({ indent: true }));
}

generate();

其中,这里的 allBlogs 来自于 contentlayer。也可以是其他 data source。

你可能感兴趣的:(不可思议的前端,javascript,typescript,前端)