Add BMessageFormat class.

This can be used to format complex messages properly. It moves the
complexity of handling plural forms, gender, and anything else needed
into the localizable string, rather than hardcoding it in the code.

This moves the difficulty of handling these things properly to people
doing translations, rather than relying on developers to do it.

Fixes #10755, but our localization must now be updated to make use of
the feature.
This commit is contained in:
Adrien Destugues
2014-10-07 08:51:23 +02:00
parent 699ddf447c
commit 0da7796e6c
7 changed files with 164 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2014, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_MESSAGE_FORMAT_H_
#define _B_MESSAGE_FORMAT_H_
#include <Format.h>
class BMessageFormat: public BFormat {
public:
status_t Format(BString& buffer, const BString message,
const int32 arg);
};
#endif
+1
View File
@@ -27,6 +27,7 @@ local sources =
DateFormat.cpp
DateTimeFormat.cpp
DurationFormat.cpp
MessageFormat.cpp
NumberFormat.cpp
TimeFormat.cpp
TimeUnitFormat.cpp
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright 2014, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include <MessageFormat.h>
#include <FormattingConventionsPrivate.h>
#include <LanguagePrivate.h>
#include <ICUWrapper.h>
#include <unicode/msgfmt.h>
status_t
BMessageFormat::Format(BString& output, const BString message, const int32 arg)
{
UnicodeString buffer;
UErrorCode error = U_ZERO_ERROR;
Formattable arguments[] = {
(int32_t)arg
};
Locale* icuLocale
= fConventions.UseStringsFromPreferredLanguage()
? BLanguage::Private(&fLanguage).ICULocale()
: BFormattingConventions::Private(&fConventions).ICULocale();
MessageFormat formatter(UnicodeString::fromUTF8(message.String()),
*icuLocale, error);
FieldPosition pos;
buffer = formatter.format(arguments, 1, buffer, pos, error);
if (!U_SUCCESS(error))
return B_ERROR;
BStringByteSink byteSink(&output);
buffer.toUTF8(byteSink);
return B_OK;
}
+1
View File
@@ -52,6 +52,7 @@ UnitTestLib localekittest.so :
DateFormatTest.cpp
DurationFormatTest.cpp
LanguageTest.cpp
MessageFormatTest.cpp
UnicodeCharTest.cpp
: be [ TargetLibstdc++ ]
@@ -11,6 +11,7 @@
#include "DateFormatTest.h"
#include "DurationFormatTest.h"
#include "LanguageTest.h"
#include "MessageFormatTest.h"
#include "UnicodeCharTest.h"
@@ -23,6 +24,7 @@ getTestSuite()
DateFormatTest::AddTests(*suite);
DurationFormatTest::AddTests(*suite);
LanguageTest::AddTests(*suite);
MessageFormatTest::AddTests(*suite);
UnicodeCharTest::AddTests(*suite);
return suite;
@@ -0,0 +1,76 @@
/*
* Copyright 2014 Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include "MessageFormatTest.h"
#include <Locale.h>
#include <MessageFormat.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestSuite.h>
MessageFormatTest::MessageFormatTest()
{
}
MessageFormatTest::~MessageFormatTest()
{
}
void
MessageFormatTest::TestFormat()
{
BString output;
BMessageFormat formatter;
struct Test {
const char* locale;
const char* pattern;
int32 number;
const char* expected;
};
static const char* polishTemplate = "{0, plural, one{Wybrano # obiekt} "
"few{Wybrano # obiekty} many{Wybrano # obiektów} "
"other{Wybrano # obyektu}}";
static const Test tests[] = {
{"en_US", "{0, plural, one{# dog} other{# dogs}}", 1, "1 dog"},
{"en_US", "{0, plural, one{# dog} other{# dogs}}", 2, "2 dogs"},
{"pl_PL", polishTemplate, 1, "Wybrano 1 obiekt"},
{"pl_PL", polishTemplate, 3, "Wybrano 3 obyektu"},
{"pl_PL", polishTemplate, 5, "Wybrano 5 obyektu"},
{"pl_PL", polishTemplate, 23, "Wybrano 23 obyektu"},
{NULL, NULL, 0, NULL}
};
for (int i = 0; tests[i].pattern != NULL; i++) {
status_t result;
NextSubTest();
output.Truncate(0);
BLanguage language(tests[i].locale);
formatter.SetLanguage(language);
result = formatter.Format(output, tests[i].pattern, tests[i].number);
CPPUNIT_ASSERT_EQUAL(B_OK, result);
CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected), output);
}
}
/*static*/ void
MessageFormatTest::AddTests(BTestSuite& parent)
{
CppUnit::TestSuite& suite = *new CppUnit::TestSuite("MessageFormatTest");
suite.addTest(new CppUnit::TestCaller<MessageFormatTest>(
"MessageFormatTest::TestFormat", &MessageFormatTest::TestFormat));
parent.addTest("MessageFormatTest", &suite);
}
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright 2014 Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#ifndef MESSAGE_FORMAT_TEST_H
#define MESSAGE_FORMAT_TEST_H
#include <TestCase.h>
#include <TestSuite.h>
class MessageFormatTest: public BTestCase {
public:
MessageFormatTest();
virtual ~MessageFormatTest();
void TestFormat();
static void AddTests(BTestSuite& suite);
};
#endif