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

nuxt-easy-texts

v1.0.3

Published

Easy key-based text extractor and loader for Nuxt 3

Downloads

582

Readme

nuxt-easy-texts - key-based texts/translations for Nuxt 3

  • Define translatable texts using keys and default texts
  • Load and override texts and translations at runtime
  • Support for plural text strings
  • Customize how text extractions are generated (JSON, PO file, GraphQL, ...)
  • Debug mode to render the key instead of the text

Why not...?

This module was created to specifically solve a single problem: Having an easy way to collect translatable texts and load their translations at runtime (for example from an API endpoint). The compiled build will only contain the keys and no more texts. They always have to be provided at runtime.

It is not meant as a full replacement for something like vue-i18n or nuxt/i18n, which offer way more features such as language based routing, SEO, etc.

If the only thing you want to do is collect texts in your code and load them at runtime, this module is for you!

<template>
  <div>{{ $texts('homeTitle', 'Welcome!') }}</div>
</template>

Setup

Install module

npm install --save nuxt-easy-texts
export default defineNuxtConfig({
  modules: ['nuxt-easy-texts'],

  easyTexts: {
    generators: [
      {
        outputPath: './texts.json',
        generate: 'json',
      },
    ],
  },
})

Use texts

The module provides two properties on the Nuxt and Vue instance: $texts and $textsPlural. You can directly use them in your Vue components (and they are reactive!):

<template>
  <div>
    <div>{{ $texts('homeTitle', 'Welcome!') }}</div>
    <div>
      {{ $textsPlural('counter', counter, 'One item', '@count items') }}
    </div>
  </div>
</template>

<script lang="ts" setup>
const counter = ref(1)
</script>

Alternatively, you can also use them from the useEasyTexts() composable:

const { $texts, $textsPlural } = useEasyTexts()

// Must be wrapped in computed() to remain reactive.
const text = computed(() => $texts('backToTop', 'Back to top'))

Configure loader

All texts are loaded at runtime. How and where the texts are loaded is up to you. To define a loader, create the following file in ~/app/easyTexts.loader.ts:

import { defineEasyTextsLoader } from '#nuxt-easy-texts/types'

export default defineEasyTextsLoader(() => {
  const language = useCurrentLanguage()

  return {
    load(): Promise<Record<string, string | [string, string]>> {
      // Load the texts in the current language from an API route.
      // Method should return an object whose properties are the full keys and
      // the values are either a string (for $texts keys) or an array of
      // exactly two strings (for $textsPlural keys).
      //
      // For our two examples above, the loader would return the following:
      // {
      //   homeTitle: 'Welcome!',
      //   counter: ['One item', '@count items'],
      // }
      return $fetch('/api/load-texts', {
        query: {
          language: language.value,
        },
      })
    },
  }
})

nuxt-easy-texts will call this method inside its plugin, right before it injects $texts and $textsPlural. Make sure that anything that requires these two methods is loaded after this plugin is initialized.

How it works

nuxt-easy-texts searches your Vue components and JS/TS files for all calls to the $texts and $textsPlural methods. Because of that, there are a few things that won't work:

  • Rename methods: The name of the method must be exactly $texts or $textsPlural - you can't rename them (e.g. const { $texts: getText } = useEasyTexts() will not work)
  • Use variables: You can only use string literals inside both methods. Something like {{ $texts('language_' + language) }} will not work

Output files

You can completely customize the generated output file (or generate mutliple files):

export default defineNuxtConfig({
  modules: ['nuxt-easy-texts'],

  easyTexts: {
    generators: [
      // Generates a JSON file of all raw extractions.
      {
        outputPath: './texts.json',
        generate: 'json',
      },

      // Provide a custom method to generate the output file.
      {
        outputPath: './texts.txt',
        generate: (extractions) => {
          // Generates a simple text file with one extraction per line.
          return extractions
            .map((v) => {
              // Handle single texts.
              if (v.type === 'text') {
                return `${v.fullKey}: ${v.defaultText}`
              }

              // Handle plural texts.
              return `${v.fullKey}: ${v.singular} | ${v.plural}`
            })
            .join('\n')
        },
      },
    ],
  },
})

Generate a GraphQL fragment for the drupal/texts module

