From 81c35b4d95a0da4d2b104aa84ba5de93d0d94d1d Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 31 Mar 2010 12:54:38 +0200 Subject: add Plural-Forms header in PO writer --- tests/auto/linguist/lconvert/data/makeplurals.pl | 9 ++-- .../auto/linguist/lconvert/data/test-kde-fuzzy.po | 2 +- .../linguist/lconvert/data/test-kde-multiline.po | 2 +- .../linguist/lconvert/data/test-kde-plurals.po | 2 +- tests/auto/linguist/lconvert/data/test1-de.po | 2 +- tools/linguist/linguist/messagemodel.cpp | 2 +- tools/linguist/shared/numerus.cpp | 57 ++++++++++++++-------- tools/linguist/shared/po.cpp | 24 ++++++++- tools/linguist/shared/qm.cpp | 4 +- tools/linguist/shared/translator.cpp | 2 +- tools/linguist/shared/translator.h | 2 +- 11 files changed, 74 insertions(+), 34 deletions(-) diff --git a/tests/auto/linguist/lconvert/data/makeplurals.pl b/tests/auto/linguist/lconvert/data/makeplurals.pl index d933a3e..9707ef0 100755 --- a/tests/auto/linguist/lconvert/data/makeplurals.pl +++ b/tests/auto/linguist/lconvert/data/makeplurals.pl @@ -57,7 +57,7 @@ sub makeit2($$$) } } -sub makeit($$) +sub makeit($$$) { open OUTFILE, ">${OUTDIR}plural-$_[0].po" || die "cannot write file in $OUTDIR"; print OUTFILE <=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/tests/auto/linguist/lconvert/data/test-kde-fuzzy.po b/tests/auto/linguist/lconvert/data/test-kde-fuzzy.po index b3f6e03..fc9ae77 100644 --- a/tests/auto/linguist/lconvert/data/test-kde-fuzzy.po +++ b/tests/auto/linguist/lconvert/data/test-kde-fuzzy.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KAider 0.1\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: kgverify.cpp:459 #, fuzzy, kde-format diff --git a/tests/auto/linguist/lconvert/data/test-kde-multiline.po b/tests/auto/linguist/lconvert/data/test-kde-multiline.po index 0ca714c..662c02e 100644 --- a/tests/auto/linguist/lconvert/data/test-kde-multiline.po +++ b/tests/auto/linguist/lconvert/data/test-kde-multiline.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KAider 0.1\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: kdmshutdown.cpp:706 #, kde-format diff --git a/tests/auto/linguist/lconvert/data/test-kde-plurals.po b/tests/auto/linguist/lconvert/data/test-kde-plurals.po index 6c85d74..9f74de0 100644 --- a/tests/auto/linguist/lconvert/data/test-kde-plurals.po +++ b/tests/auto/linguist/lconvert/data/test-kde-plurals.po @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KAider 0.1\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: de_DE\n" #: kgverify.cpp:505 diff --git a/tests/auto/linguist/lconvert/data/test1-de.po b/tests/auto/linguist/lconvert/data/test1-de.po index 256b8e9..a4523bb 100644 --- a/tests/auto/linguist/lconvert/data/test1-de.po +++ b/tests/auto/linguist/lconvert/data/test1-de.po @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KAider 0.1\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Language: de_DE\n" #: lib/acl.c:107 lib/acl.c:121 lib/acl.c:138 lib/acl.c:165 lib/acl.c:174 diff --git a/tools/linguist/linguist/messagemodel.cpp b/tools/linguist/linguist/messagemodel.cpp index 4e2b473..39ba9fd 100644 --- a/tools/linguist/linguist/messagemodel.cpp +++ b/tools/linguist/linguist/messagemodel.cpp @@ -402,7 +402,7 @@ bool DataModel::setLanguageAndCountry(QLocale::Language lang, QLocale::Country c if (lang == QLocale::C || uint(lang) > uint(QLocale::LastLanguage)) // XXX does this make any sense? lang = QLocale::English; QByteArray rules; - bool ok = getNumerusInfo(lang, country, &rules, &m_numerusForms); + bool ok = getNumerusInfo(lang, country, &rules, &m_numerusForms, 0); m_localizedLanguage = QCoreApplication::translate("MessageEditor", QLocale::languageToString(lang).toAscii()); m_countRefNeeds.clear(); for (int i = 0; i < rules.size(); ++i) { diff --git a/tools/linguist/shared/numerus.cpp b/tools/linguist/shared/numerus.cpp index 287dd03..c002119 100644 --- a/tools/linguist/shared/numerus.cpp +++ b/tools/linguist/shared/numerus.cpp @@ -318,28 +318,45 @@ struct NumerusTableEntry { const char * const *forms; const QLocale::Language *languages; const QLocale::Country *countries; + const char * const gettextRules; }; static const NumerusTableEntry numerusTable[] = { - { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0 }, - { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0 }, + { 0, 0, japaneseStyleForms, japaneseStyleLanguages, 0, "nplurals=1; plural=0;" }, + { englishStyleRules, sizeof(englishStyleRules), englishStyleForms, englishStyleLanguages, 0, + "nplurals=2; plural=(n != 1);" }, { frenchStyleRules, sizeof(frenchStyleRules), frenchStyleForms, frenchStyleLanguages, - frenchStyleCountries }, - { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0 }, - { icelandicRules, sizeof(icelandicRules), icelandicForms, icelandicLanguage, 0 }, - { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0 }, - { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguages, 0 }, - { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0 }, - { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0 }, - { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0 }, - { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0 }, - { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0 }, - { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0 }, - { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0 }, - { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0 }, - { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0 }, - { tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0 }, - { catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0 } + frenchStyleCountries, "nplurals=2; plural=(n > 1);" }, + { latvianRules, sizeof(latvianRules), latvianForms, latvianLanguage, 0, + "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" }, + { icelandicRules, sizeof(icelandicRules), icelandicForms, icelandicLanguage, 0, + "nplurals=2; plural=(n%10==1 && n%100!=11 ? 0 : 1);" }, + { irishStyleRules, sizeof(irishStyleRules), irishStyleForms, irishStyleLanguages, 0, + "nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);" }, + { slovakRules, sizeof(slovakRules), slovakForms, slovakLanguages, 0, + "nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);" }, + { macedonianRules, sizeof(macedonianRules), macedonianForms, macedonianLanguage, 0, + "nplurals=3; plural=(n%100==1 ? 0 : n%100==2 ? 1 : 2);" }, + { lithuanianRules, sizeof(lithuanianRules), lithuanianForms, lithuanianLanguage, 0, + "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" }, + { russianStyleRules, sizeof(russianStyleRules), russianStyleForms, russianStyleLanguages, 0, + "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, + { polishRules, sizeof(polishRules), polishForms, polishLanguage, 0, + "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, + { romanianRules, sizeof(romanianRules), romanianForms, romanianLanguages, 0, + "nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);" }, + { slovenianRules, sizeof(slovenianRules), slovenianForms, slovenianLanguage, 0, + "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" }, + { malteseRules, sizeof(malteseRules), malteseForms, malteseLanguage, 0, + "nplurals=4; plural=(n==1 ? 0 : (n==0 || (n%100>=1 && n%100<=10)) ? 1 : (n%100>=11 && n%100<=19) ? 2 : 3);" }, + { welshRules, sizeof(welshRules), welshForms, welshLanguage, 0, + "nplurals=5; plural=(n==0 ? 0 : n==1 ? 1 : (n>=2 && n<=5) ? 2 : n==6 ? 3 : 4);" }, + { arabicRules, sizeof(arabicRules), arabicForms, arabicLanguage, 0, + "nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : (n%100>=3 && n%100<=10) ? 3 : n%100>=11 ? 4 : 5);" }, + { tagalogRules, sizeof(tagalogRules), tagalogForms, tagalogLanguage, 0, + "nplurals=3; plural=(n==1 ? 0 : (n%10==4 || n%10==6 || n%10== 9) ? 1 : 2);" }, + { catalanRules, sizeof(catalanRules), catalanForms, catalanLanguage, 0, + "nplurals=3; plural=(n==1 ? 0 : (n==11 || n/1000==11 || n/1000000==11 || n/1000000000==11) ? 1 : 2);" }, }; static const int NumerusTableSize = sizeof(numerusTable) / sizeof(numerusTable[0]); @@ -352,7 +369,7 @@ static const uchar magic[MagicLength] = { }; bool getNumerusInfo(QLocale::Language language, QLocale::Country country, - QByteArray *rules, QStringList *forms) + QByteArray *rules, QStringList *forms, const char **gettextRules) { while (true) { for (int i = 0; i < NumerusTableSize; ++i) { @@ -365,6 +382,8 @@ bool getNumerusInfo(QLocale::Language language, QLocale::Country country, *rules = QByteArray::fromRawData(reinterpret_cast(entry.rules), entry.rulesSize); } + if (gettextRules) + *gettextRules = entry.gettextRules; if (forms) { forms->clear(); for (int k = 0; entry.forms[k]; ++k) diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp index 2895143..ab24e15 100644 --- a/tools/linguist/shared/po.cpp +++ b/tools/linguist/shared/po.cpp @@ -412,6 +412,7 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd) } if (item.msgId.isEmpty()) { QStringList hdrOrder; + QString pluralForms; foreach (const QString &hdr, item.msgStr.first().split(QLatin1Char('\n'), QString::SkipEmptyParts)) { int idx = hdr.indexOf(QLatin1Char(':')); @@ -428,14 +429,26 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd) translator.setLanguageCode(hdrValue); else if (hdrName == QLatin1String("X-Source-Language")) translator.setSourceLanguageCode(hdrValue); + else if (hdrName == QLatin1String("Plural-Forms")) + pluralForms = hdrValue; else if (hdrName == QLatin1String("X-Virgin-Header")) ; // legacy else translator.setExtra(makePoHeader(hdrName), hdrValue); } + if (!pluralForms.isEmpty()) { + if (translator.languageCode().isEmpty()) { + translator.setExtra(makePoHeader(QLatin1String("Plural-Forms")), + pluralForms); + } else { + // FIXME: have fun with making a consistency check ... + } + } // Eliminate the field if only headers we added are present in standard order. // Keep in sync with savePO - static const char * const dfltHdrs[] = { "X-Language", "X-Source-Language" }; + static const char * const dfltHdrs[] = { + "Plural-Forms", "X-Language", "X-Source-Language" + }; uint cdh = 0; for (int cho = 0; cho < hdrOrder.length(); cho++) { for (;; cdh++) { @@ -608,8 +621,15 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd) QStringList hdrOrder = translator.extra(QLatin1String("po-headers")).split( QLatin1Char(','), QString::SkipEmptyParts); // Keep in sync with loadPO - if (!translator.languageCode().isEmpty()) + if (!translator.languageCode().isEmpty()) { + QLocale::Language l; + QLocale::Country c; + Translator::languageAndCountry(translator.languageCode(), &l, &c); + const char *gettextRules; + if (getNumerusInfo(l, c, 0, 0, &gettextRules)) + addPoHeader(headers, hdrOrder, "Plural-Forms", QLatin1String(gettextRules)); addPoHeader(headers, hdrOrder, "X-Language", translator.languageCode()); + } if (!translator.sourceLanguageCode().isEmpty()) addPoHeader(headers, hdrOrder, "X-Source-Language", translator.sourceLanguageCode()); QString hdrStr; diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp index de1284f..e2c4f4a 100644 --- a/tools/linguist/shared/qm.cpp +++ b/tools/linguist/shared/qm.cpp @@ -564,7 +564,7 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) Translator::languageAndCountry(translator.languageCode(), &l, &c); QStringList numerusForms; bool guessPlurals = true; - if (getNumerusInfo(l, c, 0, &numerusForms)) + if (getNumerusInfo(l, c, 0, &numerusForms, 0)) guessPlurals = (numerusForms.count() == 1); QString context, contextUtf8; @@ -704,7 +704,7 @@ static bool saveQM(const Translator &translator, QIODevice &dev, ConversionData QLocale::Country c; Translator::languageAndCountry(translator.languageCode(), &l, &c); QByteArray rules; - if (getNumerusInfo(l, c, &rules, 0)) + if (getNumerusInfo(l, c, &rules, 0, 0)) releaser.setNumerusRules(rules); releaser.setCodecName(translator.codecName()); diff --git a/tools/linguist/shared/translator.cpp b/tools/linguist/shared/translator.cpp index 4331ce6..465355d 100644 --- a/tools/linguist/shared/translator.cpp +++ b/tools/linguist/shared/translator.cpp @@ -652,7 +652,7 @@ void Translator::normalizeTranslations(ConversionData &cd) int numPlurals = 1; if (l != QLocale::C) { QStringList forms; - if (getNumerusInfo(l, c, 0, &forms)) + if (getNumerusInfo(l, c, 0, &forms, 0)) numPlurals = forms.count(); // includes singular } for (int i = 0; i < m_messages.count(); ++i) { diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h index 0b88c07..bb199f0 100644 --- a/tools/linguist/shared/translator.h +++ b/tools/linguist/shared/translator.h @@ -233,7 +233,7 @@ private: }; bool getNumerusInfo(QLocale::Language language, QLocale::Country country, - QByteArray *rules, QStringList *forms); + QByteArray *rules, QStringList *forms, const char **gettextRules); /* This is a quick hack. The proper way to handle this would be -- cgit v0.12