1.3.18 β€’ Published 4 months ago

sheet-i18n v1.3.18

Weekly downloads
-
License
ISC
Repository
github
Last release
4 months ago

sheet-i18n

An all-in-one package for sheet-based translation

npm

Installation πŸ› οΈ

You can install sheet-i18n via npm:

npm install sheet-i18n

Usage πŸš€

πŸ“‘ Main Package - sheet-i18n

sheet-i18n is a powerful and flexible library designed to streamline the process of managing and utilizing translations stored in Google Sheets (planned to support other spreadsheets soon).

It serves as a bridge between your translation data and your application, offering an end-to-end solution for exporting, managing, and integrating translations into your projects.

The sheet-i18n ecosystem is divided into three main packages:

  • sheet-i18n/exporter
  • sheet-i18n/react
  • @sheet-i18n/cli

sheet-i18n/exporter

The server-side exporter subpackage allows you to interact with Google Sheets and export translations directly into your project. This is primarily used in server-side environments, such as Next.js API routes or other backend frameworks, where you want to fetch and store translations from a Google Spreadsheet to be served to clients or used within your server application.

import { googleSheetExporter } from 'sheet-i18n/exporter';

const exporter = await googleSheetExporter({
  credentials: {
    sheetId: 'your-google-sheet-id',
    clientEmail: 'your-client-email',
    privateKey: 'your-private-key',
  },
  defaultLocale: 'default-language-in-sheet-header',
});

await exporter.exportTranslations();

Configuration βš™οΈ

The configuration object required for using the exporter is as follows:

Required πŸ“

  • credentials: Google Sheets API credentials:
    • sheetId: The ID of your Google Spreadsheet (extracted from the URL).
    • clientEmail: The email of the Google Sheets API client.
    • privateKey: The private key associated with the client.
  • defaultLocale: The default locale/language specified in your Google Sheet header.

Optional πŸ”§

  • headerStartRowNumber: Specifies the row number where the headers begin (if not at the top).
  • ignoredSheets: A list of sheets to exclude by title. By default, sheets without the defaultLocale in headers will be ignored.
  • exportPath: Path to save exported translations. This is the location where the exported translations will be saved. By default, it will use the current working directory (cwd). This option is only used when calling the exportTranslations method.

Exporter Methods πŸ› οΈ

getTranslations πŸ“

  • Description: This function retrieves the translation data, which is structured by locale keys (such as "ko", "en", etc.). It collects all translations from the specified sheet, replaces any placeholders, and prepares them to be sent to the client in the response body. Each key corresponds to a language, and the value for each key is an object containing the translated text for that locale.

  • Type: Function

  • Parameters: None
  • Returns: An object where each key is a locale (e.g., "ko", "en"), and the value is the respective translation data for that locale.

Example:

If the headers in your Google Sheets are ["ko", "en", ...], the result returned by getTranslations will look like this:

{
  "ko": {
    "greeting": "μ•ˆλ…•ν•˜μ„Έμš”",
    "farewell": "μ•ˆλ…•νžˆ κ°€μ„Έμš”"
  },
  "en": {
    "greeting": "Hello",
    "farewell": "Goodbye"
  }

  ...
}

In this example:

  • "ko" contains the translations for Korean.
  • "en" contains the translations for English. Each locale’s object contains key-value pairs for individual translations.

exportTranslations πŸ“€

  • Description: This asynchronous function is used in a Node.js environment to export translations directly into your project. It is especially useful if your project includes server-side APIs (like Next.js API routes) and you need to update the locale JSON data in your project. It can be invoked to update or create the locale files within your project, ensuring that the translation data is synced with the local environment.

  • Type: Function

  • Parameters: None
  • Returns: void

Example:

If you call the exportTranslations function, it will update or create JSON files in your project for each locale. The result could look like this:

// ko.json
{
  "greeting": "μ•ˆλ…•ν•˜μ„Έμš”",
  "farewell": "μ•ˆλ…•νžˆ κ°€μ„Έμš”"
}

// en.json
{
  "greeting": "Hello",
  "farewell": "Goodbye"
}

API Documentation πŸ“š