If you use the drupal/texts module, you can use the builtin drupal-graphql-texts generate option. It will generate a fragment file that can be used to load the translations in a GraphQL query.

export default defineNuxtConfig({
  modules: ['nuxt-easy-texts'],

  easyTexts: {
    generators: [
      {
        outputPath: './fragment.easyTexts.graphql',
        generate: 'drupal-graphql-texts',
      },
    ],
  },
})

The resulting file will look like this:

fragment easyTexts on TextsLoader {
  homeTitle: getText(key: "homeTitle", default: "Welcome!")
  counter: getTextPlural(
    key: "counter"
    singular: "One item"
    plural: "@count items"
  ) {
    singular
    plural
  }
}

Load the texts

All default/singular/plural texts in your code are compiled away, for example:

const { $texts, $textsPlural } = useEasyTexts()

const title = $texts('footerCopyright', '© 2024 ACME')

const counter = ref(1)
const countText = $texts('countText', counter.value, 'One item', '@count items')

This is compiled to:

const { $texts, $textsPlural } = useEasyTexts()

const title = $texts('footerCopyright')

const counter = ref(1)
const countText = $textsPlural('countText', counter.value)

nuxt-easy-texts requires that every text string is loaded at runtime. The default texts are not available at runtime in any way. You could however generate a JSON file with a custom generate method and then return said JSON file in your loaders load() method.

Text keys and contexts

Providing context is also possible. You can do that by prefixing the key with a string followed by a dot:

<template>
  <div>{{ $texts('cart.addButton', 'Add to cart') }}</div>
</template>

This will generate the following extraction:

{
  "fullKey": "cart.addButton",
  "key": "addButton",
  "context": "cart",
  "defaultText": "Add to cart",
  "type": "text",
  "filePath": "/var/www/example/components/AddToCart.vue"
}

Debug Mode

This is helpful if you want to offer content editors in a CMS a way to easily find which text key is currently being shown.

const { $texts, toggleDebug } = useEasyTexts()

const goBackText = computed(() => $texts('goBack', 'Go back to homepage'))

// Logs 'Go back to homepage'
console.log(goBackText.value)

toggleDebug()

// Logs 'goBack'
console.log(goBackText.value)

Changing languages (reloading texts)

If the language or any context (like country) changes you may want to reload all texts. To do that, implement a reloadTrigger method in your loader. The method should return a computed property. nuxt-easy-texts adds a watcher to this property on the client side and will reload the texts by calling load() again on your loader when the value changes.

import { defineEasyTextsLoader } from '#nuxt-easy-texts/types'

export default defineEasyTextsLoader(() => {
  const language = useCurrentLanguage()

  return {
    reloadTrigger() {
      return computed(() => language.value)
    },
  }
})

NOTE: The watcher is only added client-side, not during SSR! The loader.load() method is called exactly once during SSR and it must return the correct translations.

Because the loaded texts are stored using useState() they are part of the payload. So during hydration, the loader.load() method is not called.

Reference

Configuration reference

export default defineNuxtConfig({
  modules: ['nuxt-easy-texts'],

  easyTexts: {
    // Define which files to include.
    pattern: [
      './components/**/*.{js,ts,vue}',
      './layouts/**/*.{js,ts,vue}',
      './layers/**/*.{js,ts,vue}',
      './pages/**/*.{js,ts,vue}',
      './composables/**/*.{js,ts,vue}',
    ],

    // Define what and where to generate extractions.
    generators: [
      {
        outputPath: './texts.json',
        generate: 'json',
      },
    ],

    // Provide text keys and default values at build time.
    // Useful if you have keys that can't be extracted for some reason.
    globalTexts: { learnMore: 'Learn more' },
  },
})

useEasyTexts() composable

const {
  $texts,
  $textsPlural,
  isDebug,
  enableDebug,
  disableDebug,
  toggleDebug,
} = useEasyTexts()

$texts

Use a single text string

$texts('backToHome', 'Back to homepage')

$textsPlural

Use text string with a singular and plural option.

const cartCount = ref(2)
$textsPlural('cartItems', cartCount.value, '1 items', '@count items')

isDebug: ComputeRef

Check if debug mode is enabled.

enabledDebug: () => void

Enable debug mode.

disableDebug: () => void

Disable debug mode.

toggleDebug: () => void

Toggle debug mode.