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

epic-language

v1.0.0

Published

Translations for React and React Native.

Downloads

10

Readme

epic-language

AI-assisted translation library for React and React Native.

  • 3 ways to store translations
    • Bundled Generated manually or with AI during build and stored in the bundle
    • CDN Generated with AI during build time and stored in a Serverless function
    • Serverless Translated with AI and cached during runtime in a Serverless function
  • Support for React Native
  • <Text id="myTranslationKey" /> component to render translations
  • Replacements with {} or {1} {2} in translations
  • Translations generated on demand using LLMs (AI)
  • Optimized for development phase: no need to commit any translations to source!

This project was built to try the Bun 🐰 runtime, Cursor AI 🖱️ editor and CodeWhisperer 🤫. See this blog post detailing my experiences.

Usage with Buildtime Translation

This is the most basic method where all translations are bundled into the main JavaScript bundle. This is useful for development, when supporting only few languages or when distributing as a package like React Native apps. To use this method, create a main JSON file in the default language containing the initial translations. The CLI translation script can then be used to translate these keys during installation or the build. Running this will require OPENAI_API_KEY and OPENAI_ORGANIZATION (optional) environment variables to be present. These variables will automatically be loaded from your .env file thanks to Bun.

bunx epic-language --input translations.json --output translations --language en --languages es,zh

Once generated the translation files can be imported as JSON and bundled directly into JavaScript.

import { create, Language } from 'epic-language'
import spanishSheet from './translations/es.json'
import chineseSheet from './translations/zh.json'

const { translate } = create({
  // Initial translations in default language.
  translations: {
    title: 'My Title',
    replacement: 'Counter: {}',
    multipleOrderedReplacements: "What's {2} current {1}?",
  },
  // Additional translated language sheets.
  sheets: {
    [Language.es]: spanishSheet,
    [Language.zh]: chineseSheet,
  },
})

translate('title') // My Title
translate('replacement', 5) // Counter: 5
translate('multipleOrderedReplacements', ['pastime', 'your']) // What's your current pastime?

Individual Translations Served As Static Files

To avoid bundling translations with your regular JavaScript code and only send the required translations to the user you can configure a static route. In most build tools assets from the /public folder will be served statically.

bunx epic-language --input translations.json --output public/translation --language en --languages es,zh
import { create, Language } from 'epic-language'
// Always load the translations for the default language.
import translations from './translations.json'

const { translate } = create({
  // Initial translations in default language.
  translations,
  // Translation files served statically as JSON files per language.
  // [language] will be replaced with the language (en, es, zh etc.)
  route: 'translation/[language].json',
})

translate('title') // My Title

Usage with Runtime Translation and Caching

The route can point to a Serverless or Edge API function that will generate and cache the desired translations. This avoids translating every language during the build and will save a lot of time during the development phase, while ensuring translations always work.

import { create } from 'epic-language'
import translations from './translations.json'

const { translate } = create({
  // Initial translations in default language.
  translations,
  // Route to load translations for user language.
  // [language] will be replaced with the language (en, es, zh etc.)
  route: '/api/translation/[language]',
})

translate('title') // My Title
translate('counter', '5') // Counter: 5

The following functions have been tested on Vercel and can be adapted accordingly for other hosting providers. It's important to make sure the OPENAI_API_KEY and OPENAI_ORGANIZATION (optional) environment variables are available in your functions. Check out the /api source code of the demo application.

Serverless Function

import { readFileSync } from 'fs'
import { it } from 'avait'
import { Language } from 'epic-language'
import { translate } from 'epic-language/function'
import type { VercelRequest, VercelResponse } from '@vercel/node'

export default async function handler(request: VercelRequest, response: VercelResponse) {
  const language = request.query.lang as Language
  if (!(language in Language))
    return response.status(500).json({ error: `Missing language "${language}"` })
  const englishSheet = JSON.parse(
    readFileSync(new URL('../../translations.json', import.meta.url), 'utf8'),
  )
  const { error, value: sheet } = await it(translate(JSON.stringify(englishSheet), language))
  if (error)
    return response.status(500).json({ error: `Translation for language "${language}" failed!` })
  response.status(200).json(sheet)
}

Edge Function

import { it } from 'avait'
import { Language } from 'epic-language'
import { translate } from 'epic-language/function'
import translations from '../../translations.json'

export const runtime = 'edge'

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const language = searchParams.get('lang') as Language
  if (!(language in Language)) return new Response(`Missing language "${language}"`)
  const { error, value: sheet } = await it(translate(JSON.stringify(translations), language))
  if (error) return new Response(`Translation for language "${language}" failed!`)
  return Response.json(sheet)
}

Usage with React Native

import { create, Language } from 'epic-language/native'

const { translate, Text } = create(
  // Default translations.
  { title: 'My App' },
  // Translations in additional languages.
  {
    [Language.es]: { title: 'Mi aplicación' },
    [Language.zh]: { title: '我的應用程式' },
  },
  // Language, defaults to english.
  Language.en,
)

const settingsText = translate('title')
const Heading = <Text style={{ fontSize: 32 }}>title</Text>

ES Module exports are still an experimental feature in React Native's Metro bundler. Since this plugin requires them you have to turn them in your metro.config.js with:

const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')

const config = {
  resolver: {
    unstable_enablePackageExports: true,
  },
}

module.exports = mergeConfig(getDefaultConfig(__dirname), config)

Advanced Usage

The State object allows you to keep track of the status for loading language sheets.

import { create, State } from 'epic-language'

const { translate } = create({ translations: { title: 'My Title' } })

State.current === 'ready' // Current state: initial, loading or ready.
State.listen((state, language) => console.log(state, language)) // Listen to state changes.
State.languages // List of all available translated languages.

It's also possible to insert JSX.Element as replacements.

import { create } from 'epic-language'

const { translate } = create({ translations: { counter: 'Count {} at {}' } })

translate(counter, [<span>5</span>, <div style={{ color: 'red' }}>{`12:34 PM`}</div>]) // Count <span>5</span> at <div>12:34 PM</div>