This package provides a streamlined way to export data using sheets API. It is designed to simplify the integration of spreadsheet data into your applications, particularly for translation workflows and internationalization (i18n).

[Google Sheets API Documentation](https://developers.google.com/sheets/api) [Google Auth Library](https://www.npmjs.com/package/google-auth-library) [Google Spreadsheet](https://www.npmjs.com/package/google-spreadsheet)

Exporting Data Format πŸ—‚οΈ

The exported translations will be saved in a format that is easy to use for localization purposes. Each translation is stored in its respective locale folder, with a structured format.


@sheet-i18n/react

The client-side i18n library subpackage of sheet-i18n.

This package provides tools to handle translations in React applications using context and hooks. It simplifies internationalization workflows by offering functions and components to manage, access, and use locale-specific translation data.

✨ Package Introduction

  • I18nStore: Store Creation Class for managing translation data.
  • createI18nContext: React Context to generate providers and hooks for translation.
  • IntlProvider: React Translation Provider for managing current locale.
  • useTranslation: Client Side Translation Hook for easy access to translation messages on the client side.
  • getTranslation: Static Translation Function for easy access to translation messages on Static module files.

πŸš€ Getting Started

Basic Usage

1. Define Translation Data

Prepare locale JSON files:

// en.json
{
  "header": {
    "login": "Login",
    "logout": "Logout"
  }
}

// ko.json
{
  "header": {
    "login": "둜그인",
    "logout": "λ‘œκ·Έμ•„μ›ƒ"
  }
}

2. Initialize i18nStore

this store will be used as a core translations module.

import en from './en.json';
import ko from './ko.json';

import { I18nStore } from '@sheet-i18n/react';

export const i18nStore = new I18nStore({
  supportedLocales: ['ko', 'en'],
  defaultLocale: 'ko',
  localeSet: {
    ko,
    en,
  },
});

3. Create i18n Context

import { i18nStore } from './file-path-of-i18nStore-initiated';
import { createI18nContext } from '@sheet-i18n/react';

export const { IntlProvider, useTranslation, getTranslation } =
  createI18nContext(i18nStore);

4. Mount Intl Context Provider in your App

import React from 'react';
import { IntlProvider } from './i18nContext';

const App = () => {
  return (
    <IntlProvider>
      <YourComponent />
    </IntlProvider>
  );
};

5. Use Translations

import React from 'react';
import { useTranslation } from './i18nContext';

const YourComponent = () => {
  const { t } = useTranslation('header');

  return (
    <div>
      <button>{t('login')}</button>
      <button>{t('logout')}</button>
    </div>
  );
};

πŸ“¦ Base API Reference

I18nStore(core)

The I18nStore manages type-safe translation states, ensuring consistency across locales.

Parameters:

  • supportedLocales: Array of supported locale strings.
  • defaultLocale: The default locale, included in supportedLocales.
  • localeSet: An object where keys match supportedLocales, and values are translation sets.
  • typeSafe(optional, default = true): An optional boolean indicating whether to use type-safe translations.

πŸ’‘ typeSafe? I18nStore is type-safe by default. So, basically, you can't add the translation data that is not defined in the locale Json files. However, for fast development, you can off the typeSafe option manually.

// typeSafe: false
const YourComponent = () => {
  // useTranslation(sheetTitle: string)
  const { t } = useTranslation('header');

  return (
    <div>
      {/* t(key: string): any */}
      <button>{t('login')}</button>
    </div>
  );
};

⚠️ Caveats:

  1. supportedLocales must be an array of locale strings.
  2. defaultLocale must exist in supportedLocales.
  3. localeSet must be an object with keys matching supportedLocales.

Example:

export const i18nStore = new I18nStore({
  supportedLocales: ['ko', 'en'],
  defaultLocale: 'ko',
  localeSet: {
    ko,
    en,
  },
});

// Accessing properties
i18nStore.supportedLocales; // ['ko', 'en']
i18nStore.defaultLocale; // 'ko'

createI18nContext

Generates React context, including the IntlProvider and useTranslation.

Parameters:

  • i18nStore: Instance of I18nStore.

⚠️ Caveats:

  1. i18nStore passed to createI18nContext must be an instance of I18nStore.
  2. custom object is not allowed to be passed to createI18nContext.

Example:

const { IntlProvider, useTranslation } = createI18nContext(i18nStore);

IntlProvider

A Context provider to provide translations to child components.

Properties:

  • currentLocale (optional):

    • A key representing the current locale to use for translations.
    • If not provided, the user's preferred language is automatically detected based on the browser setting.
    • The fallback is the default locale in i18nStore if the detected language is unsupported.
  • children: React children.

Example:

// Add currentLocale if you want to manually handle the locale

// This example is Next.js app routing
interface LayoutProps {
  params: {
    locale: Locale;
  };
}

export default function Layout({
  children,
  params,
}: PropsWithChildren<PageProps>) {
  return <IntlProvider currentLocale={params.locale}>{children}</IntlProvider>;
}

useTranslation

A hook to access translations in your components.

Parameters:

  • sheetTitle: The sheet title of the translation group.

Example:

const { t } = useTranslation('header');
const translatedMessage = t('login');

getTranslation

A function to access translations in the environment where cannot use react context and hooks.

Parameters:

  • sheetTitle: The sheet title of the translation group.

t Function

The t function is used to retrieve a translation string based on a key and optionally interpolate variables. It provides flexibility to include dynamic values, such as plain strings or React components, for enhanced localization capabilities.

Parameters:

  • key (string): The translation key to retrieve the localized string.
const translatedMessage = t('login'); // login is key
  • values (object, optional): An object containing key-value pairs to interpolate into the translation string.
// John Doe shown
const translatedMessage = t('{username} shown', { username: 'John Doe' });

πŸ’‘ Note: The values object can contain any type of data, including React components.

// <Username /> shown
const translatedMessage = t('{username} shown', { username: <Username /> });

πŸ“„ Best Practices of Translation utilities

A robust and flexible translation library designed for efficient handling of translations in React applications.

Features

  • Client-Side Translation (Explicit Strings): Translate explicit string keys directly in your components.
  • Client-Side Translation (Dynamic Strings): Dynamically match translations from JSON for unknown variables.
  • Module Translation: Use translations in the place where the react context or hooks cannot be used.

Usage

1. Client-Side Translation (Explicit Strings)

Retrieve translations for explicit keys in your React components using useTranslation.

import React from 'react';
import { useTranslation } from './i18nContext';

const YourComponent = () => {
  const { t } = useTranslation('header');

  return (
    <div>
      <button>{t('login')}</button>
      <button>{t('logout')}</button>
    </div>
  );
};

2. Client-Side Translation (Dynamic Strings)

For scenarios where the string key is not explicitly known (e.g., coming from a server), use t.dynamic. Ensure that your translation JSON and sheet are updated to include the variable values. The t.dynamic function will automatically match the values from the JSON with the provided arguments and display the result.

// App.tsx
import { useTranslation } from './i18nContext';

const { t } = useTranslation('header');
const { data } = useQuery(...queryOptions);

const deviceName = data?.device?.name;

return <div>{t.dynamic(deviceName)}</div>;

3. Module Translation

The getTranslation function allows you to use translations outside of React components, such as in static configuration files, constants, or utility modules.

It provide two possible supports

  1. t (Synchronous Translation) – Use when translation values are available at runtime.
  2. t.promise (Asynchronous Translation) – Use when the module’s evaluation order is uncertain.

βœ… Usage Scenarios

Scenario 1: Context where the translation value is evaluated at runtime

  • For the common example, t call expression in a function module
  • This behaves the same as useTranslation's t function.
Example
// module.ts
import { getTranslation } from './i18nContext';

const { t } = getTranslation('header');

export function getStatusCategory {
  return [t('Total Energy Usage'), t('Total Waste Usage')];
};

// App.tsx
// the "t" call expression is evaluated at function call timing
import { getStatusCategory } from './module';

export default function App() {
  return getStatusCategory().map((item) => <div>{item}</div>);
}

Result: The translation is resolved immediately during runtime.

⚠️ Scenario 2: Context where the translation value is evaluated immediately

  • If the module is imported dynamically in a client-side component, the evaluation order of getTranslation and t call expression may not be guaranteed.
  • Since JavaScript evaluates modules at import time, it may attempt to access translation values before IntlProvider has fully initialized the locale.
  • In this case, use t.promise to ensure the translation resolves asynchronously.

Example

// module.ts
// The "t" result in the array will be resolved asynchronously
import { getTranslation } from './i18nContext';

const { t } = getTranslation('header');

export const STATUS_CATEGORY = [
  t.promise('Total Energy Usage'),
  t.promise('Total Waste Usage'),
];

// App.tsx
// So, t.promise ensure the current client-side locale data
// is fully initialized before the translations are resolved
import { STATUS_CATEGORY } from './module.ts';

export default function App() {
  return (
    <div>
      {STATUS_CATEGORY.map((item, index) => {
        return <div key={index}>{item}</div>;
      })}
    </div>
  );
}

πŸ›  Error Handling

Custom error messages help identify misconfigurations:

  1. Invalid Params: Ensure supportedLocales, defaultLocale, and localeSet are correctly provided.
  2. Missing Default Locale: The defaultLocale must exist in supportedLocales.
  3. Invalid LocaleSet: Ensure the localeSet keys match supportedLocales.

πŸ“œ Library Versions πŸ”’

This package supports the following library versions:

  • React: ^19.0.0
  • React Intl: ^7.0.4

πŸ“œ License

This project is licensed under the ISC License.

πŸ‘€ Author

devAnderson
[GitHub](https://github.com/chltjdrhd777)
[chltjdrhd777@gmail.com](mailto:chltjdrhd777@gmail.com)


@sheet-i18n/cli

A CLI tool for efficient translation management using Google Sheets, with a focus on developer experience (DX).

Features

  • ✨ Initialize CLI configuration for translation workflows.
  • πŸ‘€ Watch files or directories for changes.
  • πŸ“€ Register translation data to Google Sheets.
  • πŸ› οΈ Built with TypeScript for type safety.

Core Concepts 🌟

😭 Traditional Translation Workflow

The traditional process of managing translations with Google Spreadsheets often includes these steps:

  1. Translator identifies the text to be translated from the page (without developer involvement).
  2. Translator adds the translation data to a Google Spreadsheet.
  3. Developer creates a script to locally download the translation data from the sheet.
  4. Developer inspects the translation data, matches it with the code, and finds the corresponding values in the source code (this can be time-consuming).
  5. If discrepancies exist between the spreadsheet and the code, developers request changes from the translator.
  6. Once resolved, the developer applies the translations using an internationalization (intl) library.
  7. For additional translations, developers manually import and apply them in the code.
  8. Results are checked on the page, and any errors prompt a repeat of the cycle.

This process is lengthy and redundant. Translators often lack visibility into how text is structured in code, especially when developers split strings, need to insert variables on the text, or add line breaks (<br/>). As a result, the translation burden falls back to the developers.

☺️ Developer-Centric Translation Workflow

This CLI streamlines the process by shifting the focus to developers:

  1. Developers identify and mark variables or text for translation directly in the code.
  2. Run the CLI to register changes (it automatically adds translations using Google Translate with support for Google Spreadsheets).
  3. Translators inspect and refine the translations for improved fluency and meaning on the Google Sheet.
  4. Developers import the confirmed translation data, completing the process.

With this approach, unnecessary back-and-forth is eliminated, and translations align seamlessly with code.

PreRequisite πŸ“

This library is tightly integrated with the @sheet-i18n/react package, which provides the React components needed for rendering translations efficiently. As a result, you must set up @sheet-i18n/react in your project before using this CLI.

1. Initialize i18n Store

This store will be used as a core translations module.

import en from './en.json';
import ko from './ko.json';

import { I18nStore } from '@sheet-i18n/react';

export const i18nStore = new I18nStore({
  supportedLocales: ['ko', 'en'],
  defaultLocale: 'ko',
  localeSet: {
    ko,
    en,
  },
  typeSafe: false, // optional (default: true)
});

πŸ’‘ typeSafe? I18nStore is type-safe by default. So, basically, you can't add the translation data that is not defined in the locale Json files. However, for fast development, you can off the typeSafe option manually.

// typeSafe: false
const YourComponent = () => {
  // can accept any string sheet title.
  const { t } = useTranslation('header');

  return (
    <div>
      {/* "t" function will accept and return any string */}
      <button>{t('login')}</button>
    </div>
  );
};

2. Create i18n Context

import { i18nStore } from './file-path-of-i18nStore-initiated';
import { createI18nContext } from '@sheet-i18n/react';

export const { IntlProvider, useTranslation } = createI18nContext(i18nStore);

3. Mount Intl Context Provider in your App

import React from 'react';
import { IntlProvider } from './i18nContext';

const App = () => {
  return (
    <IntlProvider>
      <YourComponent />
    </IntlProvider>
  );
};

4. Use Translations

import React from 'react';
import { useTranslation } from './i18nContext';

const YourComponent = () => {
  const { t } = useTranslation('header');

  return (
    <div>
      <button>{t('login')}</button>
      <button>{t('logout')}</button>
    </div>
  );
};

Follow the documentation for @sheet-i18n/react to ensure proper configuration.

Base workflow

Install the CLI tool globally or as a dev dependency:

npm install -g @sheet-i18n/cli

or

npm install --save-dev @sheet-i18n/cli

Initialize the CLI in your project:

npx sheet-i18n init

After initialization, the "sheet.config.json" file will be created in the root of your project (and .gitignored automatically).

Configuration βš™οΈ

The CLI requires a sheet.config.json file in the root of your project. Run sheet-i18n init to generate this file automatically.

Example sheet.config.json:

{
  "watchEntry": "src",
  "detectExtensions": ["tsx", "ts", "jsx", "js"],

  "googleSheetConfig": {
    "credentials": {
      "sheetId": "YOUR_GOOGLE_SHEET_ID",
      "clientEmail": "YOUR_CLIENT_EMAIL",
      "privateKey": "YOUR_PRIVATE_KEY"
    },
    "defaultLocale": "en",
    "supportedLocales": ["en", "fr", "es"],
    "ignoredSheets": ["BASE"],
    "exportPath": "./src/locales"
  }
}

1. File change monitoring configuration

  • watchEntry (optional): Entry path to detect file changes relative to current working directory (default: src)
  • detectExtensions (optional): File extensions to monitor (default: jsx, js, tsx, ts).

2. Register command configuration

  • googleSheetConfig (required): Configuration for Google Sheets integration:
    • spreadsheetId (required): The ID of your Google Spreadsheet.
    • credentials (required): Contains sheetId, clientEmail, and privateKey for API authentication.
    • defaultLocale (required): Default language/locale used in the sheet header.
    • supportedLocales(required): List of locales supported in your sheet (default: [defaultLocale]).
    • headerStartRowNumber (optional): Row number where headers start (default: 1).
    • ignoredSheets (optional): Titles of sheets to exclude from the translation process.
    • exportPath (optional): Directory path to save exported translations based on the current working directory (default: .).

Commands

🎬 init

npx sheet-i18n init

Sets up the sheet.config.json file in your project. This configuration file is required for all other commands.

πŸ‘€ watch

npx sheet-i18n watch

Monitors files or directories for changes and logs relevant updates. When watch mode is started, the CLI will detect changes of "useTranslation" and "t" calls of "@sheet-i18n/react" package.

//translationContext.tsx
import { createI18nContext } from 'sheet-i18n/react';

export const { IntlProvider, useTranslation } = createI18nContext(i18nStore);


// Component.tsx
import { useTranslation } from '../translationContext'

...

function Component (){
  // The arguments of useTranslation function = sheetTitle
  const { t } = useTranslation('environment');
  ...

  // The arguments of t function = translation key
  return (
      <>
          <SettingItem label={t('over')}>
          <SettingItem label={t('under')}>
      </>
  )
}

The watch result is

πŸ“ Translations Store:
 {
  environment: Set(1) { 'over', 'under' }
 }

πŸ“€ register

sheet-i18n register [options]

Registers translation data to Google Sheets.

Remarkable features:

  1. The register command collects the current sheets in Google Spreadsheets. If there are sheets in your local changes that do not exist in the current list, it prompts you to create the missing sheets.

  2. It updates rows and adds "translation" entries using the GOOGLETRANSLATE function supported by Google Spreadsheets for automated translations.

  3. After registering, it asks whether you want to update the locale JSON data locally. It then exports the JSON data to the exportPath specified in sheet.config.json (default is the current working directory).

Options:

  • -s, --scope <scope>
    Define the scope of the registration process:
    • diff: Only scans translation keys that have been modified in the Git history (based on git diff).
    • total: Scans the entire directory for all translation keys and updates the spreadsheet, regardless of Git changes.
      Default: diff

Detailed description of the scope option:

Scope: diff
  • The CLI identifies changes in translation keys by comparing the current state of the codebase with the latest Git commit.
  • It only processes translation keys added, removed, or modified since the last commit.
  • This is useful for incremental updates, ensuring only new or updated keys are registered.
Example:
# diff is the default scope. So you can omit the --scope option.
npx sheet-i18n register

Scope: total
  • The CLI scans the entire specified directory for all translation keys from the directory path you provide.
  • This is useful for full synchronization, ensuring all keys are registered in the spreadsheet.
  • However, it may take longer to process large directories. So, use with caution.
Example:
sheet-i18n register --scope total

πŸ“„ export

npx sheet-i18n export

Exports translation data from Google Sheets to local export directory. The configuration of export command is based on sheet.config.json on your root.

{
  "googleSheetConfig": {
    "credentials": {
      "sheetId": "YOUR_GOOGLE_SHEET_ID",
      "clientEmail": "YOUR_CLIENT_EMAIL",
      "privateKey": "YOUR_PRIVATE_KEY"
    },
    "defaultLocale": "The base language of your application in sheet header",
    "ignoredSheets": ["BASE"],
    "exportPath": "The path of export directory of translation data. (default: current working directory)"
  }
}

License πŸ“œ

This project is licensed under the ISC License. See the LICENSE file for details.

Author ✍️

Created by [devAnderson](https://github.com/chltjdrhd777).

1.3.18

4 months ago

1.3.17

5 months ago

1.3.16

5 months ago

1.3.15

5 months ago

1.3.14

5 months ago

1.3.13

5 months ago

1.3.12

5 months ago

1.3.11

5 months ago

1.3.10

5 months ago

1.3.9

6 months ago

1.3.8

6 months ago

1.3.7

6 months ago

1.3.6

6 months ago

1.3.5

6 months ago

1.3.4

6 months ago

1.3.3

6 months ago

1.3.2

6 months ago

1.3.1

6 months ago

1.2.4

6 months ago

1.2.2

6 months ago

1.2.1

6 months ago

1.2.0

6 months ago

1.1.6

6 months ago

1.1.5

6 months ago

1.1.4

6 months ago

1.1.3

6 months ago

1.1.2

6 months ago

1.1.1

6 months ago

1.1.0

6 months ago

1.0.0

6 months ago

0.3.0-canary.3

7 months ago

0.2.6-canary.3

7 months ago

0.2.6-canary.2

7 months ago

0.2.6-canary.1

7 months ago

0.2.6-canary.0

7 months ago

0.2.5

7 months ago

0.2.5-canary.10

7 months ago

0.2.5-canary.9

7 months ago

0.2.5-canary.7

7 months ago

0.2.5-canary.6

7 months ago

0.2.5-canary.5

7 months ago

0.2.5-canary.4

7 months ago

0.2.5-canary.3

7 months ago

0.2.5-canary.2

7 months ago

0.2.5-canary.1

7 months ago

0.2.5-canary.0

7 months ago

0.2.4

7 months ago

0.2.3

7 months ago

0.2.2

7 months ago

0.2.1

7 months ago

0.2.0

7 months ago

0.1.22

7 months ago

0.1.21

7 months ago

0.1.20

7 months ago

0.1.19

7 months ago

0.1.18

7 months ago

0.1.17

7 months ago

0.1.16

7 months ago

0.1.15

7 months ago

0.1.14

7 months ago

0.1.13

7 months ago

0.1.12

7 months ago

0.1.11

7 months ago

0.1.10

7 months ago

0.1.9

7 months ago

0.1.8

7 months ago

0.1.7

7 months ago

0.1.6

7 months ago

0.1.5

7 months ago

0.1.4

7 months ago

0.1.3

7 months ago

0.1.2

7 months ago

0.1.0

7 months ago