Skip to content

Very lightweight and easy to use JavaScript library for multilingual web apps and sites. Supports both script and inline translations for text, numbers, and dates from nestable JSON sources. Includes functionality for a fully customizable language menu.

License

Notifications You must be signed in to change notification settings

Design-Dude/ddLocale

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ddLocale

ddLocale is a fairly easy-to-use, yet flexible, JavaScript library for multilingual web apps and sites. It's similar to i18n, but without (too many) clutter. It supports both script and inline translations for text, simplified plurals, (ordinal) numbers, and dates from nestable JSON sources. It also includes functionality for a fully customizable language menu.

Features

  • Just a simple yet comprehensive library with no dependencies.
  • Nested language files for easy maintanance.
  • Multiple syntax options, such as attributes, arrays, or objects.
  • Extensive inline translation options, including an easy-to-use tag maker.
  • Supports language switching of static elements without reloading the page.
  • Optionally extends the Javascript String, Number, Date and Element functionality.
  • Support for Right To Left (RTL) languages.
  • Support for simple word-by-word pluralization in the JSON language files.
  • Optional support for ordinal numbers with supporting rules defined for each language in external scripts.
  • Fully functional language menu with behavior settings (WCAG-compliant) and styling options.
  • Optional use of the ddMenu library for seamless integration with other navigation menus with similar functionality and styling.
  • Free (a coffee would be much appriciated).

Installation

Download and double-click the latest version. and include the Javascript in your project. You can also use this link for the latest version.

	<script src="./.../ddLocale.js"></script>

Add a language folder to your project and include the JSON files with the necessary tranlations per language. Also include the formater script files in the ordinalRules folder if you are planning to use ordinal number translations.

Initialisation

Always call ddLocale.init({}) with the options of your choice. Normally, you'll probably load your settings first, which store the user's language choice. You can also use your browser's local date/time settings to specify ddLocale. By default, ddLocale is set to "en", regardless of whether that translation exists. To change the language later on you may use ddLocale.set({}) which is just an alternative to .init(...).

This example includes all the option available:

	ddLocale.init({
		cultures: [ // all available languages
			{
				culture: "en",
				title: "English"
			},
			{
				culture: "nl-NL",
				title: "Nederlands",
				direction: "ltr" // optional ltr or rtl
			}
		],
		path: "lang/", // path from the root to the language files, default "lang/"
		language: "", // optional if culture is used, default "en" (lowercase)
		country: "", // optional, default "" (uppercase)
		culture: "nl-NL", // optional, overrules language and country if set
		replacement: "__", // optional, default __
		menu: {
			domId: "language", // id of dom-node where language menu will be triggered
			text: "", // "long" title, "short" language code or some translation "key"
			className: "center" // optional, className for the dropdown menu
			autoOpen: 200, // delay in ms
			autoClose: 200 // delay in ms (if used, must be long enough to reach the menu from the butten)
			toggle: true // use toggle instead of built-in menu
			engine: 'ddMenu', // optional, use ddMenu instead of built-in menu
		},
		ready: function (object) {
			/*
				new language loaded
				you can use object to save settings for example
				object = {
					language: "nl",
					country: "NL",
					culture: "nl-NL",
					title: "Nederlands"
				}
			*/
		},
		success: function (err) { // ddLocale is now ready for use
			// resume initialisation scripts
		},
		failed: function (err) { // something went wrong
		},
		stringFormats: { // your collection of option for use with toLocaleString
			eur: { // name your options
				style: "currency", // known options for number translations
				currency: "EUR",
				minimumFractionDigits: 1,
				maximumFractionDigits: 2
			},
			shortDate: {
				year: "numeric", // or known options for date translations
				month: "short",
				day: "numeric",
			},
			ordinal: { format: "ordinal" },
			wordShort: { format: "word", short: true },
			ordinalValue: { format: "value" },
			ordinalWord: { format: "word" },
			ordinalDecimalWord: { format: "word", decimals: 2 },
			ordinalZeroWord: { format: "word", zero: "none" },
			ordinalAlt: { format: "alt" },
			ordinalSuffix: { format: "suffix", gender: "f" },
			bytes: { format: "bytes" }
		},
		autoInline: false, // optional, default true, start inline translation after load
		ordinalRules: false, // optional, default false, true or path-string
		log: false, // optional, default true
		nocache: false, // if true language files will be loaded using timestamps
		minified: true, // default false, load minified ordinal scripts (i.e. en.min.js)
		usePrototypes: true // default false, extends existing prototypes ```String```, ````Number``` and ````Date```.
	});

