• Stars
    star
    205
  • Rank 187,121 (Top 4 %)
  • Language
    JavaScript
  • License
    BSD 3-Clause "New...
  • Created about 7 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple localization for Preact.

preact-i18n 🌎 npm travis FOSSA Status

Usage example

Simple localization for Preact.

  • Tiny: about 1.3kb gzipped
  • Supports dictionary and key scopes/namespaces while maintaining a global dictionary
  • Supports nested dictionaries:
    • Wrap your component in a default dictionary and scope key
    • Wrap it again later on (in an app!) to override the defaults
  • Supports pluralization of strings using nested objects.
  • Supports template {{fields}} in definition values
  • Has a companion ESLint plugin to help catch bugs early

Preact Version Support

By default, the master branch of this repo supports preact 9 and below, and is published in normal patch/minor/major releases to the latest tag in npm. Support for preact X (versions 10+ of preact) is handled in the preactX branch and are always published to the preactx tag in npm. When preact X obtains widespread adoption, the master branch of this project will support preact X and a new major version under latest tag will be published to in npm.

Installation

npm install --save preact-i18n

# For TypeScript Definitions
npm install --save-dev @types/preact-i18n

Getting Started

  1. Create a definition. Typically JSON files, we'll call ours fr.json:
{
	"news": {
		"title": "Nouvelles du Monde",
		"totalStories": {
			"none": "Aucun article",
			"one": "Un article",
			"many": "{{count}} articles"
		}
	}
}
  1. Expose the definition to your whole app via <IntlProvider>:
import { IntlProvider } from 'preact-i18n';
import definition from './fr.json';

render(
	<IntlProvider definition={definition}>
		<App />
	</IntlProvider>
);
  1. Use <Text /> to translate string literals:
import { Text } from 'preact-i18n';

// Assume the "stories" prop is a list of news stories.
const App = ({ stories=[] }) => (
	<div class="app">
		<h1>
			{/* Default fallback text example: */}
			<Text id="news.title">World News</Text>
		</h1>
		<footer>
			{/* Pluralization example: */}
			<Text
				id="news.totalStories"
				plural={stories.length}
				fields={{
					count: stories.length
				}}
			/>
		</footer>
	</div>
);

That's it!

Fallback Text

Rendering our example app with an empty definition (or without the Provider) will attempt to use any text contained within <Text>..</Text> as fallback text.

In our example, this would mean rendering without a definition for news.title would produce <h1>World News</h1>.

If we provide a definition that has a title key inside a news object, that value will be rendered instead.

Pluralization and Templating

In our example, <footer> is using <Text> as a convenient way to do pluralization and templating. In our definition, news.totalStories is an Object with pluralization keys. The values in that object will be selected based on an integer plural prop passed to <Text>.

Any definition value (including pluralization values) can contain {{field}} placeholders. These placeholders get replaced with matched keys in an object passed as the fields prop. In our example, the "many" plural form is such a template - it will render "5 articles" when fields={{ count: 5 }}.

The available forms for specifying pluralization values are as follows:

  • "key": { "singular":"apple", "plural":"apples" }
  • "key": { "none":"no apples", "one":"apple", "many":"apples" }
  • "key": { "zero":"no apples", "one":"apple", "other":"apples" }
  • "key": ["apples", "apple"]

Taking <Text id="news.totalStories" ..> from our example:

  • <.. plural={0}> renders Aucun article (no articles)
  • <.. plural={1}> renders Un article (one article)
  • <.. plural={2} fields={{ count: 2 }}> renders 2 articles
  • <.. plural={3} fields={{ count: 3 }}> renders 3 articles

In addition to <Text>, withText() and <Localizer> provide ways to translate more than just display text - HTML attributes, component props, arbitrary Strings, etc.

ESLint Plugin

A companion ESLint plugin exists, eslint-plugin-preact-i18n, which has several rules that help spot common issues like un-i18n'd text, misconfigured tags, and missing keys, that are beneficial in spotting defects early and ensuring that your application is properly i18n'd.


API

Table of Contents

IntlProvider

<IntlProvider> is a nestable internationalization definition provider. It exposes an Intl scope & definition into the tree, making them available to descendant components.

