mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
fix(i18n): a quantity under another name than count is a plural group too
The count-string gate only knew {count} and {n}, the params t() picks a
plural form by. A quantity named anything else kept one form in every
language and passed: Russian read "Все {days} дней" for 2 days and
"{objects} объектов" for 3 objects, Greek "{minutes} λεπτά" for 1 minute.
Twelve such en strings become plural groups with their forms in all 27
locales, the quantity renamed to {count}: the trip length limit, the
chat image limit, the API key limit, the storage move prompt and its
reclaimable line, the Dawarich wish rule and suggestion duration, the
road trip lateness warnings, and the road trip echo and window texts.
Their callers pass the count as a number; the storage panels used to
pass it as a string, which no form is picked by. The reclaimable line
loses its dash for a comma in every locale while it is rewritten.
i18n-parity now treats the quantity names in QUANTITY_PARAMS ({days},
{minutes}, {objects}, {max} and the like) like {count}. The four strings
where the number is followed by nothing but a unit symbol (the Dawarich
durations, the upload size limit) go on NOT_PLURAL with that reason.
This commit is contained in:
@@ -10,5 +10,6 @@ const a = {
|
||||
'a.quote': 'Es ist hier',
|
||||
'a.multi':
|
||||
'A long sentence that wraps onto the next line',
|
||||
'a.stay': '{days} Tage bleiben',
|
||||
};
|
||||
export default a;
|
||||
|
||||
@@ -10,5 +10,6 @@ const a = {
|
||||
'a.quote': "It's here",
|
||||
'a.multi':
|
||||
'A long sentence that wraps onto the next line',
|
||||
'a.stay': 'Stay {days} days',
|
||||
};
|
||||
export default a;
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
// `Intl.PluralRules`, through the same helpers the client resolves with.
|
||||
// 4. Count strings: an en string carrying {count} or {n} (the two params
|
||||
// `t()` picks a plural form by) must be a plural group, so every
|
||||
// language gets the forms it needs. The exceptions are NOT_PLURAL: a
|
||||
// number that is never a quantity ("Day {n}", a step, a multiplier).
|
||||
// language gets the forms it needs. So must one carrying a quantity
|
||||
// under another name ({days}, {minutes}, {objects}, QUANTITY_PARAMS),
|
||||
// which no form is picked by: it is renamed to {count}. The exceptions
|
||||
// are NOT_PLURAL: a number that is never a quantity ("Day {n}", a step,
|
||||
// a multiplier), or one followed only by a unit symbol.
|
||||
// 5. Untranslated strings: the marked `// en-fallback` and unmarked English
|
||||
// copies per locale and file may only shrink (i18n-untranslated.mjs,
|
||||
// which also lowers the baseline with --update).
|
||||
@@ -94,13 +97,37 @@ export const NOT_PLURAL = [
|
||||
{ key: 'system_notice.pager.goto', because: 'the position of a notice in the pager' },
|
||||
{ key: 'tours.addedToDay', because: 'the number of the day a tour was added to' },
|
||||
{ key: 'tours.planner.waypointLabel', because: 'the position of a waypoint on the tour' },
|
||||
{ key: 'dawarich.duration.minutes', because: 'a duration in a unit symbol (min), the same for any number' },
|
||||
{ key: 'dawarich.duration.hours', because: 'a duration in a unit symbol (h), the same for any number' },
|
||||
{ key: 'dawarich.duration.hoursMinutes', because: 'a duration in unit symbols (h, min), the same for any number' },
|
||||
{ key: 'files.uploadErrorSize', because: 'a size limit in a unit symbol (MB), the same for any number' },
|
||||
];
|
||||
|
||||
const COUNT_PARAM_RE = /\{(?:count|n)\}/;
|
||||
/**
|
||||
* Param names that hold a quantity of something but are not the ones `t()`
|
||||
* picks a plural form by. A string using one keeps a single form in every
|
||||
* language ("Все {days} дней" for 2 days), so it is held like {count}: rename
|
||||
* the param to {count} and make the string a plural group.
|
||||
*/
|
||||
export const QUANTITY_PARAMS = [
|
||||
'days',
|
||||
'nights',
|
||||
'weeks',
|
||||
'months',
|
||||
'years',
|
||||
'hours',
|
||||
'minutes',
|
||||
'seconds',
|
||||
'items',
|
||||
'objects',
|
||||
'max',
|
||||
];
|
||||
|
||||
const COUNT_PARAM_RE = new RegExp(`\\{(?:count|n|${QUANTITY_PARAMS.join('|')})\\}`);
|
||||
|
||||
/**
|
||||
* en strings carrying a count param outside any plural group, and NOT_PLURAL
|
||||
* entries that no longer name such a string.
|
||||
* en strings carrying a count or quantity param outside any plural group,
|
||||
* and NOT_PLURAL entries that no longer name such a string.
|
||||
*/
|
||||
function checkCountStrings(enFiles, notPlural = NOT_PLURAL, root = I18N_ROOT) {
|
||||
const allowed = new Set(notPlural.map((e) => e.key));
|
||||
@@ -233,9 +260,11 @@ function formatReport(report) {
|
||||
if (ungrouped.length === 0 && stale.length === 0) {
|
||||
lines.push('Count strings: OK');
|
||||
} else {
|
||||
lines.push('Count strings: en strings with {count} or {n} that are no plural group');
|
||||
lines.push('Count strings: en strings with {count}, {n} or a quantity param that are no plural group');
|
||||
for (const { file, key } of ungrouped) {
|
||||
lines.push(` en/${file}: ${key} (add ${key}.one and the other forms, see shared/CLAUDE.md)`);
|
||||
lines.push(
|
||||
` en/${file}: ${key} (name the quantity {count} and add ${key}.one and the other forms, see shared/CLAUDE.md)`,
|
||||
);
|
||||
}
|
||||
for (const key of stale) lines.push(` NOT_PLURAL lists ${key}, which is no ungrouped count string: remove it`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user