0.6.1 • Published 9 months ago

@shopify/react-i18n-swc-plugin v0.6.1

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

shopify_react_i18n_swc_plugin

Plugin for @shopify/react-i18n.

Usage

To load plugins, you may use swcrc's jsc.experimental configs:

{
  jsc: {
    ...
   experimental: {
     ...
     cache_root: String // optional, specify where swc will create wasm bytecode cache
    // list of plugins. If `pluginname` is resolvable to npm package, it'll try to load from installed npm packages.
    // or it can be an absolute path to the .wasm binary
     plugins: [ ['@shopify/react-i18n-swc-plugin', {cwd: <root_directory_here>}] ]
   }
}

This plugin will look for an adjacent translations folder containing, at minimum, an en.json file (the default locale). It will then iterate over each reference to the useI18n hook or withI18n decorator and, if the reference is a call expression with no arguments, and inject the appropriate arguments.

Note

Make sure you pass the root directory you would like the plugin to start working from as this plugin will not do anything without this.

Default Transform

Given folder structure

├── MyComponent.tsx
└── translations
   └── en.json
   └── fr.json
   └── jp.json

File content

// Within translations/en.js

{
  "heading": "All Films"
}

// Within translations/fr.js

{
  "heading": "fr_All Films"
}

// Within translations/jp.js

{
  "heading": "jp_All Films"
}
// Within MyComponent.tsx:

useI18n();

// Becomes:

import _en from "./translations/en.json";

useI18n({
  id: "MyComponent_<hash>",
  fallback: _en,
  async translations(locale) {
    if (["fr", "jp"].indexOf(locale) < 0) {
      return;
    }

    return import(
      /* webpackChunkName: "MyComponent_<hash>-i18n", webpackMode: "lazy-once" */ `./translations/${locale}.json`
    ).then((dict) => dict && dict.default);
  },
});

Options

mode

Type: with-dynamic-paths, with-explicit-paths, from-generated-index, from-dictionary-index

Default: with-dynamic-paths

with-explicit-paths

Use this mode with bundlers that require import paths to be explicit.

The babel plugin uses dynamic imports by default to resolve translation locations. Some bundlers however require that imports be explicity stated so that the bundler knows what files are to be included in the final generated bundle. Examples of bundlers with this behaviour include Metro and rollup.js. Configure the plugin to use explicit paths rather than dynamic imports with the with-explicit-paths options.

Usage
// webpack.config.js
module.exports = {
  resolve: {
    extensions: [".js"],
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        use: {
          loader: "babel-loader",
          options: {
            plugins: [
              "@shopify/react-i18n/babel",
              { mode: "with-explicit-paths" },
            ],
          },
        },
      },
    ],
  },
};

Transform

With two locale translation files (default being en):

// After auto-fill:

import _en from "./translations/en.json";

useI18n({
  id: "MyComponent_<hash>",
  fallback: _en,
  async translations(locale) {
    if (locale !== "jp") {
      return;
    }

    return import(
      /* webpackChunkName: "MyComponent_<hash>-i18n" */ "./translations/jp.json"
    ).then((dict) => dict && dict.default);
  },
});

With three or more locale translation files (default being en):

// After auto-fill:

import _en from "./translations/en.json";

useI18n({
  id: "MyComponent_<hash>",
  fallback: _en,
  async translations(locale) {
    const returnDefault = (dict) => dict && dict.default;

    switch (locale) {
      case "fr":
        return import(
          /* webpackChunkName: "MyComponent_<hash>-i18n" */ "./translations/fr.json"
        ).then(returnDefault);
      case "jp":
        return import(
          /* webpackChunkName: "MyComponent_<hash>-i18n" */ "./translations/jp.json"
        ).then(returnDefault);
    }
  },
});

from-generated-index

Use this mode to avoid caching issues when integrating with babel-loader.