Properties

cultures

Define a list of cultures to let ddLocale know which languages are available. A cultures object has a culture, title and optional direction.

culture: this is a standard country or language code like "nl-NL" or "en". Make sure that culture matches the corresponding JSON language file name, for example nl-NL.json or en.json.

title: the name of language in it's own language for use in the drop down language menu.

direction: optional "ltr" or "rtl". If available the direction will be set as style and attribute to the body.

path

If your JSON-language files are not in a ./languages/ folder you can specify the new path. Just leave out ./ but include / at the end.

language, country and culture

language and country define culture. Make sure that culture matches one of cultures objects.

replacement

The optional replacement character is used as a filler for non-found placeholder expressions.

menu

domId: ddLocale only needs a DOM element with an id to place a button and a menu in.

text: button determines the button text:

  • "long" will place cultures[].title inside the button.
  • "short" will place the property ddLocale.language inside the button.
  • An empty string "" will leave button empty.
  • Any other string is considered a key to be translated. menu: without the menu option, the button of the built-in button cycles through the available languages. With the menu option, the button opens a popup menu with all available languages ​​from cultures. The value of menu is used as the className to style the popup menu.

engine: With ddMenu, the only engine currently available, a more flexible system is used. Of course, ddMenu must be installed, but then you can share functionality and styling.

ready

The ready() function will be called each time a new language is set and loaded. Use this function to save user settings with the new language for example.

success

If other scripts or libraries use ddLocale you can start their initialisation from the success() function. success() will only be called once per (page) load.

stringFormats

Your set of existing options for use with toLocaleString() which allows dates and numeric values ​​to be represented in locale format. The number and date formats standard javascript options. The string formats look like this:

stringFormats: { // your collection of options
	keyName: { // name your options
		format: "word", // value | ordinal | word | suffix | alt | bytes
		zero: "none", // optional replacement for value 0. Value may be a translation key
		decimals: 2, // optional number of decimals (defualt 0) used to round the value
		gender: 'f' // optional gender (default 'm') used in some countries
	}
}

autoInline

By default all elements with t attribute will be translatesd after load. If you don't want that you can set autoInline: false.

ordinalRules

With ordinalRules: true, ddLocale attempts to add additional rules for ordinal numbers. They describe adjustments for numerical values (11st) ​​according to local rules. The available scripts should be placed in the {path/}ordinalRules/ folder. Or, if ordinalRules is a string-path ddLocale will look in {path/}{ordinalRules/}. As you can see, both pathandordinalRulesshould include the/``` at the end.

ddLocale always attempts to load culture.js. If it's not available, a second attempt is made with country.js. If ordinalRules is not found, it is disabled for the selected language.

Unfortunately, the set of rules is far from complete. Current support is limited to:

  • nl
  • en (in progress)

Please also feel free to add or request missing ordinalRules for your language.

usePrototypes

By default, ddLocale works independently. You can optionally add the same functionality to the existing String, Number, and Date prototypes. A function call like ddLocale.t(key, options) would then also work like (key).t(options), where key can be a String, Number, or Date.

JSON-language files

Language files are json files. Make sure the culture matches the JSON language file name, for example nl-BE.json or en.json. Please note the following rules:

  • All language files should have the same keys.
  • keys are case sensitive.
  • non-existing keys, or keys without value will be returned unaltered.
  • You can use nested keys.
  • You can also use placeholders as numbers {0} (Array-style) or as {names} (JSON-style).
  • If you plan to use the inline translation feature, it is important to use only lowercase letters for placeholder names, because the standard data-* attributes used for inline data storage do not support uppercase letters.
	{
  		"bitcoin": "bitcoin->+s",
		"bitcoins": "On {0} I had {1} {2} worth {3}.",
  		"cheddar": "cheddar->+s",
		"cheese": "cheese->cheeses",
		"exit": {
			"goodbye": "A presto {0} {1} {2}."
		},
		"fullname": "{firstname} {middlename} {lastname}",
		"hello": "Hello {name} {lastname}.",
		"hi": "Hi {fullname}.",
		"ordinalTest": "Word: {0}, ordinal: {2}, bytes: {3}",
		"plurals": "{0} {1} and a {2}.",
		"tool name": "ddLocale",
		"currentdate": "Current date"
	}

Scripting usage

You can put ddLocal to work via scripting.

ddLocal is semi-recursive. The name of a placeholder is translated if a key with the same name exists and the formatter is not equal to s. However, going deeper than one level is not fully tested (and actually not necessary). The following example make use of the JSON above. Just pass the key to ddLocale.t() translation function.

	let myTranslation = ddLocale.t("tool name");

Or use the extended String.t() function:

	let myTranslation = "tool name".t();
	let key = "tool name";
	myTranslation = key.t();

Nested values ​​can be accessed by using a period between the keys.

	let bye = "exit.goodbye".t();

This wil return "A presto __ __ __." because the values for the placeholders are missing. You can pass values for placeholders in 3 different ways (per attribute or array only works with sequential numbered placeholders, such as {0}):

	let byAttributes = ddLocale.t( "exit.goodbye", "Benicio", "del", "Toro" );
	let byArray = ddLocale.t( "exit.goodbye", ["Benicio", "del", "Toro"] );
	let byObject = ddLocale.t("exit.goodbye", {0:"Benicio", 1:"del", 2:"Toro"} );

You may use {names} instead of numbers {0} as placeholders when using the object notation. Fill empty values for placeholders with an empty string, like middlename in the second example below. Multiple spaces are reduced to one.

Note: If you plan to use the inline translation feature, it is important to use only lowercase letters for placeholder names, because the standard data-* attributes used for inline data storage do not support uppercase letters!

	let myName = "my name is " + "fullname".t( {"firstname":"Benicio", "middlename":"del", "lastname":"Toro"} );
	let myName = "my name is " + ddLocale.t("fullname", {firstname:"Benicio", middlename:"", lastname:"Toro, del"} );

To localize numbers and dates you can pass ddLocale.culture to the standard toLocaleString and/or toLocaleDateString.

	let dateString = new Date().toLocaleString(ddLocale.culture, {
		year: "numeric",
		month: "short",
		day: "numeric",
	});

To make things a bit easier and shorter, you can store your options in stringFormats (see Initialisation above) and then use the extended functions Number.t() and Date.t() with the option name you want to use.

	let dateString = new Date().t('shortDate');
	let currency = (12345.678).t('eur');

Or you can use ddLocale to pass a date or a number for localisation.

	let dateString = ddLocale.t(new Date(), 'shortDate');
	let currency = ddLocale.t(12345.678, 'eur');

Passing timestamps is allowed, but without specific date options it will be treated as a number.

	let dateString1 = ddLocale.t(1760565600000, 'shortDate');
	let dateString2 = (1760565600000).t('shortDate');

Similarly, you can pass the option name to display ordinals and/or bytes as long as the formatting option is present in stringFormats.

	let default = (1345236).t('ordinalValue')); → 1345236ste
	let ordinal1 = (1345236).t('ordinal')); → 1.345.236ste
	let ordinal2 = (300000).t('ordinal')); → 300 duizendste
	let word = (3).t('ordinalWord')); → three
	let short = (1345236).t('wordShort')); → 1 mln
	let alternative = (300000).t('ordinalAlt')); → 300.000ᵉ (availability depends on language)
	let zero = (0).t('ordinalZeroWord')); → 'none';
	let decimal = (1345236).t('ordinalDecimalWord')); → 1,35 miljoen
	let suffix = (345236).t('ordinalSuffix')); → ste
	let bytes = (1536000).t('bytes')); → 1 MB

You can fill placeholders with dates and numbers too, but you can only do this using arrays or objects, because you have to pass an additional array or object with special binders per placeholder. Dates can be passed as a Date object or as a timestamp.

  • Date binders start with a d or date for that matter, optionally followed by a | and the date option name from stringFormats.
  • Number binders start with a n, num or number, optionally followed by a | and the number option name from stringFormats.
	let bitcoins1 = ddLocale.t("bitcoins", {0:1760620872050, 1:2.58, 2:'bitcoins', 3:(2.58*95339.54)}, { 0:'d|shortDate', 1:'n', 3:'n|eur' });
	let bitcoins2 = "bitcoins".t([new Date(), 2.56, 'bitcoins', (2.56*95339.54)], ['date|shortDate', 'number', 'string', 'num|eur']);
	let ordinalTest = "ordinalTest".t({ 0: 12542334567, 1: 1345236, 2: 1536000 }, {0:'n|ordinalWord', 1:'n|ordinalSuffix', 2:'n|bytes'}));

Pluralization

The values ​​in the language file can be expanded with plurals in three ways.

  1. Simply add a key+value pair for both the singular and plural.
  2. Add ->= along with the plural form.
  3. Add ->, followed by - with the letter(s) you want to remove. Add + followed by everything else you want to add. Useful for adding single letters and for long words.
	{
		"car": "MG",
		"cars": "Nissan and Porsche",
		"cheese": "cheese->cheeses",
		"child": "child->+ren",
		"man": "man->-an+en",
	}

In normal use, these extensions are ignored. By sending a number, you can test whether that number results in singular or plural form in the selected language.

	let normal = 'One car is a ' + ddLocale.t("car") + 'two cars are ' + ddLocale.t("cars");
	let noCheese = 'No ' + 'cheese'.t(0);
	let oneCheese = 'One ' + 'cheese'.t(1);
	let twoCheese = 'Two ' + 'cheese'.t(2);
	let oneChild = (1).t('word') + ' ' + 'cheese'.t(1);
	let oneChild = (11).t('word') + ' ' + 'cheese'.t(11);
	let oneChild = ddLocale.t(1, 'word') + ' ' + ddLocale.t('cheese', 1);
	let man = ddLocale.t(1, 'word') + ' ' + ddLocale.t('man', 1);
	let men = ddLocale.t(100, 'word') + ' ' + ddLocale.t('man', 100);
	let plurals = "plurals".t({0: 2, 1: 'man', 2: 'bird' }, { 0:'n|ordinalWord', 1:2}));

Inline usage

Static elements can be translatable too.

The inline solution is more or less recursive, as long as the necessary placeholders are defined! Recursive values ​​deeper than one level haven't really been tested very intensively. With autoInline: true (default all inline t attributew will be translated automatically. You can force the inline translations with ddLocale.html() for the entire document or ddLocale.html(domId) for partial translations. All t attributes with de element with id=domid will be checked.

Place the key inside a t attribute and ddLocale will place the translation in the innerHTML.

	<h1 t="tool name"></h1>

Inline attributes for long phrases with placeholders or even recursive keys can be complex. The best practice is to retrieve the translation via scripting, including the attributes. You do this by appending the ddLocale.t() function with an additional option true. You will then receive an array with two values. The first is simply the result of the translation. The second value is an object containing all the necessary attribute names and their values. This allows you to provide the DOM element in question with attributes. With the option usePrototypes: true, the JavaScript Element is extended with the setAttributes() function, which can be fed with the returned object.

	let toolname = ddLocale.t("tool name"); → "ddLocale"
	let toolnameAndAttributes = ddLocale.t("tool name", true); → ["ddLocale", { t:"tool name" }];
	<h1 id="header"></h1>
	let h1 = document.getElementById('header');
	h1.setAttributes(toolnameAndAttributes[1]).innerHTML = toolnameAndAttributes[0];
	→ <h1 id="header" t="tool name">ddLocale</h1>

There is now also a shorter method. Use ddLocale.setAttributes('key', element|'elementId');. This function will place all the necessary attributes in element or the object with elementId and return the translation value.

	headerTitle.innerHTML = ddLocale.setAttributes(title, headerTitle);

If the inline key has placeholders the corrresponding values must be present in data-t_* attributes, where * is a corresponding placeholders.

It is important to use only lowercase letters for placeholder names, because the standard data-* attributes used for inline data storage do not support uppercase letters!

	<h1 t="hello" data-t_name="Master" data-t_lastname="Mek"></h1>

If the inline placeholders are themselves a key, you must specify an additional attribute by adding the placeholder names in the names attribute with an underscore _.

	<h1 t="hi" data-t_fullname="fullname" data-t_fullname_firstname="Benicio" data-t_fullname_middlename="del" data-t_fullname_lastname="Toro"></h1>

Of course this also works with numbered placeholders.

	<span t="exit.goodbye" data-t_0="Benicio" data-t_1="del" data-t_2="Toro"></span>

Dates must be passed as timestamps. Because timestamps are just numbers, you need to tell ddLocale what kind of number it is with binders. This is done with an additional attribute with the same attribute name, appended with an additional _ sign.

  • Date binders start with a d or date for that matter, optionally followed by a | and the date option name from stringFormats.
  • Number binders start with a n, num or number, optionally followed by a | and the number option name from stringFormats.
	<div t="1760565600000" data-t_="date"></div>
	<div t="176056.45" data-t_="num|eur"></div>
	<div t="ordinalTest" data-t_0="12542334567" data-t_1="1345236" data-t_2="1536000" data-t_0_="n|ordinalWord" data-t_1_="n|ordinalSuffix" data-t_2_="n|bytes"></div> 

Because inline translations are recursive, the result of a placeholder translation cannot be an existing key. To prevent the translation from being incorrectly translated further, you can explicitly pass the placeholder as a string.

  • String binders start with a s, str or string.
	<div t="bitcoins" data-t_0="1760565600000" data-t_0_="d|shortDate" data-t_1="2.56" data-t_1_="num" data-t_2="bitcoins" data-t_2_="s" data-t_3="244069.222" data-t_3_="n|eur"></div>

For pluralisation you can pass a number-string in the _ attribute.

	<div t="plurals" data-t_0="100" data-t_0_="n|ordinalWord" data-t_1="man" data-t_1_="100" data-t_2="bird"></div>

Dates and numbers

Dates and numbers can also be static, but only if any options are stored in stringFormats.

Numbers...

	<div id="container">
		<span t="1760565600000" data-t-num="eur"></span>
	</div>

For dates, the static representation must be a timestamp new Date().getTime() and the option name must be in the attribute data-t-date. The option name is cumpulory because with it the timestamp is just a number.

	<div id="container">
		<span t="1760565600000" data-t-date="shortDate"></span>
	</div>

Title-attributes

After the t attribute is translated, if the element in question also contains an emtpy t-title attribute, the title will also be populated with the translation.

	<div id="container">
		<span t="1760565600000" data-t-date="shortDate" t-title></span>
	</div>

If the t-title attribute is not empty, it's value will be translated non-recursively and placed into the title attribute.

	<div id="container">
		<span t="1760565600000" data-t-date="shortDate" t-title="currentdate"></span>
	</div>

Translation menu

To use the ddLocale menu you will need a placeholder in your HTML document.

	<div id="language"></div>

This is an example of what the built-in functional menu looks like after initialisation with domId="language" and menu.align=left. With css you can style the menu, using id, class and atrributes.

	<div id="language" culture="nl-NL" class="open">
		<a id="language-menu-button"></a>
		<div class="left" id="language-menu-container">
			<a culture="en">English</a>
			<a class="selected" culture="nl-NL">Nederlands</a>
		</div>
	</div>

Use the autoOpen and autoClose options to open and close the menu without clicking. These option values ​​are in milliseconds. The autoClose timing should be long enough, depending on the distance between the button and the popup menu, otherwise the menu will close before the mouse reaches menu.

To use ddLMenu add engine: 'ddMenu' to the menu settings, along with the addtional ddMenu options.

	menu: {
		domId: 'language',
		text: '', // long, short or translation key
		align: 'center', // className
		autoOpen: null,
		autoClose: 500,
		engine: 'ddMenu', // or false/null/not set
		className: 'language',
		multilingual: true, // use ddLocale
		updateTitleOnSelect: true,
		autoOpen: 250,
		autoClose: 500,
		animation: 100,
		badge: 0,
		extraHeight: 20, // necessary for some browsers
		triggerIcon: 'icon-user',  // CSS class for your icon
		iconPosition: 'right',
		change: (e, trigger) => {
			ddLocale.set({
				culture: trigger.item.value
			});
		},
		items: {
			iconPosition: 'right'
		}
	}

css

Below is a simple css example to style the menu. An important note about the main div with id="language" though. Because the popup menu is inside the main div with id="language", this main div must have an explicit absolute or relative position and the menu's container with id="language-menu-container" should be absolute!

#language {
	position: absolute;
	width: 16px;
	height: 16px;
	border: 0px solid #1D6F42;
	top: 25px;
	left: 50%;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	cursor: hand;
	cursor: pointer;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	box-shadow: 0 2px 2px rgba(0,0,0,0.5);
	z-index: 10;
}
#language[culture=nl-NL] {
	background-image: url('../images/flags/nl.svg');
}
#language[culture=en] {
	background-image: url('../images/flags/en.svg');
}
#language.open {}
#language-menu-container {
	position: absolute;
	background-color: white;
	z-index: 100;
	top: 100%;
	left: 0;
	transform: translate(0, 10px);
}
#language-menu-container.left {
	left: 0;
	transform: translate(0, 10px);
}
body[direction=rtl] #language-menu-container.left {
	left: 100%;
	transform: translate(-100%, 10px);
}
#language-menu-container.right
{
	left: 100%;
	transform: translate(-100%, 10px);
}
#language-menu-container.center {
	left: 50%;
	transform: translate(-50%, 10px);
}
#language-menu-button {
	display: inline-block;
	width: 100%;
	height: 100%;
}
#language-menu-container a {
	display: block;
	line-height: 16px;
	border-bottom: 1px solid black;
	padding: 8px 10px;
}
#language-menu-container.center a {
	text-align: center;
}
#language-menu-container a.selected {
	background-color: lightgrey;
	cursor: default;
	text-decoration: none;
	color: black;
}
#language-menu-container a:not(.selected):hover {
	background-color: grey;
	color: white;
}
#language-menu-container a:last-child {
	border-bottom: 0;
}

If you like ddLocale you may consider buying me a coffee.

Screenshot

Thank you for using ddLocale.

Mek

About

Very lightweight and easy to use JavaScript library for multilingual web apps and sites. Supports both script and inline translations for text, numbers, and dates from nestable JSON sources. Includes functionality for a fully customizable language menu.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published