Click here to Skip to main content
15,885,782 members
Articles / Programming Languages / C

Plural Forms

Rate me:
Please Sign up or sign in to vote.
4.69/5 (12 votes)
15 Apr 2008LGPL32 min read 35.8K   93   15  
Spelling messages like "5 file(s) found" correctly in any language
// (c) Peter Kankowski, 2008. http://smallcode.weblogs.us kankowski@narod.ru

#include "plurals.h"
#include <stdio.h>
#include <assert.h>

#define NUM_OF(A) (sizeof(A)/sizeof((A)[0]))

// Code for comparison from http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
unsigned Japanese(unsigned n) {	n=n; return 0;	}
unsigned English(unsigned n) {	return n != 1;	}
unsigned French(unsigned n) {	return n > 1;	}
unsigned Latvian(unsigned n) {	return n%10==1 && n%100!=11 ? 0 : n == 0 ? 1 : 2;	}
unsigned Irish(unsigned n) {	return n==1 ? 0 : n==2 ? 1 : 2;	}
unsigned Romanian(unsigned n) {	return n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;	}
unsigned Lithuanian(unsigned n) {	return n%10==1 && n%100!=11 ? 0 :
                     					   n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;	}
unsigned Russian(unsigned n) {	return n%10==1 && n%100!=11 ? 0 : \
                     				   n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;	}
unsigned Czech(unsigned n) {	return (n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;	}
unsigned Polish(unsigned n) {	return n==1 ? 0 :
									   n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;	}
unsigned Slovenian(unsigned n) {	return n%100==1 ? 0 : n%100==2 ? 1 :
										   n%100==3 || n%100==4 ? 2 : 3;	}

unsigned nplurals[] = {1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4};

const char* strings[] = {
	"", // jp
	"1", // en
	"0 1", // fr
	"1 1 10 t; 0 0", // lv
	"1; 2", // gd
	"z=1; 1; 1 19 100", // ro
	"1 1 10 t; 2 9 10 t", // lt
	"1 1 10 t; 2 4 10 t", // ru
	"1; 2 4", // cs
	"1; 2 4 10 t", // pl
	"1 1 100; 2 2 100; 3 4 100", // sl
};

unsigned (*const funcs[])(unsigned) = {
	Japanese, English, French, Latvian, Irish, Romanian,
	Lithuanian, Russian, Czech, Polish, Slovenian,
};

void TestCompareWithGetText(void) {
	assert( NUM_OF(funcs) == NUM_OF(strings) );
	assert( NUM_OF(nplurals) == NUM_OF(funcs) );
	for(int i = 0; i < NUM_OF(funcs); i++) {
		PLURAL_INFO plurals;
		unsigned count = PluralsReadCfg(&plurals, strings[i]);
		if(count != nplurals[i])
			printf("Wrong count for %s: expected %d, got %d\n",
				strings[i], nplurals[i], count);
		for(int n = 0; n < 1000; n++)
			if(funcs[i](n) != PluralsGetForm(&plurals, n))
				printf("Error: %d for %s: expected %d, got %d\n",
					n, strings[i], funcs[i](n), PluralsGetForm(&plurals, n));
	}
}

void TestReadCfg(void) {
	PLURAL_INFO plurals;
	assert( PluralsReadCfg(&plurals, "xyz") == 1);
	assert( PluralsReadCfg(&plurals, ";") == 1);

	assert( PluralsReadCfg(&plurals, "1 2;") == 2);
	assert(plurals.form[0].start == 1);
	assert(plurals.form[0].finish == 2);
	assert(plurals.form[0].modulo == 0);
	assert(plurals.form[0].skip_teens == 0);

	assert( PluralsReadCfg(&plurals, "1 2 0;3 4 0  sdf") == 3);
	assert(plurals.form[0].start == 1);
	assert(plurals.form[0].finish == 2);
	assert(plurals.form[0].modulo == 0);
	assert(!plurals.form[0].skip_teens);
	assert(plurals.form[1].start == 3);
	assert(plurals.form[1].finish == 4);
	assert(plurals.form[1].modulo == 0);
	assert(plurals.form[1].skip_teens);

	assert( PluralsReadCfg(&plurals, ";xyz") == 1);
	assert( PluralsReadCfg(&plurals, "!1 a;xyz") == 1);

	assert( PluralsReadCfg(&plurals, " z = 1 23 sdf ; 1 2 10 ; ^% 12 ad ; 10 20 0 2") == 3);
	assert(plurals.form[0].start == 1);
	assert(plurals.form[0].finish == 2);
	assert(plurals.form[0].modulo == 10);
	assert(plurals.form[0].skip_teens == 0);
	assert(plurals.form[1].start == 10);
	assert(plurals.form[1].finish == 20);
	assert(plurals.form[1].modulo == 0);
	assert(plurals.form[1].skip_teens);
	
	assert( PluralsReadCfg(&plurals, " z = 1 23 sdf") == 1);
}

void Demo(void) {
	static const char* file_plurals[] = {"plik", "pliki", "plik\'ow"};
	PLURAL_INFO plurals;
	PluralsReadCfg(&plurals, "1; 2 4 10 t");
	for(int i = 0; i < 32; i++)
		printf("%2d %s\n", i, file_plurals[ PluralsGetForm(&plurals, i) ]);
}

int main(void) {
	TestCompareWithGetText();
	TestReadCfg();
	Demo();
	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Czech Republic Czech Republic
Peter is the developer of Aba Search and Replace, a tool for replacing text in multiple files. He likes to program in C with a bit of C++, also in x86 assembly language, Python, and PHP.

Comments and Discussions