Note: When nested, gives precedence to keys higher up the tree! This means lower-level components can set their defaults by wrapping themselves in an <IntlProvider>, but still remain localizable by their parent components.

Parameters

  • props
    • props.scope String? Nest definition under a root key, and set the active scope for the tree (essentially prefixing all <Text /> keys).
    • props.mark Boolean If true, all <Text> elements will be shown with a red/green background indicating whether they have valid Intl keys. (optional, default false)
    • props.definition Object Merge the given definition into the current intl definition, giving the current definition precedence (i.e., only adding keys, acting as defaults) (optional, default {})

Examples

// generally imported from a JSON file:
let definition = {
	foo: 'Le Feux'
};

<IntlProvider scope="weather" definition={definition}>
	<Text key="foo">The Foo</Text>
</IntlProvider>

// This will render the text:
"Le Feux"

Localizer

<Localizer /> is a Compositional Component. It "renders" out any <Text /> values in its child's props.

Parameters

  • props Object
    • props.children Object Child components with props to localize.
  • context Object
    • context.intl Object [internal] dictionary and scope info

Examples

<Localizer>
	<input placeholder={<Text id="username.placeholder" />} />
</Localizer>
// produces:
<input placeholder="foo" />
<Localizer>
	<abbr title={<Text id="oss-title">Open Source Software</Text>}>
		<Text id="oss">OSS</Text>
	</abbr>
</Localizer>
// produces:
<abbr title="Open Source Software">OSS</abbr>

MarkupText

<MarkupText> is just like Text but it can also contain html markup in rendered strings. It wraps its contents in a <span> tag.

Parameters

  • props Object props
    • props.id String Key to look up in intl dictionary, within any parent scopes ($scope1.$scope2.$id)
    • props.fields Object Values to inject into template {{fields}}. Values in the fields object will be coerced to strings, with the exception of <Text/> nodes which will be resolved to their translated value (optional, default {})
    • props.plural Number? Integer "count", used to select plural forms
  • context Object
    • context.intl Object [internal] dictionary and scope info

Examples

// If there is no dictionary in context..
<MarkupText id="foo"><b>The Foo</b></MarkupText>
// ..produces the vnode:
<span><b>The Foo</b></span>
// Given a dictionary and some fields..
<IntlProvider definition={{ foo:'Le Feux <b>{{bar}}</b>' }}>
	<MarkupText id="foo" fields={{ bar: 'BEAR' }}>The Foo</MarkupText>
</IntlProvider>
// ..produces the vnode:
<span>Le Feux <b>BEAR</b></span>
// Within a scope, both `id` and the definition are namespaced..
<IntlProvider scope="weather" definition={{ foo:'Le <a href="http://foo.com">Feux</a>' }}>
	<MarkupText id="foo">The Foo</MarkupText>
</IntlProvider>
// ..produces the vnode:
<span>Le <a href="http://foo.com">Feux</a></span>
// renders nothing if there is no key match and no fallback
<div><MarkupText /></div>
// ..produces the vnode:
<div/>

Text

<Text> renders internationalized text. It attempts to look up translated values from a dictionary in context.

Template strings can contain {{field}} placeholders, which injects values from the fields prop.

When string lookup fails, renders its children as fallback text.

Parameters

  • props Object props
    • props.id String Key to look up in intl dictionary, within any parent scopes ($scope1.$scope2.$id)
    • props.plural Number? Integer "count", used to select plural forms
    • props.fields Object Values to inject into template {{fields}}. Values in the fields object will be coerced to strings, with the exception of <Text/> nodes which will be resolved to their translated value (optional, default {})
    • props.children
  • context Object
    • context.intl Object [internal] dictionary and scope info

Examples

// If there is no dictionary in context..
<Text id="foo">The Foo</Text>
// ..produces the text:
"The Foo"
// Given a dictionary and some fields..
<IntlProvider definition={{ foo:'Le Feux {{bar}}' }}>
	<Text id="foo" fields={{ bar: 'BEAR' }}>The Foo</Text>
</IntlProvider>
// ..produces the text:
"Le Feux BEAR"
// Within a scope, both `id` and the definition are namespaced..
<IntlProvider scope="weather" definition={{ foo:'Le Feux' }}>
	<Text id="foo">The Foo</Text>