This is swc-loader here, update this. Because babel-loader's cache is based on a component's source content hash, newly added translation files will not invalidate the component's Babel cache. To combat this, run the generateTranslationIndexes function before building, and configure the plugin to use its from-generated-index mode.

The generator will look for any translations folders and generate an array of local ids in translations/index.js based on the {locale}.json files found. We recommend that you add **/translations/index.js to .gitignore to make sure the generated files are not checked-in.

Usage
// webpack.config.js
module.exports = {
  resolve: {
    extensions: [".js", ".jsx"],
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              plugins: [
                "@babel/plugin-syntax-dynamic-import",
                ["@shopify/react-i18n/babel", { mode: "from-generated-index" }],
              ],
            },
          },
        ],
      },
    ],
  },
};
// generate-translations.js
const {
  generateTranslationIndexes,
} = require('@shopify/react-i18n/generate-index');

generateTranslationIndexes();
webpack(require(./webpack.config.js));
Transform
// Generated translations/index.js from generateTranslationIndexes()
export default ["fr", "jp"];
// Within MyComponent.tsx:

useI18n();

// Becomes:

import _en from "./translations/en.json";
import __shopify__i18n_translations from "./translations";

useI18n({
  id: "MyComponent_<hash>",
  fallback: _en,
  async translations(locale) {
    if (__shopify__i18n_translations.indexOf(locale) < 0) {
      return;
    }

    return import(
      /* webpackChunkName: "MyComponent_<hash>-i18n", webpackMode: "lazy-once" */ `./translations/${locale}.json`
    ).then((dict) => dict && dict.default);
  },
});

from-dictionary-index

Use this mode to statically embed locale-specific translations.

For large applications, even asynchronously loaded translations can significantly degrade the user experience:

  • Bundlers like webpack have to embed kilobytes of data to track each translation import
  • Users not using the "default" language have to download kilobytes of translations for every language

To avoid this, it is possible to build versions of app with specific locale translations embedded directly in JavaScript.

Usage
// webpack.config.js
{
  plugins: [
    ['@shopify/react-i18n/babel', {mode: 'from-dictionary-index'}],
  ],
}

Then generate translations/index.js files containing specific locale data using the @shopify/react-i18n/generate-dictionaries helper. e.g., the following code generates three versions of an application with English, French, and German content using webpack.

// generate-translations.js
const {
  generateTranslationDictionaries,
} = require('@shopify/react-i18n/generate-dictionaries');

// Build English app.
await generateTranslationDictionaries(['en']);
webpack(require(./webpack.config.js));

// Build French app.
await generateTranslationDictionaries(['fr'], {fallbackLocale: 'en'});
webpack(require(./webpack.config.js));

// Build German app.
await generateTranslationDictionaries(['de'], {fallbackLocale: 'en'});
webpack(require(./webpack.config.js));
Transform
// Generated translations/index.js using generateTranslationDictionaries(['fr'], {fallbackLocale: 'en'});

export default JSON.parse(
  '{"en":{"heading":"All Films"},"fr":{"heading":"fr_All Films"}}'
);
// Within MyComponent.tsx:

useI18n();

// Becomes:
import __shopify__i18n_translations from "./translations";

useI18n({
  id: "MyComponent_<hash>",
  fallback: Object.values(__shopify__i18n_translations)[0],
  translations(locale) {
    return Promise.resolve(__shopify__i18n_translations[locale]);
  },
});

defaultLocale

Type: string

Default: en

Setting the default locale to something other than en

If you want your default locale to be something else than English because it's not your primary locale, you can pass the defaultLocale option to the babel plugin:

// webpack.config.js
module.exports = {
  resolve: {
    extensions: [".js"],
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        use: {
          loader: "babel-loader",
          options: {
            plugins: [["@shopify/react-i18n/babel", { defaultLocale: "fr" }]],
          },
        },
      },
    ],
  },
};
0.6.1

9 months ago

0.5.0

1 year ago

0.4.0

1 year ago

0.3.0

1 year ago

0.2.1

2 years ago

0.1.7

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago