Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added count param to phrase param and return plural form accordingly #48229

Merged
merged 48 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
736b250
update TranslationBase and make few changes
ZhenjaHorbach Aug 28, 2024
9d42d55
fix conflicts
ZhenjaHorbach Aug 28, 2024
e830bb5
update TranslationBase
ZhenjaHorbach Aug 28, 2024
09f51d4
update translations PART-1
ZhenjaHorbach Aug 29, 2024
a237886
update translations PART-2
ZhenjaHorbach Aug 29, 2024
d0bf65b
update translations PART-3
ZhenjaHorbach Aug 29, 2024
41f0334
update translations PART-4
ZhenjaHorbach Aug 29, 2024
078a5ad
Merge branch 'main' into pluralizing_localization-v2
ZhenjaHorbach Aug 31, 2024
b70f5e4
update TranslationBase type
ZhenjaHorbach Aug 31, 2024
f31c7f2
fix conflicts
ZhenjaHorbach Sep 3, 2024
af67cd0
fix conflicts
ZhenjaHorbach Sep 3, 2024
eab923d
fix conflicts
ZhenjaHorbach Sep 5, 2024
5d1a70c
update new translations
ZhenjaHorbach Sep 5, 2024
41a3199
update TranslationBaseValue
ZhenjaHorbach Sep 5, 2024
8ebf751
revert some changes
ZhenjaHorbach Sep 5, 2024
273c8e1
revert some changes
ZhenjaHorbach Sep 5, 2024
d63aa0d
update type arguments
ZhenjaHorbach Sep 5, 2024
4b7eb6d
fix conflicts
ZhenjaHorbach Sep 9, 2024
57d8b7f
fix ts issues
ZhenjaHorbach Sep 9, 2024
83481bf
fix ts issue with no string values
ZhenjaHorbach Sep 11, 2024
554611c
fix conflicts
ZhenjaHorbach Sep 11, 2024
7c24bb4
update types for translations
ZhenjaHorbach Sep 11, 2024
d3028b6
refactor TranslationBase
ZhenjaHorbach Sep 11, 2024
100feae
refactor some code
ZhenjaHorbach Sep 11, 2024
87fd61d
fix conflicts
ZhenjaHorbach Sep 12, 2024
101fd03
refactor types for translations
ZhenjaHorbach Sep 12, 2024
90af317
update types
ZhenjaHorbach Sep 12, 2024
362c264
fix issue with no object values
ZhenjaHorbach Sep 12, 2024
8f58c9e
fix ts issue in localize
ZhenjaHorbach Sep 12, 2024
04c8f76
fix comments
ZhenjaHorbach Sep 12, 2024
bd485f9
fix conflicts
ZhenjaHorbach Sep 13, 2024
5d73e02
fix conflicts
ZhenjaHorbach Sep 14, 2024
0002662
fix conflicts
ZhenjaHorbach Sep 17, 2024
4e0d494
fix conflicts
ZhenjaHorbach Sep 18, 2024
2b84c98
fix conflicts
ZhenjaHorbach Sep 19, 2024
2175302
update package-lock
ZhenjaHorbach Sep 19, 2024
d7f49e5
update package-lock x2
ZhenjaHorbach Sep 19, 2024
090af99
fix comments
ZhenjaHorbach Sep 19, 2024
e278f17
fix conflicts
ZhenjaHorbach Sep 19, 2024
be492c0
fix conflicts
ZhenjaHorbach Sep 20, 2024
94d170b
fix conflicts
ZhenjaHorbach Sep 24, 2024
ffcd48e
update branch
ZhenjaHorbach Sep 25, 2024
480bdda
fix some comments
ZhenjaHorbach Sep 25, 2024
1ebfb54
update readme
ZhenjaHorbach Sep 25, 2024
27b7056
update readme x2
ZhenjaHorbach Sep 25, 2024
7638a4c
update localize comment
ZhenjaHorbach Sep 25, 2024
12b3b41
fix comments
ZhenjaHorbach Sep 26, 2024
a36a304
refactor plural forms
ZhenjaHorbach Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,30 @@ Some pointers:
key to the translation file and use the arrow function version, like so:
`nameOfTheKey: ({amount, dateTime}) => "User has sent " + amount + " to you on " + dateTime,`.
This is because the order of the phrases might vary from one language to another.