</IntlProvider>
// ..produces the text:
"Le Feux"

withText

@withText() is a Higher Order Component, often used as a decorator.

It wraps a child component and passes it translations based on a mapping to the dictionary & scope in context.

Parameters

Examples

@withText({
	placeholder: 'user.placeholder'
})
class Foo {
	// now the `placeholder` prop is our localized String:
	render({ placeholder }) {
		return <input placeholder={placeholder} />
	}
}
@withText({
	placeholder: <Text id="user.placeholder">fallback text</Text>
})
class Foo {
	render({ placeholder }) {
		return <input placeholder={placeholder} />
	}
}
@withText('user.placeholder')
class Foo {
	// for Strings/Arrays, the last path segment becomes the prop name:
	render({ placeholder }) {
		return <input placeholder={placeholder} />
	}
}

Works with functional components, too

const Foo = withText('user.placeholder')( props =>
	<input placeholder={props.placeholder} />
)

getWrappedComponent() returns wrapped child Component

const Foo = () => <div/>;
const WrappedFoo = withText('user.placeholer')(Foo);
WrappedFoo.getWrappedComponent() === Foo; // true

intl

Higher-order function that creates an <IntlProvider /> wrapper component for the given component. It takes two forms depending on how many arguments it's given: It can take a functional form like: intl(ComponentToWrap, options)

or it can take an annotation form like:

Parameters

  • Child
  • options Object If there are two arguments, the second argument is Passed as props to <IntlProvider />
    • options.scope Nest definition under a root key, and set the active scope for the tree (essentially prefixing all <Text /> keys).
    • options.definition Merge the given definition into the current intl definition, giving the current definition precedence (i.e., only adding keys, acting as defaults) (optional, default {})

License

FOSSA Status

More Repositories

1

wiretie

A Higher Order Component for Preact that resolves (async) values from a model and passes them down as props.
JavaScript
494
star
2

dtk

DTK (data toolkit) is a suite of tools for parsing, analyzing, and graphing logs and other datasets.
Perl
112
star
3

preconf

A Higher Order Component that provides configuration as props.
JavaScript
89
star
4

gitprompt

An efficient, highly configurable prompt for Git status information in command-line shells
Perl
61
star
5

px

A Linux tool that provides progress and status information for command pipelines.
Perl
42
star
6

preact-context-provider

A generic <Provider /> for preact. It exposes any props you pass it into context.
JavaScript
33
star
7

sibyl

Sibyl is an online agile estimation tool that doesn't require sign-ups, entering user stories, or any other time consuming steps that keeps you from doing what matters: estimating on stories.
Go
32
star
8

eslint-config-synacor

Standard eslint rules for all Synacor javascript projects
JavaScript
22
star
9

python-cors

A Python package for dealing with HTTP requests and same-origin policies.
Python
14
star
10

jan

Jan is a simple library for making HTTP requests.
JavaScript
14
star
11

choose-files

Prompt the user to select files, then pass them to a callback.
JavaScript
14
star
12

frameworkless

A simple, flexible framework for developing medium-complexity web application front-ends.
JavaScript
12
star
13

maltypart

Maltypart is a simple multipart request builder
JavaScript
7
star
14

python-repath

A port of the node module path-to-regexp to Python.
Python
7
star
15

sync-git-utils

A collection of useful git utilities
Perl
6
star
16

ng-inject

Decorator that wraps a class in an Angular 1.x DI injector for the given dependencies.
JavaScript
5
star
17

eslint-plugin-preact-i18n

eslint plugin for users of the preact-i18n library
JavaScript
4
star
18

frameworkless-view

A simple view-presenter module for frameworkless
JavaScript
3
star
19

frameworkless-stage

Push views onto a stage, with optional view transitions.
JavaScript
2
star
20

argon2id

A utility library for serializing hashed passwords with its salt using argon2id
Go
2
star
21

test-mockpackages

Test::MockPackages is a package for mocking other packages as well as ensuring those packages are being used correctly.
Perl
1
star
22

nginx-module-txid120

Generate Transaction IDs in Nginx
C
1
star