npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@volvo-cars/react-shared-translations

v2.2.1

Published

React translation library that provides localized translation for packages

Downloads

6,176

Readme

React Shared Translations

Questions? Ask in Slack #vcc-ui

@volvo-cars/react-shared-translations

This package is used as a wrapper for the @volvo-cars packages with i18n support. It serves translations for the locales included with the packages, mostly short UI labels required for accessibility reasons. It automatically serves translations with minimal effect on bundle size and performance. It supports both server-side and client-side rendering.

Installation

💡 This package includes Typescript definitions

Getting started

Wrap your React application with a TranslatorProvider and feed it the locale and translator props. On the server-side, the translator will get the translations synchronously from JSON files included with npm packages. On the client-side, translator will use a cache created by the server-side translator if it exists. If it doesn't, translations will be fetched in runtime.

Next.js Example

A complete example of implementing package translations in your Next.js app can be found here.

_document.tsx

import React from 'react';
import NextDocument, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
} from 'next/document';
import {
  Translator,
  TranslatorCache,
} from '@volvo-cars/react-shared-translations';

class Document extends NextDocument<{ translator: Translator }> {
  static async getInitialProps(ctx: DocumentContext) {
    const { renderPage } = ctx;

    // Create a server translator which will hold the cache in translator.cache when the
    // app finishes rendering
    const translator = new Translator();
    const originalRenderPage = renderPage;
    // Modify renderPage and pass the translator to _app.tsx to be then passsed to the provider
    ctx.renderPage = () =>
      originalRenderPage({
        enhanceApp: (App: any) =>
          function EnhancedApp(props) {
            //                     ↓↓↓ pass the translator as a prop to _app.tsx
            return <App {...props} translator={translator} />;
          },
      });
    const initialProps = await NextDocument.getInitialProps(ctx);
    return {
      ...initialProps,
      // pass the translator as prop to Document to be used down below in render
      translator,
    };
  }

  render() {
    const { translator } = this.props;
    return (
      <Html>
        <Head />
        <body>
          {/*
          Pass the translator to TranslatorCache to render the
          Translator.cache to the HTML state
          */}
          <TranslatorCache translator={translator} />
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default Document;

_app.tsx

import React from 'react';
import { NextComponentType } from 'next';
import Providers from 'src/Providers';
import { AppProps } from 'next/app';
import {
  TranslatorProvider,
  Translator,
} from '@volvo-cars/react-shared-translations';

// Create a browser translator to be used client-side
const browserTranslator = new Translator();

function App({
  Component,
  pageProps,
  translator,
}: {
  Component: NextComponentType;
  pageProps: AppProps['pageProps'];
  translator?: Translator;
}) {
  return (
    // Pass the translator and locale to TranslatorProvider
    // translator is only defined server-side, so we add
    // browserTranslator for the browser
    <TranslatorProvider
      locale="sv-SE"
      translator={translator || browserTranslator}
    >
      <Component {...pageProps} />
    </TranslatorProvider>
  );
}
export default App;

Express Example

server.tsx

import React from 'react';
import ReactDomServer from 'react-dom/server';
import express from 'express';
import App from './components/App.tsx';
import htmlTemplate from './build/index.html';
import {
  Translator,
  TranslatorCache,
} from '@volvo-cars/react-shared-translations';

const app = express();
const port = 3000;

app.get('/', (req, res) => {
  const translator = new Translator();
  const content = ReactDomServer.renderToString(
    <App locale="sv-SE" translator={translator} />
  );
  const html = html.replace(
    '<div id="root"></div>',
    `${ReactDomServer.renderToString(
      <TranslatorCache translator={translator} />
    )}
  <div id="root">${content}</div>`
  );
  res.send(html);
});

app.listen(port, () => {
  console.info(`Listening on port ${port}`);
});

client.tsx

import React from 'react';
import ReactDOM from 'react-dom'
import App from './components/App.tsx';
import { TranslatorProvider, Translator } from '@volvo-cars/react-shared-translations';

const browserTranslator = new Translator();
ReactDOM.hydrate(
<App locale="sv-SE" translator={browserTranslator}/>
,document.getElementById('app');
)

App.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.tsx';
import {
  TranslatorProvider,
  Translator,
} from '@volvo-cars/react-shared-translations';

const App: React.FC<{ translator: Translator; locale: string }> = ({
  translator,
  locale,
  children,
}) => {
  return (
    <TranslatorProvider locale={locale} translator={translator}>
      <div>My app</div>
    </TranslatorProvider>
  );
};

Done, you should be able to see all localized strings when using the supported packages

Testing

To avoid having to wait for translations to be asynchronasly import during testing, we provided a MockedProvider components that makes testing much easier. It takes an optional mocks prop of type Record<string, string>, where the key is the key used by the packages interally, ex: react-faqs.showMore. If mocks is not provided, the key will be returned instead.

import { MockedProvider } from '@volvo-cars/react-shared-translations/testing';
import { render } from '@testing-library/react';
import { Breadcrumbs } from '@volvo-cars/react-breadcrumbs';

test('renders aria-label key when mocks are not provided', () => {
  const { getByLabelText } = render(
    <MockedProvider>
      <Breadcrumbs
        trail={[{ title: 'title', href: 'www' }]}
        currentTitle="Current Title"
      />
    </MockedProvider>
  );
  const label = getByLabelText('react-breadcrumbs.ariaLabel');
  expect(label).toBeInTheDocument();
});

test('renders aria-label mocked value', () => {
  const { getByLabelText } = render(
    <MockedProvider mocks={{ 'react-breadcrumbs.ariaLabel': 'Breadcrumb' }}>
      <Breadcrumbs
        trail={[{ title: 'title', href: 'www' }]}
        currentTitle="Current Title"
      />
    </MockedProvider>
  );
  const label = getByLabelText('Breadcrumb');
  expect(label).toBeInTheDocument();
});

Package Developers

The following section is for developers that work on @volvo-cars packages and want to localize strings in their packages.

A convention needs to be followed in the packages that will serve translated strings and it goes as follows:

  • useTranslate hook must be called with packageName.itemKey Ex:

    const showMore = useTranslate('react-faqs.showMore');

    We now know that the required string exists in the @volvo-cars/react-faqs package in the showMore item

  • Translation files must be inside a i18n directory with ${locale}.json as file names inside the package that needs them, Ex:

    └── react-faqs/
    ├── i18n/
    │ └── en.json
    │ └── sv-SE.json
    │ └── da-DK.json
    │ └── en-US.json

useTranslate takes an optional second argument that decides if the translation should happen or not:

const showMoreText = useTranslate('react-faqs.showMore', { disabled: true });

The above will return undefined as long as disabled is true. This is to allow you to override the translation with something else like a prop provided by the consumers of your package.