- When working with translations that involve plural forms, it's important to handle different cases correctly.

For example:
- zero: Used when there are no items **(optional)**.
- one: Used when there's exactly one item.
- two: Used when there's two items. **(optional)**
- few: Used for a small number of items **(optional)**.
- many: Used for larger quantities **(optional)**.
- other: A catch-all case for other counts or variations.

Here’s an example of how to implement plural translations:

messages: () => ({
zero: 'No messages',
one: 'One message',
two: 'Two messages',
few: (count) => `${count} messages`,
many: (count) => `You have ${count} messages`,
other: (count) => `You have ${count} unread messages`,
})

Comment on lines +634 to +642
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh crap, did not really notice this before... does the object containing the messages always need to be a function or can it also be:

Suggested change
messages: () => ({
zero: 'No messages',
one: 'One message',
two: 'Two messages',
few: (count) => `${count} messages`,
many: (count) => `You have ${count} messages`,
other: (count) => `You have ${count} unread messages`,
})
messages: {
zero: 'No messages',
one: 'One message',
two: 'Two messages',
few: (count) => `${count} messages`,
many: (count) => `You have ${count} messages`,
other: (count) => `You have ${count} unread messages`,
}

There's really no need for a function there...

Copy link
Contributor

@shubham1206agra shubham1206agra Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, since it will be impossible to differentiate between messages and messages.one translation key in a flat object.

Copy link
Contributor Author

@ZhenjaHorbach ZhenjaHorbach Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation looks like any other object with translations
And not a specific translation with plural forms

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, since it will be impossible to differentiate between messages and messages.one translation key in a flat object.

I don't get this

This implementation looks like any other object with translations
And not a specific translation with plural forms

Yeah, so?

Anyway, it would be nice if we supported the simple object notation too, but not going to block on that since this is a big PR, we've been waiting a long time for it and adding the function is not that bad.

In your code, you can use the translation like this:

`translate('common.messages', {count: 1});`
----

# Deploying
Expand Down
2 changes: 1 addition & 1 deletion src/components/AccountSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function AccountSwitcher() {
const error = ErrorUtils.getLatestErrorField({errorFields}, 'connect');
const personalDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email);
return createBaseMenuItem(personalDetails, error, {
badgeText: translate('delegate.role', role),
badgeText: translate('delegate.role', {role}),
onPress: () => {
if (isOffline) {
Modal.close(() => setShouldShowOfflineModal(true));
Expand Down
4 changes: 2 additions & 2 deletions src/components/AccountingConnectionConfirmationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ function AccountingConnectionConfirmationModal({integrationToConnect, onCancel,

return (
<ConfirmModal
title={translate('workspace.accounting.connectTitle', integrationToConnect)}
title={translate('workspace.accounting.connectTitle', {connectionName: integrationToConnect})}
isVisible
onConfirm={onConfirm}
onCancel={onCancel}
prompt={translate('workspace.accounting.connectPrompt', integrationToConnect)}
prompt={translate('workspace.accounting.connectPrompt', {connectionName: integrationToConnect})}
confirmText={translate('workspace.accounting.setup')}
cancelText={translate('common.cancel')}
success
Expand Down
2 changes: 1 addition & 1 deletion src/components/AddressForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function AddressForm({
if (countrySpecificZipRegex) {
if (!countrySpecificZipRegex.test(values.zipPostCode?.trim().toUpperCase())) {
if (ValidationUtils.isRequiredFulfilled(values.zipPostCode?.trim())) {
errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat', countryZipFormat);
errors.zipPostCode = translate('privatePersonalDetails.error.incorrectZipFormat', {zipFormat: countryZipFormat});
} else {
errors.zipPostCode = translate('common.error.fieldRequired');
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/HeaderWithBackButton/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {StyleProp, ViewStyle} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {PopoverMenuItem} from '@components/PopoverMenu';
import type {Action} from '@hooks/useSingleExecution';
import type {StepCounterParams} from '@src/languages/types';
import type {StepCounterParams} from '@src/languages/params';
import type {AnchorPosition} from '@src/styles';
import type {Policy, Report} from '@src/types/onyx';
import type {Icon} from '@src/types/onyx/OnyxCommon';
Expand Down
2 changes: 1 addition & 1 deletion src/components/ImportColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function ImportColumn({column, columnName, columnRoles, columnIndex}: ImportColu
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we don't want this effect to run again
}, []);

const columnHeader = containsHeader ? column[0] : translate('spreadsheet.column', columnName);
const columnHeader = containsHeader ? column[0] : translate('spreadsheet.column', {name: columnName});

return (
<View style={[styles.importColumnCard, styles.mt4]}>
Expand Down
8 changes: 4 additions & 4 deletions src/components/LocaleContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as LocalePhoneNumber from '@libs/LocalePhoneNumber';
import * as Localize from '@libs/Localize';
import * as NumberFormatUtils from '@libs/NumberFormatUtils';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type {TranslationParameters, TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {WithCurrentUserPersonalDetailsProps} from './withCurrentUserPersonalDetails';
import withCurrentUserPersonalDetails from './withCurrentUserPersonalDetails';
Expand All @@ -28,7 +28,7 @@ type LocaleContextProviderProps = LocaleContextProviderOnyxProps &

type LocaleContextProps = {
/** Returns translated string for given locale and phrase */
translate: <TKey extends TranslationPaths>(phraseKey: TKey, ...phraseParameters: Localize.PhraseParameters<Localize.Phrase<TKey>>) => string;
translate: <TPath extends TranslationPaths>(path: TPath, ...parameters: TranslationParameters<TPath>) => string;

/** Formats number formatted according to locale and options */
numberFormat: (number: number, options?: Intl.NumberFormatOptions) => string;
Expand Down Expand Up @@ -79,8 +79,8 @@ function LocaleContextProvider({preferredLocale, currentUserPersonalDetails, chi

const translate = useMemo<LocaleContextProps['translate']>(
() =>
(phraseKey, ...phraseParameters) =>
Localize.translate(locale, phraseKey, ...phraseParameters),
(path, ...parameters) =>
Localize.translate(locale, path, ...parameters),
[locale],
);

Expand Down
2 changes: 1 addition & 1 deletion src/components/ParentNavigationSubtitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import CONST from '@src/CONST';
import type {ParentNavigationSummaryParams} from '@src/languages/types';
import type {ParentNavigationSummaryParams} from '@src/languages/params';
import ROUTES from '@src/ROUTES';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
import Text from './Text';
Expand Down
2 changes: 1 addition & 1 deletion src/components/ReceiptAudit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function ReceiptAudit({notes, shouldShowAuditResult}: ReceiptAuditProps) {

let auditText = '';
if (notes.length > 0 && shouldShowAuditResult) {
auditText = translate('iou.receiptIssuesFound', notes.length);
auditText = translate('iou.receiptIssuesFound', {count: notes.length});
} else if (!notes.length && shouldShowAuditResult) {
auditText = translate('common.verified');
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ReportActionItem/ExportWithDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function ExportWithDropdownMenu({
const options = [
{
value: CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION,
text: translate('workspace.common.exportIntegrationSelected', connectionName),
text: translate('workspace.common.exportIntegrationSelected', {connectionName}),
...optionTemplate,
},
{
Expand Down Expand Up @@ -126,7 +126,7 @@ function ExportWithDropdownMenu({
title={translate('workspace.exportAgainModal.title')}
onConfirm={confirmExport}
onCancel={() => setModalStatus(null)}
prompt={translate('workspace.exportAgainModal.description', report?.reportName ?? '', connectionName)}
prompt={translate('workspace.exportAgainModal.description', {connectionName, reportName: report?.reportName ?? ''})}
confirmText={translate('workspace.exportAgainModal.confirmText')}
cancelText={translate('workspace.exportAgainModal.cancelText')}
isVisible={!!modalStatus}
Expand Down
Loading
Loading